رود مپ اتصال 2 رله با برد توسعه ESP32 WROOM-32D​ به اِیر اِنجین

آموزش اتصال به ایر انجین با ESP32 و رله

نمونه پروژه

کنترل 2 عدد رله توسط سامانه اِیر اِنجی

مقدمه

رله دو کاناله به طور گسترده در سیستم‌های خانه هوشمند و اینترنت اشیا برای کنترل دستگاه‌ها و تجهیزات مختلف استفاده می‌شود. این رله‌ها می‌توانند به عنوان یک سوییچ مکانیکی اقدام به قطع و وصل برق کنند. این برق می تواند متناسب با نوع رله متفاوت باشد.

برای مثال، با اتصال رله دو کاناله به یک سیستم تهویه هوا، می‌توان یک دمای مناسب در داخل خانه حفظ کرد. زمانی که دمای محیط از یک آستانه خاص مانند 30 درجه سانتی‌گراد عبور کند، رله می‌تواند به طور خودکار یک تهویه مطبوع را روشن کند تا دمای اتاق کاهش یابد. برعکس، وقتی دما به زیر حد مشخصی مثل 22 درجه سانتی‌گراد برسد، رله دستگاه تهویه مطبوع را خاموش می‌کند. این کار نه تنها باعث حفظ دمای مطلوب در محیط می‌شود، بلکه به صرفه‌جویی در مصرف انرژی و کاهش هزینه‌های برق نیز کمک می‌کند.

با استفاده از پلتفرم AirNgin، می‌توانید یک رله دو کاناله هوشمند را از صفر بسازید و راه‌اندازی کنید. در این پروژه، از یک برد برد توسعه ESP32 WROOM-32D​، برد ارتباطی Wi-Fi ESP32و 2 عدد رله نرمال اوپن استفاده می‌شود. در این مثال ما دو رله را به پلتفرم ابری وصل می نماییم و تمامی مراحل را قدم به قدم با یکدیگر جلو می رویم.

مواد لازم

قدم اول معرفی ابزار

برای شروع ابتدا به پنل تولیدکنندگان بروید و ثبت نام خود را تکمیل کنید. سپس از منوی سمت راست به قسمت ابزارهای ابری بروید.

داشبورد ایر انجین

شما از این قسمت وارد مرحله اول تعریف ابزار خود می شوید. با کلیک بر روی ابزار جدید، صفحه تعریف ابزار برای شما باز می شود.

تعریف اولیه ابزار

صفحه زیر باز می شود.

توجه: تمامی اطلاعات وارده تا قبل از ارسال ابزار جهت تایید قابل تغییر می باشد. پس آموزش را گام به گام با ما ادامه دهید.

تعریف ابزار در ایر انجین

گزینه های فرم بالا به شرح زیر است (با دقت مطالعه نمایید) :

نام:

این نام ابزار است که در نرم‌افزار موبایل نیز هنگام اضافه کردن ابزار توسط کاربر نهایی نمایش داده می‌شود. بنابراین، اگر نیاز است، مدل ابزار را نیز در ادامه همین نام قرار دهید.

دسته بندی:

از این بخش باید دسته‌بندی ابزار خود را انتخاب نمایید. این دسته‌بندی در پیدا کردن ابزار شما توسط کاربر نهایی در نرم‌افزار موبایل و همچنین نمایش بخش‌بندی در نرم‌افزار موبایل اهمیت دارد.

توجه: اگر دسته‌بندی مورد نظر شما موجود نمی‌باشد، از بخش تیکت درخواست اضافه شدن آن دسته‌بندی را اعلام نمایید.

نوع آنالیز:

ما در حال حاضر دو نوع آنالیز داریم که صرفاً تا انتهای سال ۱۴۰۳ توسط “فایل NodeJs” قابل استفاده خواهد بود و بخش لینک هنوز فعال نمی‌باشد.

آنالیز توسط فایل NodeJs:

در این حالت به دو فایل JS نیاز داریم که حتماً در ادامه به توضیح آنها خواهیم پرداخت.

یک فایل Generate:

این فایل مشخص می‌کند که داده‌های دریافتی از نرم‌افزارهای موبایل به چه داده‌هایی تبدیل شده و به سمت ابزار شما ارسال می‌شوند. در واقع، این کار برای جلوگیری از تغییرات گسترده در پروژه‌های شما انجام می‌شود. علاوه بر این، در آینده قابلیت اتصال به خدمات مختلف ابری و استفاده از APIهای شخصی توسط خود شما در این فایل‌ها میسر خواهد شد.

برای بهره مندی از نمونه و تست فایل Generator می توانید فایل زیر را دانلود نمایید.

دانلود فایل نمونه

سپس به بخش آزمایشگاه > تست فایل JS جهت ارسال داده به ابزار بروید و فایل را بارگذاری کنید و متناسب با ابزار هایی که اضافه کرده اید آن را تست و ویرایش کنید.

آزمایشگاه فایل js generate برای تست در ایر انجین

 

یک فایل برای Analyzer:

این فایل مشخص می‌کند که داده‌های دریافتی از سخت‌افزارهای شما دقیقاً چه معنایی برای سرور خواهند داشت. در واقع، این کار برای جلوگیری از تغییرات گسترده در پروژه‌های شما انجام می‌شود. علاوه بر این، در آینده قابلیت اتصال به خدمات مختلف ابری و استفاده از APIهای شخصی توسط خود شما در این فایل‌ها میسر خواهد شد.

برای بهره مندی از نمونه و تست فایل آنالیز می توانید فایل زیر را دانلود نمایید.

دانلود فایل نمونه

سپس به بخش آزمایشگاه > تست فایل JS جهت دریافت داده از ابزار بروید و فایل را بارگذاری کنید و متناسب با ابزار هایی که اضافه کرده اید آن را تست و ویرایش کنید.

آزمایشگاه تست فایل JS برای آنالیز ایر انجین

فایل راهنما:

