Stop the war

Stand with Ukraine flag

Support Ukraine

Try it now Pricing
Cloud
Community Edition Professional Edition Cloud Edge PE Edge IoT Gateway License Server Trendz Analytics Mobile Application PE Mobile Application MQTT Broker
How to connect Arduino Nano RP2040 Connect to ThingsBoard?
Getting Started Documentation Devices Library Guides API FAQ
On this page

How to connect Arduino Nano RP2040 Connect to ThingsBoard?

Introduction

Arduino Nano RP2040 Connect

The feature packed Arduino Nano RP2040 Connect brings the new Raspberry Pi RP2040 microcontroller to the Nano form factor.
Make the most of the dual core 32-bit Arm® Cortex®-M0+ to make Internet of Things projects with Bluetooth and WiFi connectivity thanks to the U-blox Nina W102 module.
Dive into real-world projects with the onboard accelerometer, gyroscope, RGB LED and microphone.
Develop robust embedded AI solutions with minimal effort using the Arduino Nano RP2040 Connect.

In this guide, we will learn how to create device on Thingsboard, install required libraries and tools.
After this we will modify our code and upload it to the device, and check the results of our coding and check data on ThingsBoard using imported dashboard. Our device will synchronize with ThingsBoard using client and shared attributes requests functionality.
Of course, we will control our device using provided functionality like shared attributes or RPC requests.

Prerequisites

To continue with this guide we will need the following:

Create device on ThingsBoard

For simplicity, we will provide the device manually using the UI.

  • Log in to your ThingsBoard instance and open the Devices page.

  • By default, you navigate to the device group “All”. Click on the “+” icon in the top right corner of the table and then select “Add new device”.

  • Input device name. For example, “My Device”. No other changes are required at this time. Click “Add” to add the device.

  • Your first device has been added.

Install required libraries and tools

Install the board for Arduino IDE:

  • Go to Tools > Board > Board Manager and install the Arduino Mbed OS RP2040 Boards by Arduino board.

After the installation is complete, select the board by Board menu:
Tools > Board > Arduino Mbed OS Nano Boards > Arduino Nano RP2040 Connect.

Connect the device to computer using USB cable and select the port for the device:

Tools > Port > /dev/ttyUSB0.

Port depends on operation system and may be different:

  • for Linux it will be /dev/ttyUSBX
  • for MacOS it will be usb.serialX.. or usb.modemX..
  • for Windows - COMX.

To install ThingsBoard Arduino SDK - we will need to do the following steps:

  • Go to “Tools” tab and click on “Manage libraries”.

  • Put “ThingsBoard” into the search box and press “INSTALL” button for the found library.

doc warn icon

All provided code examples require ThingsBoard Library version 0.10.2.

Also, for boards, based on RP2040 chip we should install the “WiFiNINA” library.

  • Put into library search field “WiFiNINA” and install the library - “WiFiNINA by Arduino

At this point, we have installed all required libraries and tools.

Connect device to ThingsBoard

To connect your device, you’ll first need to get its credentials. While ThingsBoard supports a variety of device credentials, for this guide, we will use the default auto-generated credentials, which is an access token.

  • Click on the device row in the table to open device details.

  • Click “Copy access token”. The token will be copied to your clipboard. Please save it in a safe place.

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/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[] = "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();
}

Doc info icon

Don’t forget to replace placeholders with your real WiFi network SSID, password, ThingsBoard device access token.

Necessary variables for connection:

Variable name Default value Description
WIFI_SSID YOUR_WIFI_SSID Your WiFi network name.
WIFI_PASSWORD YOUR_WIFI_PASSWORD Your WiFi network password.
TOKEN YOUR_DEVICE_ACCESS_TOKEN Access token from device. Obtaining process described in #connect-device-to-thingsboard
THINGSBOARD_SERVER thingsboard.cloud Your ThingsBoard host or ip address.
THINGSBOARD_PORT 1883U ThingsBoard server MQTT port. Can be default for this guide.
MAX_MESSAGE_SIZE 512U Maximal size of MQTT messages. Can be default for this guide.
SERIAL_DEBUG_BAUD 1883U Baud rate for serial port. Can be default for this guide.
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[] = "thingsboard.cloud";
constexpr uint16_t THINGSBOARD_PORT = 1883U;

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

