نحوه اتصال دستگاه Arduino Nano RP2040 Connect به ThingsConnect

مقدمه

با استفاده از Arduino Nano RP2040 Connect با قابلیت‌های فراوان، می‌توانید میکروکنترلر Raspberry Pi RP2040 جدید را در فرم فرمت Nano به کار ببرید.
از هسته دوگانه 32 بیتی Arm® Cortex®-M0+ بهره بگیرید و با استفاده از ماژول U-blox Nina W102، پروژه‌های اینترنت اشیا با امکانات بلوتوث و وای‌فای را انجام دهید.
با استفاده از شتاب‌سنج، ژیروسکوپ، LED RGB و میکروفون داخلی، به پروژه‌های واقعی بپردازید.
با استفاده از Arduino Nano RP2040 Connect، راه حل‌های هوش مصنوعی تعبیه شده قوی را با کمترین تلاش توسعه دهید.

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

پیش‌نیازها

برای ادامه کار با این راهنما، نیاز به موارد زیر داریم:

  • Arduino Nano RP2040 Connect
    محیط توسعه Arduino IDE
  • حساب ThingsBoard

ایجاد دستگاه در ThingsConnect

برای سهولت، ما دستگاه را به صورت دستی با استفاده از رابط کاربری ایجاد خواهیم کرد.

  • وارد نمونه ThingsBoard خود شوید و به بخش “Entities” بروید. سپس صفحه “Devices” را انتخاب کنید.
  • روی نماد “+” در گوشه بالا و سمت راست جدول کلیک کنید و سپس “Add new device” را انتخاب کنید.
  • نام دستگاه را وارد کنید. به عنوان مثال، “دستگاه من”. در این مرحله تغییرات دیگری نیاز نیست. برای افزودن دستگاه، روی “Add” کلیک کنید.
  • دستگاه شما اضافه شده است.

//img

کتابخانه ها و ابزار مورد نیاز را نصب کنید

نصب برد برای محیط توسعه Arduino:

  • به قسمت ابزارها (Tools) بروید، سپس به قسمت برد (Board) و برنامه‌مدیر بردها (Board Manager) بروید و بردهای Arduino Mbed OS RP2040 Boards را توسط Arduino Board نصب کنید.

//img

پس از اتمام نصب، برد را از طریق منوی برد انتخاب کنید:
ابزارها (Tools) > برد (Board) > بردهای Arduino Mbed OS Nano > Arduino Nano RP2040 Connect.

برای نصب ThingsBoard Arduino SDK، باید مراحل زیر را انجام دهید:

  • به برگه “ابزارها” (Tools) بروید و روی “مدیریت کتابخانه‌ها” (Manage libraries) کلیک کنید.
  • کلمه “ThingsBoard” را در جعبه جستجو قرار دهید و دکمه “نصب” (INSTALL) برای کتابخانه پیدا شده را فشار دهید.

//img

همچنین، برای بردهای مبتنی بر چیپ RP2040 باید کتابخانه “WiFiNINA” را نصب کنیم.

  • عبارت “WiFiNINA” را در جعبه جستجوی کتابخانه قرار داده و کتابخانه “WiFiNINA by Arduino” را نصب کنید.

//img

در این نقطه، همه کتابخانه‌ها و ابزارهای مورد نیاز را نصب کرده‌ایم.

اتصال دستگاه به ThingsBoard
برای اتصال دستگاه، ابتدا باید اطلاعات اعتبار دستگاه را دریافت کنید. ThingsBoard از انواع مختلف اعتبار دستگاه پشتیبانی می‌کند. ما توصیه می‌کنیم از اعتبار پیش‌فرض خودکار تولید شده استفاده کنید که برای این راهنما یک توکن دسترسی است.

  • روی ردیف دستگاه در جدول کلیک کنید تا جزئیات دستگاه باز شود.
  • روی “کپی کردن توکن دسترسی” کلیک کنید. توکن به کلیپ‌بورد شما کپی خواهد شد. لطفاً آن را در یک مکان امن ذخیره کنید.

///img

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

#include <ThingsBoard.h>
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
#include <WiFi.h>
#else 
#include <WiFiNINA.h>
#endif

// Wifi credentials
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 = 512U;

// 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;


// 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 LED_COLOR_ATTR[] = "ledColor";

// 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;
// Current led colors
volatile uint8_t redColor = 255;
volatile uint8_t greenColor = 255;
volatile uint8_t blueColor = 255;
// 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;

// List of shared attributes for subscribing to their updates
constexpr std::array<const char *, 3U> SHARED_ATTRIBUTES_LIST = {
  LED_STATE_ATTR,
  BLINKING_INTERVAL_ATTR,
  LED_COLOR_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
};

const char *getMAC() {
  uint8_t macAddress[WL_MAC_ADDR_LENGTH];
  WiFi.macAddress(macAddress);
  char macStr[12];
  sprintf(macStr, "%x", *macAddress);
  return macStr;
}