این فایل از نوع html است و بسیار مهم است زیرا به عنوان راهنمای گام به گام برای کاربر جهت افزودن ابزار به مکان خود نمایش داده می‌شود و شامل تنظیمات اضافی آن ابزار و نکات مهمی است که باید برای استفاده صحیح رعایت شوند. همچنین، این فایل می‌تواند در عیب‌یابی به کاربران کمک کند. هدف از این کار این است که شما نیازی به بروشورهای کاغذی نداشته باشید که معمولاً ممکن است توسط کاربر گم شوند و همچنین این امکان را فراهم می‌آورد که بتوانید با استفاده از ویدیو، به کاربران نهایی آموزش دهید.

این فایل راهنما در دو قسمت قابل مشاهده است:

1- افزودن یک ابزار توسط کاربرنهایی:ابتدا کاربر نهایی در نرم افزار موبایل دسته بندی ابزار را انتخاب می کند. سپس از لیست ابزار های لود شده ابزار شما را انتخاب می کند.بعد از انتخاب ابزار بصورت خودکار این فایل راهنما برای کاربر به نمایش در می آید تا اطلاع از اقدامات اولیه برای افزودن ابزار و همچنین نحوه استفاده از آن ابزار داشته باشد.

2 – پس از افزودن یک ابزار در قسمت اطلاعات هر ابزار در نرم افزار موبایل کماکان این فایل راهنما قابل مشاهده است.

از آنجایی که ممکن است شما برای ساخت این فایل نیاز به کمک داشته باشید یک ویرایشگر html برای شما در پنل آزمایشگاه > ویرایشگر html طراحی شده است.

می توانید همین نمونه را متناسب با نیاز خود تغییر دهید و سپس از طریق دکمه دانلود فایل html آن را دانلود نمایید.

ویرایشگر html ابر انجین

 

فایل تنظیمات (اجباری نمی باشد):

این فایل نیز از نوع html است. یکی از مزایای این فایل، افزودن تنظیمات اضافی به ابزار شما است. تنظیماتی مانند تغییر رنگ چراغ، تغییر حالت‌های یک رله یا یک دکمه و هر آنچه که برای شما مورد نیاز است.

برای این بخش هنوز ویرایشگری تعبیه نشده است، اما می‌توانید از بخش مستندات آموزشی، APIهای آن را مشاهده کنید.

آموزش تنظیمات اختصاصی برای ابزار

امکان ارسال دستور از طریق پیامک (در این آموزش تیک آن را نزنید):

این گزینه صرفاً برای ابزارهایی است که دارای GSM درونی هستند، مانند SIM800. توضیحات این بخش به آموزش‌های دیگری که همراه با GSM است سپرده می‌شود. البته، اگر نیاز به توضیحات بیشتر دارید، می‌توانید به مستندات درگاه پیامکی مراجعه کنید.

عکس:

عکس محصول دقیقا همان عکسی می باشد که هنگام اضافه کردن ابزار به کاربر جهت انتخاب محصول مورد نظر خود نمایش می دهیم. پس لازم است یک عکس واقعی از محصول خود را آپلود نمایید.

در انتها این ابزار را ذخیره نمایید. نگران نباشید در آینده درصورت نیاز می شود تمامی این اطلاعات تا قبل از ارسال نهایی یک ابزار به بخش پشتیبانی، ویرایش کرد.

قدم دوم تنظیمات پنل اِیر اِنجین بعد از افزودن ابزار

بعد از افزودن یک ابزار به پنل کاربری خود، زمانی که به ابزارهای ابری از منوی سمت راست رجوع نمایید می توانید ابزاری که به پنل خود اضافه کرده اید را مشاهده کنید.

اضافه شدن نهایی ابزار به ایر انجین

حال بر روی اسم ابزار خود کلیک نمایید تا وارد بخش تنظیمات آن ابزار شوید.

پنل تنظیمات ابزار

با صفحه بالا روبرو می شوید. در منوی عمومی می توانید اطلاعات اولیه ابزار را ویرایش نمایید.

در قدم اول نیاز است به قسمت دستورات بروید.

پنل دستورات ابزار

این قدم بسیار مهم می باشد. بر روی دستور جدید کلیک نمایید.

افزودن دستور جدید

کانال:

منظور از کانال در واقع قابلیت های ابزار شما است. منظور قابلیت ها مثل رله 1 کاناله دارای 1 کانال است. رله 2 کاناله دارای 2 کانال است و یا یک رله 2 کاناله همراه با یک عدد سنسور دما دارای 3 کانال است، 2 کانال رله و یک کانال سنسور.

بخش کانال دارای پارامترهای زیر است: 

ظاهر نرم افزار: با این بخش مشخص می نمایید این کانال که در حال تعریف آن هستید، چه نوع ابزاری است و باید چه نوع UI در نرم افزارهای موبایل به نمایش بگذارد. پس در انتخاب این بخش دقت نمایید. چون ما در این قسمت رله داریم می توانیم هم کلید روشنایی انتخاب نماییم و هم رله پریز (تفاوت در آیکن می باشد، در آینده اجازه میدهیم آیکن ها توسط کاربران انتخاب شوند).

کانال، درواقع قابلیت های یک ابزار است.

نام (operationName):

نام کانال چیست؟
نام کانال شما همان operationName است که در پلتفرم اینترنت اشیا استفاده می‌شود.

operationName در واقع نامی است که شما برای هر کانال مشخص می‌کنید. به این معنا که شما باید تصمیم بگیرید که هر کانال، مانند یک رله، چه معنایی برای شما دارد. به عنوان مثال، در کد نمونه ما، برای رله‌ها از نام‌های ch1 و ch2 استفاده کرده‌ایم تا کانال‌های 1 و 2 را شناسایی کنیم.

با این حال، این انتخاب کاملاً به شما بستگی دارد. ممکن است شما به رله اول نام relay1 و به رله دوم نام relay2 بدهید. بنابراین، operationName همان نام دلخواهی است که شما برای هر کانال انتخاب می‌کنید و این کاملاً اختیاری است.

