产品定价 立即试用
目录
如何将 Arduino Nano RP2040 Connect 连接至 ThingsBoard?

概述

Arduino Nano RP2040 Connect将树莓派RP2040微控制器带入Nano尺寸,功能丰富。
借助U-blox Nina W102模块提供的Bluetooth与Wi-Fi连接,可利用双核32位Arm® Cortex®-M0+ 开发物联网项目。
板载加速度计、陀螺仪、RGB LED和麦克风,可直接投入实际项目。
使用Arduino Nano RP2040 Connect可轻松开发嵌入式AI应用。

本指南将介绍如何在ThingsBoard上创建设备安装所需库与工具
随后将修改代码并上传到设备, 并查看运行结果及通过导入的仪表板在ThingsBoard上查看数据。 设备将借助客户端与共享属性请求功能与ThingsBoard保持同步。
同时,我们将使用共享属性RPC 请求控制设备。

前置条件

继续本指南前,需具备:

在ThingsBoard中创建设备

为简化流程,我们将在界面中手动创建设备。

  • 登录ThingsBoard实例并进入 实体 > 设备 页面。

  • 默认将进入设备组“全部”。点击右上角 ”+” 按钮并选择 添加新设备

  • 输入设备名称,例如 “My Device”。其他字段可保持默认,点击 添加 创建设备。

  • 首个设备已添加完成。

安装所需库和工具

为Arduino IDE安装开发板:

  • 进入 工具 > 开发板 > 开发板管理器 并安装 Arduino Mbed OS RP2040 Boards by Arduino 开发板。

安装完成后,通过 工具 > 开发板 > Arduino Mbed OS Nano Boards > Arduino Nano RP2040 Connect 选择开发板。

用USB线连接设备与电脑,并在 工具 > 端口 > /dev/ttyUSB0 中选择设备端口。

端口随操作系统不同而不同:

  • Linux下为 /dev/ttyUSBX
  • MacOS下为 usb.serialX.. 或 usb.modemX..
  • Windows下为 COMX。

安装ThingsBoard Arduino SDK需执行以下步骤:

  • 进入 工具 选项卡,点击 管理库

  • 在搜索框中输入 ThingsBoard 并点击找到的库的 安装 按钮。

文档警告图标

所有示例代码均需ThingsBoard库版本 0.10.2.

基于RP2040芯片的开发板还需安装 “WiFiNINA” 库。

  • 在库搜索框中输入 WiFiNINA 并安装 WiFiNINA by Arduino

至此已安装全部所需库与工具。

连接设备到ThingsBoard

连接设备前,需先获取其凭证。
ThingsBoard 支持多种设备凭证类型,本指南使用默认自动生成的访问令牌(access token)。

  • 点击设备列表中的行以打开设备详情。

  • 点击“复制访问令牌”。令牌将复制到剪贴板,请妥善保存。

Now it’s time to program the board to connect to ThingsBoard.
To do this, you can use the code below. It contains all required functionality for this guide.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
#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/paas/eu/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[] = "eu.thingsboard.cloud";
// 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.sendAttributeData("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.sendTelemetryData(LED_MODE_ATTR, ledMode);
    tb.sendTelemetryData(LED_STATE_ATTR, ledState);
    tb.sendAttributeData(LED_MODE_ATTR, ledMode);
    tb.sendAttributeData(LED_STATE_ATTR, ledState);
  }

  if (ledMode == 1 && millis() - previousStateChange > blinkingInterval) {
    previousStateChange = millis();
    ledState = !ledState;
    setLedColor();
    tb.sendTelemetryData(LED_STATE_ATTR, ledState);
    tb.sendAttributeData(LED_STATE_ATTR, ledState);
  }

  // Sending telemetry every telemetrySendInterval time
  if (millis() - previousDataSend > telemetrySendInterval) {
    previousDataSend = millis();
    tb.sendTelemetryData("temperature", random(10, 20));
    tb.sendAttributeData("rssi", WiFi.RSSI());
    tb.sendAttributeData("ssid", WIFI_SSID);
    tb.sendAttributeData("bssid", getBSSID());
    tb.sendAttributeData("localIp", String(String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3])).c_str());
  }

  tb.loop();
}

文档信息图标

请勿忘记将占位符替换为您的真实Wi-Fi网络SSID、密码及ThingsBoard设备访问令牌。

连接所需变量:

变量名 默认值 说明
WIFI_SSID YOUR_WIFI_SSID 您的Wi-Fi网络名称。
WIFI_PASSWORD YOUR_WIFI_PASSWORD 您的Wi-Fi网络密码。
TOKEN YOUR_DEVICE_ACCESS_TOKEN 设备访问令牌。获取方式参见 #connect-device-to-thingsboard
THINGSBOARD_SERVER eu.thingsboard.cloud 您的ThingsBoard主机或IP地址。
THINGSBOARD_PORT 1883U ThingsBoard服务MQTT端口。本指南可使用默认值。
MAX_MESSAGE_SIZE 512U MQTT消息最大尺寸。本指南可使用默认值。
SERIAL_DEBUG_BAUD 1883U 串口波特率。本指南可使用默认值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...

constexpr char WIFI_SSID[] = "YOUR_WIFI_SSID";
constexpr char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";

constexpr char TOKEN[] = "YOUR_ACCESS_TOKEN";

constexpr char THINGSBOARD_SERVER[] = "eu.thingsboard.cloud";
constexpr uint16_t THINGSBOARD_PORT = 1883U;

constexpr uint32_t MAX_MESSAGE_SIZE = 512U;
constexpr uint32_t SERIAL_DEBUG_BAUD = 115200U;

...

代码中的数据发送部分(默认示例发送 temperature 的随机值及部分Wi-Fi信息):

1
2
3
4
5
6
7
...
    tb.sendTelemetryData("temperature", random(10, 20));
    tb.sendAttributeData("rssi", WiFi.RSSI());
    tb.sendAttributeData("ssid", WIFI_SSID);
    tb.sendAttributeData("bssid", getBSSID());
    tb.sendAttributeData("localIp", String(String(WiFi.localIP()[0]) + "." + String(WiFi.localIP()[1]) + "." + String(WiFi.localIP()[2]) + "." + String(WiFi.localIP()[3])).c_str());
...

然后按上传按钮或Ctrl+U将代码上传到设备。

在ThingsBoard上查看数据

ThingsBoard支持创建和自定义用于监控与管理数据、设备的交互式可视化(仪表板)。
通过ThingsBoard仪表板,您可以高效管理与监控IoT设备及数据。下面为我们的设备创建仪表板。

将仪表板添加到ThingsBoard需要导入。请按以下步骤操作:

  • 进入 仪表板 页面。默认进入仪表板组 “All”。点击右上角 + 图标,选择 导入仪表板

  • 在仪表板导入窗口中上传JSON文件并点击 导入 按钮。

  • 仪表板已导入。

检查并控制设备数据仪表板结构:

  • 点击表格中的仪表板以打开导入的仪表板,查看设备数据。

  • 检查数据和控制设备的仪表板视图。

  • 从设备接收的属性。

  • ThingsBoard服务器上的设备信息。

  • 查看LED模式变化历史的部件。

  • 查看模拟温度历史的部件。

使用客户端和共享属性请求同步设备状态

为在启动时从ThingsBoard获取设备状态,代码中实现了相应功能

以下为示例代码的相关部分:

  • 引入模块以使用API功能:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    ...
    #include <AttributeRequest.h>
    ...
    Attribute_Request<2U, MAX_ATTRIBUTES> attr_request;
    ...
    const std::array<IAPI_Implementation*, ...> apis = {
      ...
      &attr_request,
      ...
    };
    ...
    

    需在代码中定义要使用的API。

  • 属性回调:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
...
void processSharedAttributes(const JsonObjectConst &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 JsonObjectConst &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;
    }
  }
}
...
// Attribute request did not receive a response in the expected amount of microseconds 
void requestTimedOut() {
  Serial.printf("Attribute request timed out did not receive a response in (%llu) microseconds. Ensure client is connected to the MQTT broker and that the keys actually exist on the target device\n", REQUEST_TIMEOUT_MICROSECONDS);
}
...
const Attribute_Request_Callback<MAX_ATTRIBUTES> attribute_shared_request_callback(&processSharedAttributes, REQUEST_TIMEOUT_MICROSECONDS, &requestTimedOut, SHARED_ATTRIBUTES_LIST);
const Attribute_Request_Callback<MAX_ATTRIBUTES> attribute_client_request_callback(&processClientAttributes, REQUEST_TIMEOUT_MICROSECONDS, &requestTimedOut, CLIENT_ATTRIBUTES_LIST);
...

共有三个回调:

  • 共享属性回调:专用于共享属性,主要接收包含闪烁间隔的响应,以确定合适的闪烁周期;
  • 客户端属性回调:专用于客户端属性,接收LED模式与状态信息,收到后保存并应用这些参数;
  • 请求超时回调:在属性数据请求超时时触发,用于处理超时。

此功能使设备在重启后能保持实际状态。

  • 属性请求:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    ...
      // Request current states of shared attributes
      if (!attr_request.Shared_Attributes_Request(attribute_shared_request_callback)) {
        Serial.println("Failed to request for shared attributes");
        return;
      }
    
      // Request current states of client attributes
      if (!attr_request.Client_Attributes_Request(attribute_client_request_callback)) {
        Serial.println("Failed to request for client attributes");
        return;
      }
    ...
    

    为使回调能接收数据,需向ThingsBoard发送请求。