...

Send data part in code (By default the example sends random value for temperature key and some WiFi information):

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());
...

Then upload the code to the device by pressing Upload button or keyboard combination Ctrl+U.

Check data on ThingsBoard

ThingsBoard provides the ability to create and customize interactive visualizations (dashboards) for monitoring and managing data and devices.
Through ThingsBoard dashboards, you can efficiently manage and monitor your IoT devices and data. So, we will create the dashboard, for our device.

To add the dashboard to ThingsBoard, we need to import it. To import a dashboard, follow these steps:

  • Navigate to the “Dashboards” page. By default, you navigate to the dashboard group “All”. Click on the “+” icon in the top right corner. Select “Import dashboard”.

  • In the dashboard import window, upload the JSON file and click “Import” button.

  • Dashboard has been imported.

The Check and control device data dashboard structure:

  • To check the data from our device we need to open the imported dashboard by clicking on it in the table.

  • The view of checking data and controlling our device dashboard.

  • Received attributes from device.

  • Device information from the ThingsBoard server.

  • Widget to see the history of LED mode changes.

  • Widget to see the history of our emulated temperature.

Synchronize device state using client and shared attribute requests

In order to get the state of the device from ThingsBoard during booting we have functionality to do this in the code.

Below are the relevant parts of the code example:

  • Attribute callbacks:
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
...
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);
...

We have two callbacks:

  • Shared Attributes Callback: This callback is specific to shared attributes. Its primary function is to receive a response containing the blinking interval, which determines the appropriate blinking period.;
  • Client Attributes Callback: This callback is specific to client attributes. It receives information regarding the mode and state of the LED. Once this data is received, the system saves and sets these parameters.

This functionality allows us to keep the actual state after rebooting.

  • Attribute requests:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    ...
      // 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;
      }
    ...
    

    In order for our callbacks to receive the data, we have to send a request to ThingsBoard.

Control device using shared attributes

Also we can change the period of the blinking using shared attribute update functionality.

  • To change period of the blinking we just need to change the value on our dashboard.

  • After applying by pressing check mark you will see a confirmation message.

In order to change state when blinking is disabled - we can use the switch in the same widget:

  • It can be done only when the blinking mode is disabled.

To reach this, we have a variable “blinkingInterval” used in the following parts of the code:

  • Callback for shared attributes update:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...

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);
...
  • Subscribing for shared attributes update:
1
2
3
4
5
6
...
    if (!tb.Shared_Attributes_Request(attribute_shared_request_callback)) {
      Serial.println("Failed to request for shared attributes");
      return;
    }
...
  • Part of code to blink:
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);
    }
  }
...

Such as the board has included RGB LED we can control it color.

  • You can update the color of the led on the board, using the widget on ThingsBoard dashboard.

To control the led we change “ledColor” shared attribute. It contains RGB values in the following string format “R,G,B”.

The following part of the code used to parse incoming values and save them:

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();
}
...

To set the color of the LED we use the following function in the code:

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);
  }
}
...

You can change the logic to reach your goals and add processing for your attributes.

Control device using RPC

You can manually change state of the LED and change mode between continuous lightning and blinking. To do this, you can use the following parts of our dashboard:

  • Change LED state using switch widget to continuous lightning.

  • Change LED state using round switch widget to blinking mode.

Please note that you can change the LED state only if blinking mode is disabled.

In the code example we have functionality to handle RPC commands.
To get ability to control the device we have used the following parts of the code:

  • Callback for RPC requests:
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
...

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

...
  • Subscribing for RPC requests:
1
2
3
4
5
6
...
    if (!tb.RPC_Subscribe(callbacks.cbegin(), callbacks.cend())) {
      Serial.println("Failed to subscribe for RPC");
      return;
    }
...

You can change the code to reach your goals and add processing for your RPC commands.

Conclusion

With the knowledge outlined in this guide, you can easily connect your Arduino Nano RP2040 Connect and send data to ThingsBoard.

Explore the platform documentation to learn more about key concepts and features. For example, configure alarm rules or dashboards.