این flexibility به شما این امکان را می‌دهد که نام‌ها را به روشی متناسب با نیازها و ساختار پروژه خود انتخاب کرده و از آن‌ها به راحتی استفاده کنید.

نوع دستور:

اگر دستور شما مربوط به یک دستگاه فعال‌سازی مانند رله باشد، به آن عملگر می‌گوییم، اما اگر دستور شما مربوط به یک دستگاه اندازه‌گیری مانند سنسور باشد، به آن سنسور می‌گوییم.

به عبارت دیگر، در پلتفرم اینترنت اشیا، هر دستگاه را بر اساس نوع عملکرد آن دسته‌بندی می‌کنیم. برای مثال:

  • عملگر: برای دستگاه‌هایی که به طور مستقیم اقدام به تغییر وضعیت می‌کنند، مانند رله‌ها (که می‌توانند یک مدار را باز یا بسته کنند).
  • سنسور: برای دستگاه‌هایی که داده‌ها یا اندازه‌گیری‌هایی از محیط جمع‌آوری می‌کنند، مانند سنسورها (که می‌توانند دما، رطوبت، حرکت و غیره را اندازه‌گیری کنند).

این تفکیک کمک می‌کند تا در سیستم، تشخیص داده‌ها و دستورات به درستی صورت گیرد و عملیات مورد نظر با دقت و صحت انجام شود.

تکمیل شده فرم بصورت بالا می باشد. حال روی ثبت و مرحله بعد کلیک می نماییم. 

در مرحله بعد دستورات آن ابزار متناسب با ظاهر انتخاب شده بصورت خودکار برای ما به نمایش در می آید.

همانطور که در بالا مشاهده می نمایید برای یک رله دستور روشن و خاموش معنی دارد. گزینه های این بخش به شرح زیر است:

نام: نام هر دستور است، این نام در بخش های مختلف مانند سناریو نویسی به نمایش در می آید. پس نیاز است نامی انتخاب نمایید که کاملا معنی آن دستور را می دهد.

مقدار سیستمی: 

این مقدار دقیقا همان مقداری می باشد که نرم افزارهای موبایل به سمت سرور ارسال می کند و شما توسط فایل generate باید آن را بررسی و دستور منحصر خود را به سمت ابزار ارسال کنید.دقت نمایید در تمامی دستورات ما به این مقدار سیتمی value می گوییم که در ادامه بررسی فایل های analyze و generate که برای سرور مورد نیاز است توضیح خواهیم داد. 

دستور:

در آینده این قسمت به کار می آید.

روی ثبت کلیک نمایید.

**تبریک می گویم شما اولین کانال خود را ثبت کرده اید.**

همین کار را برای کانال دوم رله نیز انجام می دهیم.

نکته: تا قبل از استفاده یک ابزار در یک پروژه می توانید با کلیک بر روی نام دستور مقادیر را تغییر دهید.

لیست دستورات

بسیار عالی.

سریال ها:

توسط این قسمت می توانید سریال ابزارها را اضافه نمایید. 

سریال ابزار برای چیست: 

برای اینکه هر ابزار بتواند به سرور وصل شود نیاز است یک سریال داشته باشد. جهت توضیحات کامل تر در این رابطه به بخش مستندات فنی رجوع نمایید.

نکته: سریال دارای قواعد خود می باشد حتما به مستندات فنی رجوع نمایید.

کد تولید کننده را که بصورت خودکار به شما ارایه می شود می توانید از بخش عمومی در پنل کاربری خود با کلیک بر روی آیکن یوزر در سمت چپ بالا > گزینه تولید کننده مشاهده نمایید.

مزیت های سریال:

ابزار شما یونیک می شود و تنها ابزارهایی که شما تولید کرده اید و به لیست سریال ها اضافه کرده اید قابلیت اتصال به سرور ابری اِیر اِنجین را دارند و همچنین قابل رهگیری نیز می باشند (رهگیری صرفا توسط بخش پشتیبانی سامانه آن هم با دستور مقام قضایی ممکن می باشد و به هیچ عنوان به دلیل حفظ حریم شخصی توسط تولید کننده قابل انجام نمی باشد).

تا این قسمت شما به درستی توانسته اید در پنل کاربری خود ابزار را اضافه نمایید و صرفا تغییرات فایل های analyzer و generate مانده که در ادامه که کدها را توضیح می دهیم این دو فایل را کامل می نماییم.

قدم سوم کدنویسی و تغییرات فایل های JS

ما برای کد نویسی از vscode استفاده می نماییم.

آموز نصب vscode را می توانید از بخش مستندات مشاهده نمایید.

ساخت یک پروژه جدید در vscode

کدهای اصلی نرم افزار که در گیتهاب می باشد را می توانید از اینجا دانلود نمایید.

برای توضیحات اولیه به این فایل مراجعه نمایید.

مطالعه 4 لینک بالا بسیار مهم می باشد. حال که 4 لینک بالا را مطالعه کرده اید به قسمت توضیحات می رویم.

اگر در قسمتی از کدها متوجه چرایی آن نشدید هم می توانید از چت جی پی تی کمک بگیرید و یا از طریق بخش پشتیبانی (تیکت) کمک دریافت نمایید.

ابتدا به سراغ فایل اصلی نرم افزار در پوشه products>relay2_curtain می رویم.

خط 1 : توسط این خط تمامی ایمپورت های لازم را انجام می دهیم. این فایل در پوشه src می باشد.

خط 3 : توسط این خط تنظیمات اولیه را انجام می دهیم. متد اصلی این خط در فایل src>common.h می باشد.

خط 5 : حلقه اصلی نرم افزار می باشد. متد اصلی این خط در فایل src>common.h می باشد.

نکته: تمامی متغییر ها (VARIBLE) ها در فایل definitions.h در پوشه src می باشد.


#include "import_all.h"