const char *getBSSID() {
  uint8_t macAddress[WL_MAC_ADDR_LENGTH];
  WiFi.BSSID(macAddress);
  char macStr[12];
  sprintf(macStr, "%x", *macAddress);
  return macStr;
}

void setLedColor() {
  if (redColor < 255 && ledState) {
    analogWrite(LEDR, redColor);
  } else {
    pinMode(LEDR, OUTPUT);
    digitalWrite(LEDR, LOW);
  }
  if (greenColor < 255 && ledState) {
    analogWrite(LEDG, greenColor);
  } else {
    pinMode(LEDG, OUTPUT);
    digitalWrite(LEDG, LOW);
  }
  if (blueColor < 255 && ledState) {
    analogWrite(LEDB, blueColor);
  } else {
    pinMode(LEDB, OUTPUT);
    digitalWrite(LEDB, LOW);
  }
}

/// @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.print(".");
  }
  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() {
  // Check to ensure we aren't connected yet
  const uint8_t status = WiFi.status();
  if (status == WL_CONNECTED) {
    return true;
  }

  // If we aren't establish a new connection to the given WiFi network
  InitWiFi();
  return true;
}


/// @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);
}

// 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, 1U> callbacks = {
  RPC_Callback{ "setLedMode", processSetLedMode }
};

/// @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>();
      setLedColor();
      Serial.print("Updated state to: ");
      Serial.println(ledState);
    } else if (strcmp(it->key().c_str(), LED_COLOR_ATTR) == 0) {
      std::string data = it->value().as<std::string>();
      Serial.print("Updated colors: ");
      Serial.println(data.c_str());
      int i = 0;
      bool end = false;
      while (data.length() > 0) {
        int index = data.find(',');
        if (index == -1) {
          end = true;
          index = data.length();
        }
        switch (i) {
          case 0:
            redColor = map(atoi(data.substr(0, index).c_str()), 0, 255, 255, 0);
            break;
          case 1:
            greenColor = map(atoi(data.substr(0, index).c_str()), 0, 255, 255, 0);
            break;
          case 2:
            blueColor = map(atoi(data.substr(0, index).c_str()), 0, 255, 255, 0);
            break;
          default:
            break;
        }
        i++;
        if (end) {
          break;
        } else {
          data = data.substr(index + 1);
        }
      }
      Serial.print("Updating led color to values:");
      Serial.print("\tR: ");
      Serial.print(redColor);
      Serial.print("\tG: ");
      Serial.print(greenColor);
      Serial.print("\tB: ");
      Serial.println(blueColor);
      setLedColor();
    }
  }
  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() {
  // Initalize serial connection for debugging
  Serial.begin(115200);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  digitalWrite(LEDR, LOW);
  digitalWrite(LEDG, LOW);
  digitalWrite(LEDB, LOW);
  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", getMAC());
  }

  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 (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;
    setLedColor();
    tb.sendTelemetryBool(LED_STATE_ATTR, ledState);
    tb.sendAttributeBool(LED_STATE_ATTR, ledState);
  }

  // Sending telemetry every telemetrySendInterval time
  if (millis() - previousDataSend > telemetrySendInterval) {
    previousDataSend = millis();
    tb.sendTelemetryInt("temperature", random(10, 20));
    tb.sendAttributeInt("rssi", WiFi.RSSI());
    tb.sendAttributeString("ssid", WIFI_SSID);
    tb.sendAttributeString("bssid", getBSSID());
    tb.sendAttributeString("localIp", String(String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3])).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 = 512U;
constexpr uint32_t SERIAL_DEBUG_BAUD = 115200U;

...

بخش ارسال داده در کد (به طور پیش فرض، نمونه مقدار تصادفی برای کلید دما و برخی اطلاعات WiFi را ارسال می‌کند).

...
    tb.sendTelemetryInt("temperature", random(10, 20));
    tb.sendAttributeInt("rssi", WiFi.RSSI());
    tb.sendAttributeString("ssid", WIFI_SSID);
    tb.sendAttributeString("bssid", getBSSID());
    tb.sendAttributeString("localIp", String(String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3])).c_str());
...

سپس با فشردن دکمه “بارگذاری” یا ترکیب صفحه کلید Ctrl+U، کد را به دستگاه بارگذاری کنید.

//img

بررسی داده ها بر روی ThingsConnect

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

ابتدا پرونده داشبورد بررسی و کنترل داده دستگاه را دانلود کنید.

برای اضافه کردن داشبورد به ThingsBoard، باید اقدامات زیر را انجام دهید:

  • از طریق منوی اصلی در سمت چپ صفحه، به بخش “داشبوردها” بروید.
  • روی دکمه “+” در گوشه بالا سمت راست صفحه کلیک کنید و “وارد کردن داشبورد” را انتخاب کنید.
  • پرونده dashboard.json خود را انتخاب کرده و دکمه وارد کردن را فشار دهید.
  • اکنون می‌توانید داشبورد وارد شده را در جدول مشاهده کنید.

