نحوه اتصال m5stack timer camera x به ThingsConnect
مقدمه
دوربین تایمر M5Stack Timer Camera X یک ماژول دوربین بر اساس ESP32 است که با چیپ ESP32 و 8M-PSRAM یکپارچه شده است.
دوربین (OV3660) با 3 میلیون پیکسل، DFOV 66.5 درجه و عکس با رزولوشن 2048×1536 را به تصویر میکشد. این دوربین دارای باتری 140mAh و نشانگر وضعیت LED است که دارای مصرف انرژی بسیار کم است.
دکمهی ریست در زیر LED وجود دارد. امکان استفاده از تایمینگ خواب و بیداری از طریق RTC (BM8563) وجود دارد. جریان استندبای دستگاه فقط 2μA است.
در این راهنما، ما به بحث در مورد اتصال بردهای مبتنی بر ESP32 به ThingsBoard خواهیم پرداخت.
در این راهنما، خواهیم آموخت که چگونه دستگاه را در ThingsBoard ایجاد کنیم و کتابخانهها و ابزارهای مورد نیاز را نصب کنیم.
سپس کد خود را تغییر داده و آن را به دستگاه بارگذاری کرده و نتایج کدنویسی خود را بررسی کرده و با استفاده از داشبورد وارد شده، دادهها را در ThingsBoard بررسی خواهیم کرد. دستگاه ما با استفاده از عملکرد درخواستهای ویژگیهای مشترک با ThingsBoard همگام میشود.
البته، ما دستگاه خود را با استفاده از قابلیتهای ارائه شده مانند ویژگیهای مشترک یا درخواستهای RPC کنترل خواهیم کرد.
پیشنیازها
برای ادامه کار با این راهنما، نیاز به موارد زیر دارید:
- دوربین M5Stack Timer Camera X
- محیط توسعه Arduino IDE
- حساب کاربری ThingsBoard
ایجاد دستگاه در ThingsConnect
برای ایجاد یک دستگاه در ThingsBoard، میتوانید به صورت دستی از طریق رابط کاربری (UI) عمل کنید. به دنبال راهنمای زیر برای ایجاد دستگاه بروید:
- به نمونه ThingsBoard خود وارد شده و به بخش “Entities” بروید. سپس صفحه “Devices” را انتخاب کنید.
- روی آیکون “+” در گوشه بالا و سمت راست جدول کلیک کنید و سپس “Add new device” را انتخاب کنید.
- نام دستگاه را وارد کنید. به عنوان مثال، “My Device”. در این مرحله تغییر دیگری لازم نیست. برای افزودن دستگاه روی “Add” کلیک کنید.
- دستگاه شما اضافه شده است.
//img
نصب کتابخانهها و ابزارهای مورد نیاز
برای نصب برد در محیط توسعه Arduino IDE: به بخش File (فایل) > Preferences (تنظیمات) بروید و آدرس زیر را در فیلد Additional Boards Manager URLs (آدرسهای افزوده برای مدیر بردها) اضافه کنید.
https://www.M5Stack.com/download/package_m5stack_index.json
سپس به قسمت Tools (ابزارها) > Board (برد) > Board Manager (مدیر بردها) بروید و برد M5Stack از سازنده M5Stack Official را نصب کنید.
//img
پس از اتمام نصب، برد را از طریق منوی Board انتخاب کنید:
Tools (ابزارها) > Board (برد) > M5Stack > M5Stack-Timer-CAM.
برای نصب ThingsBoard Arduino SDK ، باید مراحل زیر را انجام دهید:
- به برگه “Tools” (ابزارها) بروید و بر روی “Manage libraries” (مدیریت کتابخانهها) کلیک کنید.
- در جعبه جستجو، “ThingsBoard” را وارد کنید و برای کتابخانه پیدا شده دکمه “INSTALL” (نصب) را فشار دهید.
//img
در این مرحله، تمام کتابخانهها و ابزارهای مورد نیاز را نصب کردهایم.
اتصال دستگاه به ThingsConnect
برای اتصال دستگاه به ThingsBoard، ابتدا باید اطلاعات احراز هویت دستگاه را دریافت کنید. ThingsBoard از انواع مختلفی از احراز هویت دستگاه پشتیبانی میکند. ما توصیه میکنیم از احراز هویت پیشفرض و خودکار که یک توکن دسترسی استفاده میکند، برای این راهنما استفاده کنید.
روی ردیف دستگاه در جدول کلیک کنید تا جزئیات دستگاه باز شود.
روی “Copy access token” (کپی توکن دسترسی) کلیک کنید. توکن به کلیپبورد شما کپی میشود. لطفاً آن را در یک محل امن ذخیره کنید.
///img
کد زیر را میتوانید برای برنامهنویسی برد و اتصال آن به ThingsBoard استفاده کنید. این کد شامل تمام قابلیتهای مورد نیاز برای این راهنما است.
#include "battery.h" #include "esp_camera.h" #include <WiFi.h> #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" #define THINGSBOARD_ENABLE_DYNAMIC 1 #include <ThingsBoard.h> #include <esp_heap_caps.h> extern "C" { #include "libb64/cencode.h" } constexpr char WIFI_SSID[] = "YOUR_WIFI_SSID"; constexpr char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; // See https://thingsboard.io/docs/getting-started-guides/helloworld/ // to understand how to obtain an access token constexpr char TOKEN[] = "YOUR_ACCESS_TOKEN"; // Thingsboard we want to establish a connection too constexpr char THINGSBOARD_SERVER[] = "demo.thingsboard.io"; // MQTT port used to communicate with the server, 1883 is the default unencrypted MQTT port. constexpr uint16_t THINGSBOARD_PORT = 1883U; // Maximum size packets will ever be sent or received by the underlying MQTT client, // if the size is to small messages might not be sent or received messages will be discarded constexpr uint32_t MAX_MESSAGE_SIZE = 100U * 1024; // Baud rate for the debugging serial connection. // If the Serial output is mangled, ensure to change the monitor speed accordingly to this variable constexpr uint32_t SERIAL_DEBUG_BAUD = 115200U; // Definitions for camera pins #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 #define SIOD_GPIO_NUM 25 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 19 #define Y8_GPIO_NUM 36 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 5 #define Y4_GPIO_NUM 34 #define Y3_GPIO_NUM 35 #define Y2_GPIO_NUM 32 #define VSYNC_GPIO_NUM 22 #define HREF_GPIO_NUM 26 #define PCLK_GPIO_NUM 21 // Initialize underlying client, used to establish a connection WiFiClient wifiClient; // Initialize ThingsBoard instance with the maximum needed buffer size ThingsBoard tb(wifiClient, MAX_MESSAGE_SIZE); // Attribute names for attribute request and attribute updates functionality constexpr char BLINKING_INTERVAL_ATTR[] = "blinkingInterval"; constexpr char LED_MODE_ATTR[] = "ledMode"; constexpr char LED_STATE_ATTR[] = "ledState"; constexpr char PICTURE_ATTR[] = "photo"; // Statuses for subscribing to rpc bool subscribed = false; // handle led state and mode changes volatile bool attributesChanged = false; // LED modes: 0 - continious state, 1 - blinking volatile int ledMode = 0; // Current led state volatile bool ledState = false; // Settings for interval in blinking mode constexpr uint16_t BLINKING_INTERVAL_MS_MIN = 10U; constexpr uint16_t BLINKING_INTERVAL_MS_MAX = 60000U; volatile uint16_t blinkingInterval = 1000U; uint32_t previousStateChange; // For telemetry constexpr int16_t telemetrySendInterval = 2000U; uint32_t previousDataSend; // Picture buffer char *imageBuffer; // Flag to send a picture volatile bool sendPicture = false; // List of shared attributes for subscribing to their updates constexpr std::array<const char *, 2U> SHARED_ATTRIBUTES_LIST = { LED_STATE_ATTR, BLINKING_INTERVAL_ATTR }; // List of client attributes for requesting them (Using to initialize device states) constexpr std::array<const char *, 1U> CLIENT_ATTRIBUTES_LIST = { LED_MODE_ATTR }; /// @brief Initalizes WiFi connection, // will endlessly delay until a connection has been successfully established void InitWiFi() { Serial.println("Connecting to AP ..."); // Attempting to establish a connection to the given WiFi network WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) { // Delay 500ms until a connection has been succesfully established delay(500); Serial.println(WiFi.status()); Serial.println(WL_CONNECTED); Serial.println("."); } Serial.println("Connected to AP"); } /// @brief Reconnects the WiFi uses InitWiFi if the connection has been removed /// @return Returns true as soon as a connection has been established again const bool reconnect() { if (WiFi.status() == WL_CONNECTED) { return true; } // If we aren't establish a new connection to the given WiFi network InitWiFi(); return true; } bool initCamera() { camera_config_t config; config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = Y2_GPIO_NUM; config.pin_d1 = Y3_GPIO_NUM; config.pin_d2 = Y4_GPIO_NUM; config.pin_d3 = Y5_GPIO_NUM; config.pin_d4 = Y6_GPIO_NUM; config.pin_d5 = Y7_GPIO_NUM; config.pin_d6 = Y8_GPIO_NUM; config.pin_d7 = Y9_GPIO_NUM; config.pin_xclk = XCLK_GPIO_NUM; config.pin_pclk = PCLK_GPIO_NUM; config.pin_vsync = VSYNC_GPIO_NUM; config.pin_href = HREF_GPIO_NUM; config.pin_sscb_sda = SIOD_GPIO_NUM; config.pin_sscb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = 20000000; config.pixel_format = PIXFORMAT_JPEG; config.frame_size = FRAMESIZE_240X240; config.jpeg_quality = 10; config.fb_count = 1; esp_err_t err = esp_camera_init(&config); if (err != ESP_OK) { Serial.printf("Camera init failed with error 0x%x", err); return false; } sensor_t *s = esp_camera_sensor_get(); // initial sensors are flipped vertically and colors are a bit saturated s->set_vflip(s, 1); // flip it back s->set_brightness(s, 1); // up the brightness just a bit s->set_saturation(s, -2); // lower the saturation return true; } bool captureImage() { camera_fb_t *fb = NULL; fb = esp_camera_fb_get(); if (!fb) { return false; } encode((uint8_t *)fb->buf, fb->len); esp_camera_fb_return(fb); return true; } void encode(const uint8_t *data, size_t length) { size_t size = base64_encode_expected_len(length) + 1; base64_encodestate _state; base64_init_encodestate(&_state); int len = base64_encode_block((char *)&data[0], length, &imageBuffer[0], &_state); len = base64_encode_blockend((imageBuffer + len), &_state); } /// @brief Processes function for RPC call "setLedMode" /// RPC_Data is a JSON variant, that can be queried using operator[] /// See https://arduinojson.org/v5/api/jsonvariant/subscript/ for more details /// @param data Data containing the rpc data that was called and its current value /// @return Response that should be sent to the cloud. Useful for getMethods RPC_Response processSetLedMode(const RPC_Data &data) { Serial.println("Received the set led state RPC method"); // Process data int new_mode = data; Serial.print("Mode to change: "); Serial.println(new_mode); if (new_mode != 0 && new_mode != 1) { return RPC_Response("error", "Unknown mode!"); } ledMode = new_mode; attributesChanged = true; // Returning current mode return RPC_Response("newMode", (int)ledMode); } /// @brief Processes function for RPC call "setLedMode" /// RPC_Data is a JSON variant, that can be queried using operator[] /// See https://arduinojson.org/v5/api/jsonvariant/subscript/ for more details /// @param data Data containing the rpc data that was called and its current value /// @return Response that should be sent to the cloud. Useful for getMethods RPC_Response processTakePicture(const RPC_Data &data) { Serial.println("Received the take picture RPC method"); if (!captureImage()) { return RPC_Response("error", "Cannot take a picture!"); } sendPicture = true; // Returning current mode return RPC_Response("size", strlen(imageBuffer)); } // Optional, keep subscribed shared attributes empty instead, // and the callback will be called for every shared attribute changed on the device, // instead of only the one that were entered instead const std::array<RPC_Callback, 2U> callbacks = { RPC_Callback{ "setLedMode", processSetLedMode }, RPC_Callback{ "takePicture", processTakePicture } }; /// @brief Update callback that will be called as soon as one of the provided shared attributes changes value, /// if none are provided we subscribe to any shared attribute change instead /// @param data Data containing the shared attributes that were changed and their current value void processSharedAttributes(const Shared_Attribute_Data &data) { for (auto it = data.begin(); it != data.end(); ++it) { if (strcmp(it->key().c_str(), BLINKING_INTERVAL_ATTR) == 0) { const uint16_t new_interval = it->value().as<uint16_t>(); if (new_interval >= BLINKING_INTERVAL_MS_MIN && new_interval <= BLINKING_INTERVAL_MS_MAX) { blinkingInterval = new_interval; Serial.print("Updated blinking interval to: "); Serial.println(new_interval); } } else if (strcmp(it->key().c_str(), LED_STATE_ATTR) == 0) { ledState = it->value().as<bool>(); digitalWrite(LED_BUILTIN, ledState ? HIGH : LOW); Serial.print("Updated state to: "); Serial.println(ledState); } } attributesChanged = true; } void processClientAttributes(const Shared_Attribute_Data &data) { for (auto it = data.begin(); it != data.end(); ++it) { if (strcmp(it->key().c_str(), LED_MODE_ATTR) == 0) { const uint16_t new_mode = it->value().as<uint16_t>(); ledMode = new_mode; } } } const Shared_Attribute_Callback attributes_callback(SHARED_ATTRIBUTES_LIST.cbegin(), SHARED_ATTRIBUTES_LIST.cend(), &processSharedAttributes); const Attribute_Request_Callback attribute_shared_request_callback(SHARED_ATTRIBUTES_LIST.cbegin(), SHARED_ATTRIBUTES_LIST.cend(), &processSharedAttributes); const Attribute_Request_Callback attribute_client_request_callback(CLIENT_ATTRIBUTES_LIST.cbegin(), CLIENT_ATTRIBUTES_LIST.cend(), &processClientAttributes); void setup() { WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable detector bat_init(); bat_hold_output(); ledcAttachPin(4, 4); ledcSetup(4, 5000, 8); // Initalize serial connection for debugging imageBuffer = (char *)ps_malloc(50U * 1024); Serial.begin(SERIAL_DEBUG_BAUD); Serial.println("Camera initialization..."); if (!initCamera()) { Serial.println("Camera initialization failed!"); ESP.restart(); } pinMode(LED_BUILTIN, OUTPUT); delay(1000); InitWiFi(); } void loop() { delay(10); if (!reconnect()) { subscribed = false; return; } if (!tb.connected()) { subscribed = false; // Connect to the ThingsBoard Serial.print("Connecting to: "); Serial.print(THINGSBOARD_SERVER); Serial.print(" with token "); Serial.println(TOKEN); if (!tb.connect(THINGSBOARD_SERVER, TOKEN, THINGSBOARD_PORT)) { Serial.println("Failed to connect"); return; } // Sending a MAC address as an attribute tb.sendAttributeString("macAddress", WiFi.macAddress().c_str()); } if (!subscribed) { Serial.println("Subscribing for RPC..."); // Perform a subscription. All consequent data processing will happen in // processSetLedState() and processSetLedMode() functions, // as denoted by callbacks array. if (!tb.RPC_Subscribe(callbacks.cbegin(), callbacks.cend())) { Serial.println("Failed to subscribe for RPC"); return; } if (!tb.Shared_Attributes_Subscribe(attributes_callback)) { Serial.println("Failed to subscribe for shared attribute updates"); return; } Serial.println("Subscribe done"); subscribed = true; // Request current states of shared attributes if (!tb.Shared_Attributes_Request(attribute_shared_request_callback)) { Serial.println("Failed to request for shared attributes"); return; } // Request current states of client attributes if (!tb.Client_Attributes_Request(attribute_client_request_callback)) { Serial.println("Failed to request for client attributes"); return; } } if (sendPicture) { tb.sendTelemetryString(PICTURE_ATTR, imageBuffer); sendPicture = false; } if (attributesChanged) { attributesChanged = false; if (ledMode == 0) { previousStateChange = millis(); } tb.sendTelemetryInt(LED_MODE_ATTR, ledMode); tb.sendTelemetryBool(LED_STATE_ATTR, ledState); tb.sendAttributeInt(LED_MODE_ATTR, ledMode); tb.sendAttributeBool(LED_STATE_ATTR, ledState); } if (ledMode == 1 && millis() - previousStateChange > blinkingInterval) { previousStateChange = millis(); ledState = !ledState; digitalWrite(LED_BUILTIN, ledState); tb.sendTelemetryBool(LED_STATE_ATTR, ledState); tb.sendAttributeBool(LED_STATE_ATTR, ledState); if (LED_BUILTIN == 99) { Serial.print("LED state changed to: "); Serial.println(ledState); } } // Sending telemetry every telemetrySendInterval time if (millis() - previousDataSend > telemetrySendInterval) { previousDataSend = millis(); tb.sendTelemetryInt("temperature", random(10, 20)); tb.sendAttributeInt("rssi", WiFi.RSSI()); tb.sendAttributeInt("channel", WiFi.channel()); tb.sendAttributeString("ssid", WIFI_SSID); tb.sendAttributeString("localIp", WiFi.localIP().toString().c_str()); } tb.loop(); }
در کد، جایگزین کردن عبارات کلیدی با نام شبکه WiFi SSID، رمز عبور، و توکن دسترسی دستگاه ThingsBoard لازم است.
متغیرهای مورد نیاز برای اتصال عبارتند از:
///جدول
... constexpr char WIFI_SSID[] = "YOUR_WIFI_SSID"; constexpr char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; constexpr char TOKEN[] = "YOUR_ACCESS_TOKEN"; constexpr char THINGSBOARD_SERVER[] = "demo.thingsboard.io"; constexpr uint16_t THINGSBOARD_PORT = 1883U; constexpr uint32_t MAX_MESSAGE_SIZE = 256U; constexpr uint32_t SERIAL_DEBUG_BAUD = 115200U; ...
بخش ارسال داده (به طور پیش فرض، نمونه مقدار تصادفی برای کلید دما و برخی اطلاعات WiFi را ارسال میکند):
... tb.sendTelemetryInt("temperature", random(10, 20)); tb.sendAttributeInt("rssi", WiFi.RSSI()); tb.sendAttributeString("bssid", WiFi.BSSIDstr().c_str()); tb.sendAttributeString("localIp", WiFi.localIP().toString().c_str()); tb.sendAttributeString("ssid", WiFi.SSID().c_str()); tb.sendAttributeInt("channel", WiFi.channel()); ...
سپس با فشار دادن دکمه “Upload” یا ترکیب کلیدهای Ctrl+U، کد را به دستگاه بارگذاری کنید.
//img
بررسی داده ها در ThingsConnect
برای بررسی دادهها و ارسال دستورات یا داده به دستگاه، ما نیاز به ایجاد داشبورد داریم.
ابتدا پرونده داشبورد “Check and control device data” را دانلود کنید.
برای افزودن داشبورد به ThingsBoard، باید آن را وارد کنیم و برای انجام این کار، باید مراحل زیر را طی کنیم:
- از طریق منوی اصلی در سمت چپ صفحه به بخش “Dashboards” (داشبوردها) بروید.
- روی دکمه “+” در گوشه سمت راست صفحه کلیک کنید و “Import dashboard” (وارد کردن داشبورد) را انتخاب کنید.
- فایل dashboard.json خود را انتخاب کنید و دکمه import را فشار دهید.
- حالا میتوانید داشبورد وارد شده را در جدول مشاهده کنید.
///img
پس از وارد کردن داشبورد، باید برای دستگاه خود یک نام مستعار (Alias) انتخاب کنیم.
برای انجام این کار، باید روی آیکون خودکار قلم کلیک کنید و سپس “Entity Aliases” (نامهای مستعار موجودیت) را انتخاب کنید. سپس نام مستعار “My device” را انتخاب کنید و با فشار دادن آیکون خودکار قلم، آن را برای ویرایش باز کنید.
سپس از لیست کشویی، یک دستگاه با نام “My device” را انتخاب کنید و نام مستعار موجودیت را ذخیره کنید. حالا باید قادر به مشاهده دادهها از دستگاه خود باشید.
برای بررسی دادهها از دستگاه خود، باید داشبورد وارد شده را باز کنید:
- با کلیک بر روی داشبورد در جدول، آن را باز کنید.
- نمایی از داشبورد برای بررسی دادهها و کنترل دستگاه ما.
- ویژگیهای دریافت شده از دستگاه.
- اطلاعات دستگاه از سرور ThingsBoard.
- ویجت برای مشاهده تاریخچه تغییرات حالت LED.
- ویجت برای مشاهده تاریخچه دمای شبیهسازی شده ما.
//img
همگامسازی وضعیت دستگاه با استفاده از درخواستهای کلاینت و ویژگیهای مشترک
برای همگامسازی وضعیت دستگاه با استفاده از درخواستهای کلاینت و ویژگیهای مشترک، در کد نمونه قابلیتی برای انجام این کار وجود دارد. قسمتهای مسئول در نمونه کد به شرح زیر است:
بازخوانی ویژگیها (Attribute callbacks):
... void processSharedAttributes(const Shared_Attribute_Data &data) { for (auto it = data.begin(); it != data.end(); ++it) { if (strcmp(it->key().c_str(), BLINKING_INTERVAL_ATTR) == 0) { const uint16_t new_interval = it->value().as<uint16_t>(); if (new_interval >= BLINKING_INTERVAL_MS_MIN && new_interval <= BLINKING_INTERVAL_MS_MAX) { blinkingInterval = new_interval; Serial.print("Updated blinking interval to: "); Serial.println(new_interval); } } else if(strcmp(it->key().c_str(), LED_STATE_ATTR) == 0) { ledState = it->value().as<bool>(); digitalWrite(LED_BUILTIN, ledState ? HIGH : LOW); Serial.print("Updated state to: "); Serial.println(ledState); } } attributesChanged = true; } void processClientAttributes(const Shared_Attribute_Data &data) { for (auto it = data.begin(); it != data.end(); ++it) { if (strcmp(it->key().c_str(), LED_MODE_ATTR) == 0) { const uint16_t new_mode = it->value().as<uint16_t>(); ledMode = new_mode; } } } ... const Attribute_Request_Callback attribute_shared_request_callback(SHARED_ATTRIBUTES_LIST.cbegin(), SHARED_ATTRIBUTES_LIST.cend(), &processSharedAttributes); const Attribute_Request_Callback attribute_client_request_callback(CLIENT_ATTRIBUTES_LIST.cbegin(), CLIENT_ATTRIBUTES_LIST.cend(), &processClientAttributes); ...
همانطور که مشاهده میکنید، ما 2 بازخوانی (callback) داریم، یکی برای ویژگیهای مشترک و دیگری برای ویژگیهای کلاینت.
اولین بازخوانی پاسخی با بازه چشمک زنی دریافت میکند تا بازه صحیح برای چشمک زنی تنظیم شود.
بازخوانی دوم حالت و وضعیت LED را دریافت میکند تا آنها را ذخیره کرده و تنظیم کند.
این قابلیت به ما امکان میدهد که پس از راهاندازی دستگاه، وضعیت واقعی را حفظ کنیم.
درخواستهای ویژگیها (Attribute requests):
... // Request current states of shared attributes if (!tb.Shared_Attributes_Request(attribute_shared_request_callback)) { Serial.println("Failed to request for shared attributes"); return; } // Request current states of client attributes if (!tb.Client_Attributes_Request(attribute_client_request_callback)) { Serial.println("Failed to request for client attributes"); return; } ...
برای اینکه تماسهای بازگشتی ما داده را دریافت کنند، باید یک درخواست به ThingsBoard ارسال کنیم.
کنترل دستگاه با استفاده از ویژگی های مشترک
همچنین، میتوانیم با استفاده از قابلیت بهروزرسانی ویژگیهای مشترک، ورودی را تغییر دهیم.
- برای تغییر دوره چشمک زنی، فقط کافیست مقدار مربوطه را در داشبورد خود تغییر دهید.
- بعد از اعمال تغییرات با فشار دادن نشانه تیک، یک پیام تأیید را مشاهده خواهید کرد.
//img
برای تغییر وضعیت هنگامی که حالت چشمک زنی غیرفعال است، میتوانیم از سوئیچ موجود در همان ویجت استفاده کنیم.
این کار فقط زمانی امکانپذیر است که حالت چشمک زنی غیرفعال است.
//img
برای دستیابی به این هدف، متغیری به نام “blinkingInterval” در بخشهای زیر از کد استفاده شده است:
- کالبک برای بهروزرسانی ویژگیهای مشترک:
... void processSharedAttributes(const Shared_Attribute_Data &data) { for (auto it = data.begin(); it != data.end(); ++it) { if (strcmp(it->key().c_str(), BLINKING_INTERVAL_ATTR) == 0) { const uint16_t new_interval = it->value().as<uint16_t>(); if (new_interval >= BLINKING_INTERVAL_MS_MIN && new_interval <= BLINKING_INTERVAL_MS_MAX) { blinkingInterval = new_interval; Serial.print("Updated blinking interval to: "); Serial.println(new_interval); } } else if(strcmp(it->key().c_str(), LED_STATE_ATTR) == 0) { ledState = it->value().as<bool>(); digitalWrite(LED_BUILTIN, ledState ? HIGH : LOW); Serial.print("Updated state to: "); Serial.println(ledState); } } attributesChanged = true; } ... const Shared_Attribute_Callback attributes_callback(SHARED_ATTRIBUTES_LIST.cbegin(), SHARED_ATTRIBUTES_LIST.cend(), &processSharedAttributes); ...
- اشتراک برای بهروزرسانی ویژگیهای مشترک:
... if (!tb.Shared_Attributes_Request(attribute_shared_request_callback)) { Serial.println("Failed to request for shared attributes"); return; } ...
- بخشی از کد برای چشمک زدن:
... if (ledMode == 1 && millis() - previousStateChange > blinkingInterval) { previousStateChange = millis(); ledState = !ledState; digitalWrite(LED_BUILTIN, ledState); tb.sendTelemetryBool(LED_STATE_ATTR, ledState); tb.sendAttributeBool(LED_STATE_ATTR, ledState); if (LED_BUILTIN == 99) { Serial.print("LED state changed to: "); Serial.println(ledState); } } ...
شما میتوانید منطق را تغییر دهید تا به اهداف خود برسید و پردازشی برای ویژگیهایتان اضافه کنید.
کنترل دستگاه با استفاده از RPC
شما میتوانید به صورت دستی وضعیت LED را تغییر داده و حالت آن را بین روشنایی مداوم و چشمکزنی تغییر دهید. برای این کار، میتوانید از بخشهای زیر در داشبورد ما استفاده کنید:
- استفاده از ویجت سوئیچ برای تغییر وضعیت LED به روشنایی مداوم.
- استفاده از ویجت سوئیچ گرد برای تغییر وضعیت LED به حالت چشمکزنی.
//img
لطفاً توجه کنید که شما تنها میتوانید وضعیت LED را تغییر دهید در صورتی که حالت چشمکزنی غیرفعال باشد.
در مثال کد، ما قابلیت بررسی دستورات RPC را داریم.
برای دسترسی به قابلیت کنترل دستگاه، از بخشهای زیر در کد استفاده کردهایم:
- تابع بازخوانی برای درخواستهای RPC:
... RPC_Response processSetLedMode(const RPC_Data &data) { Serial.println("Received the set led state RPC method"); // Process data int new_mode = data; Serial.print("Mode to change: "); Serial.println(new_mode); if (new_mode != 0 && new_mode != 1) { return RPC_Response("error", "Unknown mode!"); } ledMode = new_mode; attributesChanged = true; // Returning current mode return RPC_Response("newMode", (int)ledMode); } ... const std::array<RPC_Callback, 2U> callbacks = { RPC_Callback{ "setLedMode", processSetLedMode }, RPC_Callback{ "takePicture", processTakePicture } }; ...
- اشتراک برای درخواست های RPC:
... if (!tb.RPC_Subscribe(callbacks.cbegin(), callbacks.cend())) { Serial.println("Failed to subscribe for RPC"); return; } ...
مانند اینکه برد شامل دوربین است، ما میتوانیم یک عکس بگیریم و آن را در داشبورد مشاهده کنیم.
میتوانید با فشار دادن دکمه روی داشبورد ThingsBoard، تصویری از ماژول دوربین بگیرید و آن را مشاهده کنید.
//img
برای گرفتن عکس، ما دستور “takePicture” را به دستگاه ارسال میکنیم.
بخش زیر از کد، یک عکس میگیرد.
... bool captureImage() { camera_fb_t *fb = NULL; fb = esp_camera_fb_get(); if (!fb) { return false; } encode((uint8_t *)fb->buf, fb->len); esp_camera_fb_return(fb); return true; } ...
به دلیل عدم قابلیت ارسال یک آرایه بایت خام از عکس در قالب JSON، ما همچنین بایتها را به Base64 تبدیل میکنیم.
... void encode(const uint8_t *data, size_t length) { size_t size = base64_encode_expected_len(length) + 1; base64_encodestate _state; base64_init_encodestate(&_state); int len = base64_encode_block((char *)&data[0], length, &imageBuffer[0], &_state); len = base64_encode_blockend((imageBuffer + len), &_state); } ...
تصویر رمزگذاری شده ما در حلقه اصلی ارسال خواهد شد.
... if (sendPicture) { tb.sendTelemetryString(PICTURE_ATTR, imageBuffer); sendPicture = false; } ...
شما میتوانید کد را تغییر دهید تا به اهداف خود برسید و پردازش برای دستورات RPC خود اضافه کنید.
نتیجه
با دانش ذکر شده در این راهنما، می توانید به راحتی M5Stack Timer Camera X خود را متصل کرده و داده ها را به ThingsBoard ارسال کنید.
برای کسب اطلاعات بیشتر در مورد مفاهیم و ویژگی های کلیدی، اسناد پلت فرم را کاوش کنید. به عنوان مثال، قوانین هشدار یا داشبورد را پیکربندی کنید.