void setup() { Public_Main(); }

void loop() { Public_Loop(); }

متد public_main به شرح زیر است:


void Public_Main(){
   //...................................... 3 Sec Delay to Stable Power
  delay(500);
#if HARDWARE_GSM
  GSM__PowerOff();
#endif
  //-------------------------------------------------------- Preparing Serial & DebugPrint
#if SOFTWARE_DEBUGMODE
    Debug__ConsoleBegin();
    DEBUG_SERIAL_PRINTLN("\r\n=========================\r\n======= Restarted =======\r\n=========================");
#endif
  //-------------------------------------------------------- Preparing Device
  Dvc__SetPinout(true);
  delay(500);
  Tools__LED_WarningBlink(10, WARNINGLED_REBOOT, WARNINGLED_OFF, 100);

  //-------------------------------------------------------- Preparing EEPROM & File
  EEPROM_Start();
  MemoCheck();
  SPIFFS_Start();

  //-------------------------------------------------------- Create Token For This Session
  if (_Token == "") _Token = (String(Tools__GetChipID()) + "0000000000").substring(0, 10) + String(Tools__Random(100000, 999998));  // 16 Digit Token

  //-------------------------------------------------------- Loading Data
  Tools__LED_Warning(WARNINGLED_DATA);

  Tools__AllDefaults();
  Tools__SettingRead();
  Tools__SerialBarcodeReload();
  Tools__SettingShowInfo();


  Tools__RunsenLoad();
  Tools__ScenarioLoad();
  if (_IOTCloudUse) Tools__CloudJSON_Reload();
  Dvc__LastStatusLoad();

  //-------------------------------------------------------- Preparing
  if (_Circuit == "iot") {
    _Need_IOT = true;
  } else if (_Circuit == "gateway") {
    _Need_IOT = true;
  }
  //........................
  if (_StartMode == "config_panel") {
    _Started_ConfigPanel = true;
    _Need_Taskloop = true;
  } else if (_StartMode == "reboot_sendall") {
    //if (_ModemSSID == "" ) _Need_ConfigIsBad = true;
    _Started_RebootSendall = true;
  } else if (_StartMode == "config_sendall") {
    //if (_ModemSSID == "" ) _Need_ConfigIsBad = true;
    _Started_ConfigSendall = true;
  } else if (_StartMode == "config_ranking") {
    //if (_ModemSSID == "" ) _Need_ConfigIsBad = true;
    _Started_ConfigRanking = true;
  } else {  // Started_Normal
    if (!_Started_WiredOnly) {
      if (_Need_IOT) {
        if (_ProjectCode == "" || _ModemSSID == "" || _MqttBroker == "") _Need_ConfigIsBad = true;
        else _Started_IotRun = true;
      } else if (_Need_Mesh) {
        if (_ProjectCode == "") _Need_ConfigIsBad = true;
        else _Started_MeshRun = true;
      }
    }
    if (!_Started_MeshRun) _Need_Taskloop = true;
  }
  
 
  //...................................... Warning LED
  // if(_Need_ConfigIsBad) Tools__LED_Warning(WARNINGLED_CONFIG_NEED, 2000);
  Tools__LED_Warning(WARNINGLED_DATA);

  //...................................... Set WiFi-Power
  Tools__WifiPower(_Started_WiredOnly ? false : true);

  //.............................................. To Do : Check Banned Any Where
  if (_Banned == 1)
  {
    while (true)
    {
      Tools__LED_WarningBlink(10, WARNINGLED_REBOOT, WARNINGLED_OFF);
      delay(1000);
    }
  }



  //...................................... Setup LED & TouchKey (To Use Manualy)
  Manual__Setup();

  //----------------------------------------------------------------- Running By Mode (On Main Thread)
  //...................................... Setup On Mode-Config
  if (_Started_ConfigPanel)
  {
    Config__Setup(NULL);
  }

  //............................................. Setup On Mode IOT / Mqtt
  else if (_Started_IotRun)
  {
    IOT__Setup(NULL);
  }
  //............................................. Setup On Mode Wifi-Mesh
  else if (_Started_MeshRun)
  {
    // Mesh__Setup(NULL);
  }
  //.............................................
  Tools__LED_Warning(WARNINGLED_AUTO);
}

خط 4 : در این مکان چک می شود آیا سخت افزار ما دارای امکان GSM است یا خیر. (در این پروژه ما GSM نداریم پس در فایل definitions.h مقدار این متغییر را false می نماییم یا آن را کامنت می نماییم زیرا وقتی یک متغییر که در کامپایلر تعریف می شود اگر وجود نداشته باشد مقدار دیفالت آن false است).

خط 8 : مشخص می نماید اگر شما نیاز به پرینت حالت های دیباگ داشته باشید اقدام به پرینت می نماید. این پرینتها متناسب با نیاز ما برای دیباگ راحتتر نوشته شده است که می تواند توسط شما کم و یا زیاد شود.

خط 14 : این متد جهت تعریف پین های مورد نیاز و تعیین حالت out یا in پین ها می باشد.

توضیحات متد Dvc__SetPinout :

void Dvc__SetPinout(bool refresh) {
  DEBUG_SERIAL_PRINTLN("Dvc__SetPinout");
  pinMode(Key, INPUT); 
  for (byte i = 0; i < MAX_RELAY; i++) {
    pinMode(Relay[i], OUTPUT);
    if (refresh) digitalWrite(Relay[i], LOW);
  }
  if (refresh) {
    pixels.begin();
  }
}

خط 3 : دکمه اصلی ما می باشد که بصورت پوش باتن است. به واسطه این دکمه ابزار را به حالت ap می باریم تا نرم افزار موبایل بتواند به آن وصل شود و آن را به مکان کاربر اضافه کند.