///img

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

برای بررسی داده‌ها از دستگاه خود، باید داشبورد وارد شده را باز کنید:

  • با کلیک بر روی آن در جدول، داشبورد را باز کنید.
  • مشاهده داده و کنترل داشبورد دستگاه ما.
  • دریافت ویژگی‌های دریافت شده از دستگاه.
  • اطلاعات دستگاه از سرور ThingsBoard.
  • ویجت برای مشاهده تاریخچه تغییرات حالت LED.
  • ویجت برای مشاهده تاریخچه دمای شبیه‌سازی شده ما.

//Img

هماهنگ‌سازی وضعیت دستگاه با استفاده از درخواست‌های مشترک و درخواست‌های ویژگی کاربر

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

  • تماس‌های بازگشت ویژگی (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) داریم. تماس بازگشت اول برای ویژگی‌های مشترک (shared attributes) و تماس بازگشت دوم برای ویژگی‌های مشتری (client attributes) است.
تماس بازگشت اول پاسخی با فاصله‌زمان چشمک‌زنی (blinking interval) دریافت می‌کند تا با استفاده از آن بازه زمانی صحیح برای چشمک‌زنی را تنظیم کند.
تماس بازگشت دوم حالت و وضعیت LED را دریافت و آنها را ذخیره و تنظیم می‌کند.
این قابلیت به ما امکان می‌دهد تا پس از راه‌اندازی مجدد، وضعیت فعلی را حفظ کنیم.

  • درخواست های ویژگی:
...
  // 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);
    }
  }
...

با توجه به اینکه برد شامل LED RGB است، می‌توانیم رنگ آن را کنترل کنیم.

  • شما می‌توانید با استفاده از ویجت موجود در داشبورد ThingsBoard، رنگ LED را بروزرسانی کنید.

/img

برای کنترل led، ویژگی مشترک “ledColor” را تغییر می دهیم. حاوی مقادیر RGB در قالب رشته زیر “R,G,B” است.

قسمت زیر از کد برای تجزیه مقادیر ورودی و ذخیره آنها استفاده می شود:

...
if (strcmp(it->key().c_str(), LED_COLOR_ATTR) == 0) {
  std::string data = it->value().as<std::string>();
  Serial.print("Updated colors: ");
  Serial.println(data.c_str());
  int i = 0;
  bool end = false;
  while (data.length() > 0) {
    int index = data.find(',');
    if (index == -1) {
      end = true;
      index = data.length();
    }
    switch (i) {
      case 0:
        redColor = map(atoi(data.substr(0, index).c_str()), 0, 255, 255, 0);
        break;
      case 1:
        greenColor = map(atoi(data.substr(0, index).c_str()), 0, 255, 255, 0);
        break;
      case 2:
        blueColor = map(atoi(data.substr(0, index).c_str()), 0, 255, 255, 0);
        break;
      default:
        break;
    }
    i++;
    if (end) {
      break;
    } else {
      data = data.substr(index + 1);
    }
  }
  setLedColor();
}
...

برای تنظیم رنگ LED از تابع زیر در کد استفاده می کنیم:

...
void setLedColor() {
  if (redColor < 255 && ledState) {
    analogWrite(LEDR, redColor);
  } else {
    pinMode(LEDR, OUTPUT);
    digitalWrite(LEDR, LOW);
  }
  if (greenColor < 255 && ledState) {
    analogWrite(LEDG, greenColor);
  } else {
    pinMode(LEDG, OUTPUT);
    digitalWrite(LEDG, LOW);
  }
  if (blueColor < 255 && ledState) {
    analogWrite(LEDB, blueColor);
  } else {
    pinMode(LEDB, OUTPUT);
    digitalWrite(LEDB, LOW);
  }
}
...

شما می توانید منطق را برای رسیدن به اهداف خود تغییر دهید و برای ویژگی های خود پردازش اضافه کنید.

کنترل دستگاه با استفاده از 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, 1U> callbacks = {
  RPC_Callback{ "setLedMode", processSetLedMode }
};

...
  • اشتراک برای درخواست های RPC:
...
    if (!tb.RPC_Subscribe(callbacks.cbegin(), callbacks.cend())) {
      Serial.println("Failed to subscribe for RPC");
      return;
    }
...

می توانید کد را تغییر دهید تا به اهداف خود برسید و پردازشی برای دستورات RPC خود اضافه کنید.

نتیجه

با دانش بیان شده در این راهنما، می توانید به راحتی Arduino Nano RP2040 Connect خود را متصل کرده و داده ها را به ThingsBoard ارسال کنید.

برای کسب اطلاعات بیشتر در مورد مفاهیم و ویژگی های کلیدی، اسناد پلت فرم را کاوش کنید. به عنوان مثال، قوانین هشدار یا داشبورد را پیکربندی کنید.

عناوین هر بخش