使用共享属性控制设备

还可通过共享属性更新功能修改闪烁周期。

  • 修改闪烁周期只需在仪表板上更改数值即可。

  • 按勾选图标应用后,将显示确认消息。

在关闭闪烁时改变状态,可使用同一部件中的开关:

  • 仅当关闭闪烁模式时方可操作。

实现该功能使用变量 “blinkingInterval”,出现在以下代码中:

  • Connecting modules to use API functionality:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    ...
    #include <AttributeRequest.h>
    ...
    Attribute_Request<2U, MAX_ATTRIBUTES> attr_request;
    ...
    const std::array<IAPI_Implementation*, ...> apis = {
      ...
      &shared_update
      ...
    };
    ...
    

    使用属性请求功能需引入相关模块并将其作为所用API的一部分。

  • 共享属性更新回调:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
...

void processSharedAttributes(const JsonObjectConst &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;
}

...
// Attribute request did not receive a response in the expected amount of microseconds 
void requestTimedOut() {
  Serial.printf("Attribute request timed out did not receive a response in (%llu) microseconds. Ensure client is connected to the MQTT broker and that the keys actually exist on the target device\n", REQUEST_TIMEOUT_MICROSECONDS);
}
...
const Attribute_Request_Callback<MAX_ATTRIBUTES> attribute_shared_request_callback(&processSharedAttributes, REQUEST_TIMEOUT_MICROSECONDS, &requestTimedOut, SHARED_ATTRIBUTES_LIST);
...
  • 订阅共享属性更新:
1
2
3
4
5
6
...
    if (!shared_update.Shared_Attributes_Request(attribute_shared_request_callback)) {
      Serial.println("Failed to request for shared attributes");
      return;
    }
...
  • 闪烁相关代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...

  if (ledMode == 1 && millis() - previousStateChange > blinkingInterval) {
    previousStateChange = millis();
    ledState = !ledState;
    digitalWrite(LED_BUILTIN, ledState);
    tb.sendTelemetryData(LED_STATE_ATTR, ledState);
    tb.sendAttributeData(LED_STATE_ATTR, ledState);
    if (LED_BUILTIN == 99) {
      Serial.print("LED state changed to: ");
      Serial.println(ledState);
    }
  }
...

开发板集成RGB LED,可控制其颜色。

  • 可通过ThingsBoard仪表板上的部件更新开发板LED颜色。

通过修改共享属性 “ledColor” 控制LED,其值为 “R,G,B” 格式的RGB字符串。

以下代码用于解析接收到的值并保存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
...
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颜色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
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设为闪烁模式。

注意:仅在关闭闪烁模式时可更改LED状态。

示例代码中实现了 RPC 命令 处理。
实现设备控制使用了以下代码部分:

  • Connecting modules to use API functionality:
1
2
3
4
5
6
7
8
9
10
11
12
...
#include <Server_Side_RPC.h>
...
Server_Side_RPC<..., ...> rpc;
...

const std::array<IAPI_Implementation*, ...> apis = {
    ...
    &rpc,
    ...
    }
...

使用RPC需引入相关模块并将其作为所用API的一部分。

  • RPC请求回调:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
...

void processSetLedMode(const JsonVariantConst &data, JsonDocument &response) {
  Serial.println("Received the set led state RPC method");

  // Process data
  int new_mode = data;

  Serial.print("Mode to change: ");
  Serial.println(new_mode);
  StaticJsonDocument<1> response_doc;

  if (new_mode != 0 && new_mode != 1) {
    response_doc["error"] = "Unknown mode!";
    response.set(response_doc);
    return;
  }

  ledMode = new_mode;

  attributesChanged = true;


  response_doc["newMode"] = (int)ledMode;
  // Returning current mode
  response.set(response_doc);
}

...

const std::array<RPC_Callback, 1U> callbacks = {
  RPC_Callback{ "setLedMode", processSetLedMode }
};

...
  • 订阅RPC请求:
1
2
3
4
5
6
...
    if (!rpc.RPC_Subscribe(callbacks.cbegin(), callbacks.cend())) {
      Serial.println("Failed to subscribe for RPC");
      return;
    }
...

可修改代码以实现您的目标,并添加自定义RPC命令处理。

总结

现在您可以轻松将Arduino Nano RP2040 Connect连接到ThingsBoard并开始发送数据。

进一步了解可查阅ThingsBoard文档, 学习创建仪表板可视化遥测、 配置告警规则实时监控设备行为等核心功能。

发现即插即用硬件,助力您的解决方案
合作伙伴图标