خط 4 : توسط این حلقه پایه های مورد نظر رله را به خروجی تبدیل می نماییم. MAX_RELAY یک متغییر می باشد که متناسب به کد نویسی ما تعبیه شده است و اگر شما تعداد رله هایتان بیش یا کمتر از 2 است می توانید با تغییر این متغییر در فایل dvcSetting.h این مورد را مدیریت نمایید. همچنین متغییر Relay یک آرایه است که پایه های رله را در آن تعریف کرده ایم. این متغییر در رله فایل dvc.h است.

خط 9 : این خط مربوط به کنترل led از نوع WS2812 می باشد.

به توضیحات متد Public_Main بر می گردیم:

خط 16 : این متد برای مدیریت ال ای دی می باشد که ما برای راحتی برای خودمان بصورت متغییر تعریف کرده ایم متغییرهای ال ای دی در فایل definitions.h تعریف شده است.

خط 19 : بررسی و مقداردهی اولیه EEPROM

خط 20 : تابع MemoCheck برای بررسی و پاک‌سازی داده‌های ذخیره‌شده در حافظه‌ی EEPROM یا مشابه آن طراحی شده است. این تابع وضعیت دو نقطه از حافظه (احتمالاً به‌عنوان بخش‌هایی از یک پارتیشن حافظه) را ارزیابی کرده و در صورت مغایرت، آن‌ها را پاک‌سازی و دوباره مقداردهی می‌کند.

خط 21 : این تابع به نام SPIFFS_Start برای راه‌اندازی و بررسی فایل‌سیستم SPIFFS در دستگاه‌های مبتنی بر ESP (مانند ESP8266 یا ESP32) استفاده می‌شود. SPIFFS یک سیستم فایل کوچک است که برای ذخیره‌سازی داده‌های فایل‌محور در فلش مموری استفاده می‌شود.

خط 24 : بواسط آی دی چیپ و درست کردن راندوم اعداد سریال مورد نظر ابزار را می سازیم که در ادامه از آن استفاده می نماییم.

خط 30 : مقدار دهی مقادیر اولیه درصورت عدم داشتن مقدار

خط 31 : از آنجایی که EEPROMها بعد از تعداد مشخصی خواندن و نوشتن دچار مشکل می‌شوند، لازم است برای افزایش طول عمر هر میکروکنترلر، در ابتدای برنامه مقادیر موردنیاز را از EEPROM خوانده و در متغیرهای محلی و سراسری ذخیره کنید.

خط 32 : در این خط شروع به ساخت سریال ابزار می نماید.

توضیحات مربوط به متد ایجاد سریال :
void Tools__SerialBarcodeReload()
{
  _SerialNo = Tools__Memory_StrGet("_SerialNo");
  if (_SerialNo.length() != 10)
  {
    _SerialNo = _SerialNo + "0000000000";
    _SerialNo = _SerialNo.substring(0, 10);
  }
    String chip = (String(Tools__GetChipID()) + "0000000").substring(0, 7);
    if (chip == "0000000")
      chip = (String(Tools__Random(1000000, 9999998)) + "0000000").substring(0, 7);
    _SerialNo = ("000" + chip).substring(0, 10);
    Tools__LED_WarningBlink(10, WARNINGLED_CONFIG_NEED, WARNINGLED_CONFIG_NEED, 100);
  
  //............................................. Rebuild Some Variables
  _SerialHex = Tools__StringToHexStr(_SerialNo);
  _SerialCloud = "AIRN" + _SerialNo;  // AIRN : گد شما می باشد که توسط سامانه به شما اختصاص داده شده است
  CA_CopyStr(_MySenderId, "dvc_" + _SerialNo);
  DEBUG_SERIAL_PRINTLN("_SerialNo : " + _SerialCloud);
}

این تابع به نام Tools__SerialBarcodeReload برای مقداردهی و بازسازی اطلاعات مربوط به شماره سریال (_SerialNo) استفاده می‌شود. در ادامه خط به خط این تابع توضیح داده می‌شود:

1. خواندن مقدار شماره سریال از حافظه:

_SerialNo = Tools__Memory_StrGet("_SerialNo");

2. بررسی طول شماره سریال:

if (_SerialNo.length() != 10)

بررسی می‌شود که آیا طول رشته‌ی _SerialNo برابر با 10 کاراکتر است یا خیر.
اگر طول آن متفاوت باشد:
ابتدا 10 صفر به انتهای _SerialNo اضافه می‌شود.

سپس با استفاده از متد substring، اولین 10 کاراکتر از رشته گرفته می‌شود:

_SerialNo = _SerialNo.substring(0, 10);

3. ایجاد مقدار اولیه برای شماره سریال در صورت نیاز:

String chip = (String(Tools__GetChipID()) + "0000000").substring(0, 7);

مقدار chip با استفاده از تابع Tools__GetChipID تولید می‌شود که شناسه‌ی منحصر به فرد میکروکنترلر را برمی‌گرداند.

اگر مقدار Tools__GetChipID برابر با “0000000” باشد:

chip = (String(Tools__Random(1000000, 9999998)) + "0000000").substring(0, 7);

مقدار تصادفی با استفاده از تابع Tools__Random بین 1000000 و 9999998 تولید می‌شود.
این مقدار تصادفی به همراه چند صفر، به طول 7 کاراکتر برش داده می‌شود.

4. ساخت شماره سریال:

سه صفر به ابتدای chip اضافه می‌شود.
اولین 10 کاراکتر نتیجه‌ی ترکیب به عنوان مقدار جدید _SerialNo تنظیم می‌شود.

5. هشدار با LED:

 

Tools__LED_WarningBlink(10, WARNINGLED_CONFIG_NEED, WARNINGLED_CONFIG_NEED, 100);

6. بازسازی متغیرهای وابسته:

_SerialHex = Tools__StringToHexStr(_SerialNo);

مقدار _SerialNo به رشته‌ی هگزادسیمال تبدیل شده و در _SerialHex ذخیره می‌شود. و در نهایت سریال مورد نیاز سرور بصورت زیر تهیه می شود.

_SerialCloud = "AIRN" + _SerialNo;

