نحوه اتصال esp32 dev kit v1 به ThingsConnect

مقدمه

مجموعه توسعه ESP32 Dev Kit V1 بر پایه ماژول ESP-WROOM-32 ساخته شده است که یک تراشه Wi-Fi + BT + BLE بسیار کوچک و با عملکرد بالا از Espressif است. این ماژول برای یک دستگاهی با تنظیمات حداقلی مورد نیاز طراحی شده است که برای شروع سریع و راحت کار با ESP-WROOM-32 کافی است.

ESP-WROOM-32 بر پایه چیپست دو هسته‌ای ESP32 محبوب است و دارای فرکانس ساعت متغیر از 80 مگاهرتز تا 240 مگاهرتز است که قابلیت کنترل و تامین برق انفرادی را دارد. این ماژول برای برنامه‌های قابل حمل و خودکار در حوزه الکترونیک و اینترنت اشیاء طراحی شده است و در یک بسته کوچک به ابعاد 25.5 میلیمتر در 18 میلیمتر ساخته شده است. این ماژول دارای حافظه فلش داخلی، کوارتز 40 مگاهرتز و آنتن PCB است که ویژگی‌های RF خوبی را فراهم می‌کند.

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

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

1. ESP32 Dev Kit V1
2. محیط توسعه Arduino IDE

حساب ThingsBoard
ایجاد دستگاه در ThingsBoard
به منظور سهولت، ما دستگاه را به صورت دستی اضافه خواهیم کرد با استفاده از رابط کاربری (UI).

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

///img

نصب کتابخانه‌ها و ابزارهای مورد نیاز
برای نصب برد در محیط توسعه Arduino IDE: به قسمت File > Preferences بروید و آدرس زیر را به فیلد Additional Boards Manager URLs اضافه کنید:

https://dl.espressif.com/dl/package_esp32_index.json

//img

سپس به قسمت Tools > Board > Board Manager بروید و برد ESP32 از Espressif Systems را نصب کنید.

//img

بعد از تکمیل نصب، برد را از طریق منوی Board انتخاب کنید:
Tools > Board > ESP32 > ESP32 Dev Module.

همچنین، فراموش نکنید که پورت مربوط به دستگاه را مشخص کنید:

Tools > Port > /dev/ttyUSB0.

پورت بستگی به سیستم عامل شما دارد و ممکن است متفاوت باشد:

  • برای لینوکس / مک، آن /dev/ttyUSBX خواهد بود
  • برای ویندوز – COMX.

که X یک عدد است که توسط سیستم شما تخصیص داده شده است.

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

  • به بخش “Tools” بروید و بر روی “Manage libraries” کلیک کنید.
  • در جعبه جستجو، “ThingsBoard” را وارد کنید و برای کتابخانه پیدا شده بر روی دکمه “INSTALL” کلیک کنید.

//img

اتصال دستگاه به ThingsConnect

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

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

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

//img

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

#if defined(ESP8266)
  #include <ESP8266WiFi.h>
  #define THINGSBOARD_ENABLE_PROGMEM 0
#elif defined(ARDUINO_NANO_RP2040_CONNECT)
  #include <WiFiNINA_Generic.h>
#elif defined(ESP32) || defined(RASPBERRYPI_PICO) || defined(RASPBERRYPI_PICO_W)
  #include <WiFi.h>
  #include <WiFiClientSecure.h>
#endif

#define THINGSBOARD_ENABLE_PSRAM 0
#define THINGSBOARD_ENABLE_DYNAMIC 1

#ifndef LED_BUILTIN
#define LED_BUILTIN 99
#endif

#include <ThingsBoard.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 = 256U;

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

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

// 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.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 wl_status_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>();
      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() {
  // Initalize serial connection for debugging
  Serial.begin(115200);
  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 (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("bssid", WiFi.BSSIDstr().c_str());
    tb.sendAttributeString("localIp", WiFi.localIP().toString().c_str());
    tb.sendAttributeString("ssid", WiFi.SSID().c_str());
  }

  tb.loop();
}

در کد، با مقادیر SSID شبکه WiFi، رمزعبور و توکن دسترسی دستگاه 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

اگر نمی‌توانید کد را بارگذاری کنید و خطای “Property ‘upload.tool.serial’ is undefined” دریافت می‌کنید، می‌توانید اقدامات زیر را انجام دهید:

1. به بخش “Tools” بروید و “Programmer” را انتخاب کنید و “Esptool” را به عنوان برنامه‌نویس انتخاب کنید.
2. به بخش “Sketch” رفته و “Upload Using Programmer” را انتخاب کنید.

//img

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

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

برای افزودن داشبورد به ThingsBoard، باید مراحل زیر را طی کنیم:

1. از طریق منوی اصلی در سمت چپ صفحه به بخش “Dashboards” بروید.
2. روی دکمه “+” در گوشه سمت راست صفحه کلیک کنید و “Import dashboard” را انتخاب کنید.
3. فایل dashboard.json خود را انتخاب کنید و دکمه “import” را فشار دهید.
4. حالا می‌توانید داشبورد وارد شده را در جدول مشاهده کنید.

//img

 

عناوین هر بخش