AIRN: کد مربوط به هر تولید کننده هست که عملا شناسه یکتا می باشد که در هنگام ثبتنام توسط سرور بصورت خودکار ساخته می شود.

کد تولید کننده را که بصورت خودکار به شما ارایه می شود می توانید از بخش عمومی در پنل کاربری خود با کلیک بر روی آیکن یوزر در سمت چپ بالا > گزینه تولید کننده مشاهده نمایید.

مقدار _SerialCloud ساخته می‌شود که شامل پیشوند “AIRN” به همراه مقدار _SerialNo است. 

توجه : هر روشی دیگر که یم خواهید سریال برای خود تولید نمایید کاملا امکان پذیر است و صرفا تعداد کاراکتر و قرار گیری کد مخصوص شما در ابتدای سریال مهم می باشد. برای اطلاعات بیشتر می توانید به مستندات فنی رجوع نمایید.

دوباره به توضیحات main_loop باز می گردیم : 

خط 33 : اگر متغییر SOFTWARE_DEBUGMODE را در فایل dvcSetting.h به مقدار true تغییر دهید، تمامی اطلاعات و تنظیمات شما را در پورت سریال پرینت می کند. این متد در ابتدای کار بسیار برای ایراد یابی شما مفید می باشد.

خط 36 و 37 : مربوط به سناریو و اجرای آن می باشد که در یک آموزش جدا کاملا آن را توضیح می دهیم. اما اگر نیاز دارید اطلاعات شما در این رابطه بیشتر شود می توانید به بخش مستندات فنی رجوع کنید.

خط 42 : در اینجا ما نوع اتصال را مشخص می نماییم. در آینده نزدیک امکان اتصال به gateway نیز داده می شود پس پیش بینی آن را کرده ایم.

زمانی که دکمه تنظیمات را بگیرید (در بخش Dvc__SetPinout قبلا پین 23 را input تعریف کرده ایم و آن را pull up کرده ایم تا با اتصال این پایه به GND بتوانیم وارد پنل تنظیمات شویم) از طریق فانکشن Manual_SwichToConfig در فایل dev.h وارد پنل تنظیمات می شویم. 

در فانکشن Manula_loop در خط 93 این عملیات مدیریت می شود.

خط 95 : Manual__Setup یکی از فانکشن های اصلی می باشد که در ادامه آن را توضیح خواهیم داد.

خط 102 : اگر کاربر دکمه تنظیمات که همان پین 93 می باشد را فشار داده و تا 6 ثانیه نگه داشته نیاز است بعد از ریبوت ابزار به قسمت کانفیگ برود. با ذخیره سازی مقدار TRUE برای متغییر _Started_ConfigPanel پیش از ریبوت، هنگام شروع به کار دوباره ابزار متوجه می شویم که نیاز است به حالت تنظیمات و AP ابزار برویم.

خط 108 : در این خط به متد اصلی تنظیمات و اتصال به سرور می رویم که در ادامه آن را توضیح می دهیم.

فانکشن IOT__Setup :


void IOT__Setup(void *param)
{
  Tools__LED_Warning(WARNINGLED_DIS_IOT);
  DEBUG_SERIAL_PRINTLN(".... IOT Mqtt Setup ....");
  //....................................... Reject if Wifi-SSID empty
  // if (_Need_ConfigIsBad) {
  // 	Tools__LED_Warning(WARNINGLED_CONFIG_NEED, 1000);
  // 	// Tools__SetMode("normal", true);
  // 	return;
  // }
  //........................................... WiFi Settings
  _WifiClient.setTimeout(15);
  _WiFi_ConnectWorking = true;
  if (WiFi.isConnected())
  {
    WiFi.disconnect(true);
    delay(100);
  }
  _WiFi_IsConnected = false;
  WiFi.onEvent(IOT__WiFiEvent);
  //........................................... Local-Broker or Cloud-Broker
  _IOTCloudUse = true;
  if (_MqttBroker.length() > 8)
  {
    if (_MqttBroker.substring(0, 8) == "192.168.")
      _IOTCloudUse = false;
  }
  else
    _IOTCloudUse = false;
  //............................................ Mqtt Settings
  if (_IOTCloudUse)
    _MqttClientId = _CloudClientId;
  else
    _MqttClientId = (_ProjectCode + "d" + _SerialNo + "0000" + String(Tools__Random(100000, 999999)) + "xxxxxxxxxxxxxxxxxxxxxxx").substring(0, 23);
  _MqttCon_IsConnected = false;
  _MqttCon_Steps = 0;
  //........................................... IOT Checker
  xTaskCreatePinnedToCore(
      IOT__Checker,         // Function that should be called
      "IOT__Checker",       // Name of the task (for debugging)
      TASK_STACKSIZE_LARGE, // Stack size (bytes)
      NULL,                 // Parameter to pass
      TASK_PRIORITY_HIGH,   // Task priority (Max:1)
      NULL,                 // Task handle
      TASK_CORE_1);         // Core (0/1)

  DEBUG_SERIAL_PRINTLN(".... IOT Setup OK ...");
  //........................................... Stay In Loop
  Tools__LED_Warning(WARNINGLED_AUTO);
  IOT__Loop();
}

پس از ورود به این فانکشن تنظیمات اولیه جهت اتصال به سرور اِیر اِنجین انجام می شود و در نهایت با ایجاد یک Task دیگر به نام IOT__Checker علاوه بر اتصال به سرور، در طول روشن بودن ابزار اقدام به نگهبانی و نگهداری از اتصال دایم با سرور می کند.

خط 34 : _MqttClientId در واقع همان کلاینت آی دی در پروتکل ارتباطی MQTT می باشد. نحوه ساخت آن مشخص می باشد اما شما می توانید برای دریافت اطلاعات بیشتر به این بخش از مستندات فنی رجوع نمایید.

دقت کنید زمانی که می خواهیم اطلاعات اولیه از نرم افزار موبایل را بر روی ابزار شما ذخیره نماییم، یکی از گزینه هایی که ما بر روی ابزار شما ذخیره می نماییم همین کلاینت آی دی می باشد. پس نیاز نمی باشد آن را شما بسازید.

فانکشن Manual__Setup :


void Manual__Setup() {
  DEBUG_SERIAL_PRINTLN(".... Start Manual Setup ....");
  Dvc__SetPinout(true);
   Dvc__RelayControl();
  Tools__LED_Warning(WARNINGLED_DATA);
  //........................................... Stay In Loop
  xTaskCreatePinnedToCore(
    Manual__Loop,          // Function that should be called
    "Manual__Loop",        // Name of the task (for debugging)
    TASK_STACKSIZE_LARGE,  // Stack size (bytes)
    NULL,                  // Parameter to pass
    TASK_PRIORITY_HIGH,    // Task priority (Max:1)
    NULL,                  // Task handle
    TASK_CORE_1);          // Core (0/1)

  //........................................... Check Scenario "Start"
  String sName = "";
  for (byte i = 0; i < MAX_SCENARIO; i++) {
    if (_Scenario_List[i].Act == 0x01) {
      sName = CA_ToString(_Scenario_List[i].Name);
      sName.toLowerCase();
      std::replace(sName.begin(), sName.end(), '1', ' ');
      std::replace(sName.begin(), sName.end(), '2', ' ');
      std::replace(sName.begin(), sName.end(), '3', ' ');
      sName.trim();
      if (sName == "start") Dvc__Senario_DoAction(i, 0x02);
    }
  }
  DEBUG_SERIAL_PRINTLN(".... End Manual Setup ....");
  Tools__LED_Warning(WARNINGLED_AUTO);
}

خط 3 : احتمالا از خودتان می پرسید چرا ما دوباره Dvc__SetPinout(true);  را فراخوانی کرده ایم؟ 

دلیل اینکار این می باشد که پس از خواندن حالت های قبلی رله قبل از خاموش شدن ابزار (به هر دلیل) اگر کاربر نهایی تنظیمات را روی روشن شدن روی آخرین وضعیت تنظیم کند، رله ها را به آخرین وضعیت تغییر دهد.

خط 7 : توسط این خط ما از قابلیت بسیار عالی ایجاد Task استفاده می نماییم. توسط این قابلیت ما می توانیم چندین حلقه همزمان با هم را در حال اجرا داشته باشیم. حلقه Manual__Loop حلقه اصلی برنامه ما می باشد و جایگزین loop اصلی پیشفرض کرده ایم.

 

متد IOT__Checker :


void IOT__Checker(void *param)
{
  while (true)
  {
    delay(1000);
    try
    {
      //.............................................................. Prevent Any Action During Network_Reset()
      while (_MqttCon_Steps == 99)
        delay(1000);
      //..............................................................
      if (_MqttCon_Steps == 0)
      {
        if (_IOT_ModemTimeout == TIMER_JOB_DONE) _IOT_ModemTimeout = MIN_3;
        IOT__WiFiStart();
#if HARDWARE_GSM
        if (_Dvc_GSM.Type != T_GSM)
          DEBUG_SERIAL_PRINTLN("############ _MqttCon_Steps 0 :: WiFiStart()");
#elif SOFTWARE_DEBUGMODE
        DEBUG_SERIAL_PRINTLN("############ _MqttCon_Steps 0 :: WiFiStart()");
#endif
        if (_MqttCon_Steps == 0)
          _MqttCon_Steps = 1;
      }
      else if (_MqttCon_Steps == 2)
      {
        const char *mqtt_broker = _MqttBroker.c_str();
        const int mqtt_port = 1883; // 5000
        _MqttObj.setCallback(Mqtt__OnRecieve);
        _MqttObj.setServer(mqtt_broker, mqtt_port);
        _MqttObj.setBufferSize(11000);
        _MqttObj.setSocketTimeout(15);
        _MqttObj.setKeepAlive(15);
        _MqttCon_Steps = 3;
#if HARDWARE_GSM
        if (_Dvc_GSM.Type != T_GSM)
          DEBUG_SERIAL_PRINTLN("############ _MqttCon_Steps 1 :: MqttObj.setServer()");
#elif SOFTWARE_DEBUGMODE
        DEBUG_SERIAL_PRINTLN("############ _MqttCon_Steps 1 :: MqttObj.setServer()");
#endif
        //......................................
      }
      else if (_MqttCon_Steps == 3)
      {
        delay(1000);
        Mqtt__Connect();
      }
      else
      {
        DEBUG_SERIAL_PRINTLN("############ _MqttCon_Steps " + String(_MqttCon_Steps) + " :: No Action");
      }
    }
    catch (...)
    {
    }
  }
}

این حلقه که بصورت بی انتها می باشد (WHILE) حلقه اصلی اتصال و نگهداری اتصال به سرور می باشد.

برای اطلاع بیشتر از نحوه لاگین و تاپیک های اصلی در MQTT می توانید به بخش مستندات رجوع نمایید.

متد Manual__Loop :

void Manual__Loop(void* param) {
  delay(3000);
  Dvc__SetPinout(false);
  while (true) {
    try {
      TimerSec_Refresh();
      byte Time = 0;
      if (digitalRead(Key) == LOW) {
        while (digitalRead(Key) == LOW && Time < 120) {
          if (Time == 60) {
            Manual_SwichToConfig();
          }
          Time++;
          delay(50);
        }
        {
          DEBUG_SERIAL_PRINTLN("Time= " + String(Time));
          DEBUG_SERIAL_PRINTLN("DvcRelay[0] = " + String(DvcRelay[0]));
          DEBUG_SERIAL_PRINTLN("DvcRelay[1] = " + String(DvcRelay[1]));
        }
        if (Time < 30) {
          byte TC = _Dvc_Relay[0].Type;
          switch (TC) {
            case T_LIGHT:
              if (DvcRelay[0] == 0 || DvcRelay[1] == 0) {
                DvcRelay[0] = 1;
                DvcRelay[1] = 1;
              } else {
                DvcRelay[0] = 0;
                DvcRelay[1] = 0;
              }
              digitalWrite(Relay[0], DvcRelay[0]);
              digitalWrite(Relay[1], DvcRelay[1]);
      if(DvcRelay[0]==1)
      {
        Mqtt__Send("DeviceToServer", "cloud", "ch1on" , "", true);
      }
      else if(DvcRelay[0]==0)
      {
        Mqtt__Send("DeviceToServer", "cloud", "ch1off" , "", true);
      } 
      if(DvcRelay[1]==1)
      {
        Mqtt__Send("DeviceToServer", "cloud", "ch2on" , "", true);
      }
      else if(DvcRelay[1]==1)
      {
        Mqtt__Send("DeviceToServer", "cloud", "ch2off" , "", true);
      }

        
              break;
  
          }
        }
      }

    } catch (...) {
      DEBUG_SERIAL_PRINTLN("Manual__Loop Error ");
    }
    //-------------------------------------------- TIMER
    TimerSec_Refresh();
    //-------------------------------------------- Loop-Delay
    delay(TASK_LOOP_DELAY);
  }
}

خط 6 : متد TimerSec_Refresh برای مدیریت و به‌روزرسانی تایمرهای مختلف در برنامه طراحی شده است. این تایمرها وظیفه‌ی هماهنگی و زمان‌بندی عملیات مختلف، شامل بررسی سناریوها، مدیریت ارتباطات IoT، و کنترل وضعیت سخت‌افزار (مانند GSM) را بر عهده دارند. به طور کلی، این متد برای اجرای عملیات دوره‌ای (مانند چک کردن تایم‌اوت‌ها) استفاده می‌شود.

خط 8 تا 15 : اگر دکنه تنظیمات یا همان پین شماره 93 به مدت 6 ثانیه گرفته شده توسط کاربر، ابزار به حالت Ap می رود تا قابلیت افزودن آن را به پروژه داشته باشیم و یک پنل کافینگ نیز ما برای آن طراحی کرده ایم تا با اتصال به ابزار بتوانید با زدن آی پی 192.168.1.1 وارد پنل داخلی ابزار شوید. (جهت تنظیمات دستی و غیره)

خط 34 تا 49 : جهت ارسال وضعیت اولیه به محض اتصال به سرور می باشد تا وضعیت ابزار در سرور بروز رسانی شود.

متد public_loop :

این متد حلقه اصلی اجرایی می باشد که مروری بر آن خواهیم داشت.
void Public_Loop(){
  while (true)
  {
  
  #if HARDWARE_GSM
  try {
    if (_Dvc_GSM.Type == T_GSM) GSM__SmsRead();
  } catch (...) {}
  #endif
  }
  delay(100);
  }
}

همانطور که مشاهده می نمایید توسط یک حلقه بی انتهای while میکروکنترلر را در این حلقه نگه می داریم.

خط 7 : همانطور که گفتیم به دلیل آنکه این ابزار دارای GSM نمی باشد از این بخش صرف نظر می نماییم.

 

متد public_loop :

_SerialCloud = "AIRN" + _SerialNo;

void Manual__Setup() {
  DEBUG_SERIAL_PRINTLN(".... Start Manual Setup ....");
  Dvc__SetPinout(true);
   Dvc__RelayControl();
  Tools__LED_Warning(WARNINGLED_DATA);
  //........................................... Stay In Loop
  xTaskCreatePinnedToCore(
    Manual__Loop,          // Function that should be called
    "Manual__Loop",        // Name of the task (for debugging)
    TASK_STACKSIZE_LARGE,  // Stack size (bytes)
    NULL,                  // Parameter to pass
    TASK_PRIORITY_HIGH,    // Task priority (Max:1)
    NULL,                  // Task handle
    TASK_CORE_1);          // Core (0/1)

  //........................................... Check Scenario "Start"
  String sName = "";
  for (byte i = 0; i < MAX_SCENARIO; i++) {
    if (_Scenario_List[i].Act == 0x01) {
      sName = CA_ToString(_Scenario_List[i].Name);
      sName.toLowerCase();
      std::replace(sName.begin(), sName.end(), '1', ' ');
      std::replace(sName.begin(), sName.end(), '2', ' ');
      std::replace(sName.begin(), sName.end(), '3', ' ');
      sName.trim();
      if (sName == "start") Dvc__Senario_DoAction(i, 0x02);
    }
  }
  DEBUG_SERIAL_PRINTLN(".... End Manual Setup ....");
  Tools__LED_Warning(WARNINGLED_AUTO);
}

هماهنگ سازی فایل های js سرور با ابزار:

همان طور که در شروع این آموزش هنگام ثبت ابزار خودمان مشاهده کرده ایم، ما 2 عدد رله با نام (operationName) ch1 , ch2 اضافه کرده ایم که هرکدام از آن دارای 2 دستور خاموش و روشن (on,off) بوده اند.

اگر در مکان هایی که ما تابع Mqtt__Send را صدا زده ایم دقت نمایید، برای ما دستورات کانال 1 و 2 به شرح زیر است :

کانال 1 :

ch1on برای روشن
ch1off برای خاموش
 
این مقادیر برای سرور بصورت زیر است:
 
operationName=ch1 , value=on or off
 
 
 
کانال 2 :
 
ch2on برای روشن
ch2off برای خاموش
 
این مقادیر برای سرور بصورت زیر است:
 
operationName=ch1 , value=on or off
 
 
حال ممکن است این برای شما سوال شود آیا هم باید به همین روش در ابزار خود مقادیر را مشخص کنید؟ جواب خیر است! دقیقا هر دو فایل js در سرور وظیفه مترجم بین ابزار شما و سرور را به عهده دارند. شما می توانید به هر روشی این عملیات را انجام دهید.