Dashboards let humans see data. APIs let software talk to software — they enable mobile apps, automated scripts, and other IoT devices to read sensor data and send commands programmatically. This lesson covers REST API usage in both directions: reading data from the cloud and sending commands back to devices.
ANALOGY
A REST API is like a restaurant menu. Each item (endpoint) has a name, a price (authentication requirement), and a description of what you get. You order (send HTTP request), the kitchen prepares it (server processes), and the waiter brings your dish (JSON response). You don’t need to know how the kitchen works.
ThingsBoard REST API — Authentication
ThingsBoard REST API uses JWT (JSON Web Tokens) for authentication. You obtain a token by logging in with your username and password, then include it in all subsequent requests.
Getting a ThingsBoard JWT Token (curl)
# Get JWT token using your ThingsBoard credentials
curl -X POST https://thingsboard.cloud/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username": "your@email.com", "password": "yourPassword"}'
# Response: { "token": "eyJhbGciOi...", "refreshToken": "eyJ..." }
# All subsequent requests include the token in Authorization header:
# Authorization: Bearer eyJhbGciOi...
Reading Telemetry via REST API
ThingsBoard REST API — Read Telemetry (curl)
# Get latest telemetry values for a device
curl -X GET \
'https://thingsboard.cloud/api/plugins/telemetry/DEVICE/{deviceId}/values/timeseries' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN'
# Response:
# {
# "temperature": [{ "ts": 1700000000000, "value": "24.5" }],
# "humidity": [{ "ts": 1700000000000, "value": "68.2" }],
# "light": [{ "ts": 1700000000000, "value": "2340" }]
# }
# Get historical telemetry for last 1 hour:
curl -X GET \
'https://thingsboard.cloud/api/plugins/telemetry/DEVICE/{deviceId}/values/timeseries
?keys=temperature,humidity&startTs=1699996400000&endTs=1700000000000&limit=100' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN'
Sending Commands via RPC (Remote Procedure Call)
ThingsBoard supports server-side RPC: the cloud sends a command to a device, and the device can optionally respond. This is how dashboards control actuators remotely.
ThingsBoard REST API — Send RPC Command to Device
# One-way RPC (fire and forget — no response expected)
curl -X POST \
'https://thingsboard.cloud/api/plugins/rpc/oneway/{deviceId}' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-d '{"method": "setLedState", "params": {"state": true}}'
# Two-way RPC (device returns a response)
curl -X POST \
'https://thingsboard.cloud/api/plugins/rpc/twoway/{deviceId}' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-d '{"method": "getTemperature", "params": {}, "timeout": 5000}'
# Response (two-way): { "temperature": 24.5 }
ESP32 — Handling RPC Commands from ThingsBoard
C/C++ — ESP32 Subscribes to ThingsBoard RPC Topic and Acts on Commands
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
const char* RPC_SUBSCRIBE = "v1/devices/me/rpc/request/+";
const char* RPC_RESPONSE = "v1/devices/me/rpc/response/";
#define LED_PIN 2
#define RELAY_PIN 16
void onRPCRequest(char* topic, byte* payload, unsigned int length) {
// Extract request ID from topic: v1/devices/me/rpc/request/{id}
String topicStr = String(topic);
String requestId = topicStr.substring(topicStr.lastIndexOf('/') + 1);
StaticJsonDocument<200> req;
deserializeJson(req, payload, length);
String method = req["method"].as<String>();
Serial.print("RPC: method="); Serial.print(method);
if (method == "setLedState") {
bool state = req["params"]["state"];
digitalWrite(LED_PIN, state ? HIGH : LOW);
Serial.print(" state="); Serial.println(state);
// Send response back (for two-way RPC)
String responseTopic = String(RPC_RESPONSE) + requestId;
String response = "{\"result\": \"ok\", \"led\": " + String(state ? "true" : "false") + "}";
mqtt.publish(responseTopic.c_str(), response.c_str());
} else if (method == "setRelayState") {
bool state = req["params"]["state"];
digitalWrite(RELAY_PIN, state ? HIGH : LOW);
Serial.println(" relay=" + String(state ? "ON" : "OFF"));
} else if (method == "getTemperature") {
float temp = dht.readTemperature();
String responseTopic = String(RPC_RESPONSE) + requestId;
String response = "{\"temperature\": " + String(temp, 1) + "}";
mqtt.publish(responseTopic.c_str(), response.c_str());
}
}
// In setup(), after MQTT connect:
// mqtt.setCallback(onRPCRequest);
// mqtt.subscribe(RPC_SUBSCRIBE);
KEY
ThingsBoard RPC enables true bidirectional cloud-device control. From a dashboard you can add a Switch widget or Button widget that calls an RPC method. This is how dashboards control relays, servos, and displays on remote ESP32 devices in real time.
ThingsBoard Dashboard Control Widgets
| Widget type |
How it works |
IoT use case |
| Switch widget |
Toggle switch that calls setLedState (true/false) via RPC. Operator clicks to toggle; LED/relay responds instantly. |
Relay control, LED on/off, motor start/stop |
| Button widget |
Single-action button that sends an RPC command on click. E.g. ‘Open Valve’ sends {method: openValve, params: {}}. |
Valve control, door unlock, alarm acknowledge |
| Slider widget |
Slider that sends RPC with a numeric value. E.g. set fan speed 0–100%: {method: setFanSpeed, params: {speed: 75}}. |
Fan speed, LED brightness, servo angle |
| Round knob |
Rotary knob for value setting. Sends RPC with numeric value on release. |
Temperature setpoint, volume control |
ThingSpeak REST API — Python Example
Python — ThingSpeak REST API Read and Write
import requests
CHANNEL_ID = 'YOUR_CHANNEL_ID'
READ_KEY = 'YOUR_READ_API_KEY'
WRITE_KEY = 'YOUR_WRITE_API_KEY'
# --- READ last 10 temperature readings ---
url = f'https://api.thingspeak.com/channels/{CHANNEL_ID}/fields/1.json'
params = {'api_key': READ_KEY, 'results': 10}
data = requests.get(url, params=params).json()
print('Last 10 temperature readings:')
for entry in data['feeds']:
print(f" {entry['created_at']}: {entry['field1']} C")
# --- WRITE a sensor reading ---
r = requests.post('https://api.thingspeak.com/update', params={
'api_key': WRITE_KEY,
'field1': 24.5, # temperature
'field2': 68.2 # humidity
})
print(f'Write response: {r.text}') # Returns entry number or 0 on error
Building a Remote Monitoring Application
The following Python script demonstrates automated monitoring with email alerts using the ThingsBoard REST API:
Python — Automated ThingsBoard Monitor with Email Alert (server-side script)
import requests, time, smtplib
from email.mime.text import MIMEText
TB_HOST = 'https://thingsboard.cloud'
TB_EMAIL = 'your@email.com'
TB_PASSWORD= 'yourPassword'
DEVICE_ID = 'YOUR_DEVICE_ID'
ALERT_EMAIL= 'operator@nedu.rw'
TEMP_LIMIT = 35.0 # Alert if temperature exceeds this
def get_token():
r = requests.post(f'{TB_HOST}/api/auth/login',
json={'username': TB_EMAIL, 'password': TB_PASSWORD})
return r.json()['token']
def get_latest_telemetry(token):
headers = {'Authorization': f'Bearer {token}'}
r = requests.get(
f'{TB_HOST}/api/plugins/telemetry/DEVICE/{DEVICE_ID}/values/timeseries',
headers=headers)
data = r.json()
temp = float(data.get('temperature', [{}])[0].get('value', 0))
hum = float(data.get('humidity', [{}])[0].get('value', 0))
return temp, hum
def send_alert(temp):
msg = MIMEText(f'ALERT: Temperature {temp:.1f}C exceeds limit {TEMP_LIMIT}C!')
msg['Subject'] = 'IoT Temperature Alert — Action Required'
msg['From'] = 'iot-monitor@nedu.rw'
msg['To'] = ALERT_EMAIL
with smtplib.SMTP('smtp.gmail.com', 587) as server:
server.starttls()
server.login('iot-monitor@nedu.rw', 'smtpPassword')
server.send_message(msg)
print(f'Alert email sent: {temp:.1f}C')
token = get_token()
alert_sent = False
while True:
try:
temp, hum = get_latest_telemetry(token)
print(f'T: {temp:.1f}C H: {hum:.1f}%')
if temp > TEMP_LIMIT and not alert_sent:
send_alert(temp)
alert_sent = True
elif temp <= TEMP_LIMIT - 2: # hysteresis: clear alert at 33°C
alert_sent = False
except Exception as e:
print(f'Error: {e}')
token = get_token() # Refresh token on error
time.sleep(60) # Check every 60 seconds
TRY
Run this script on your laptop while your ESP32 is publishing to ThingsBoard. Cover the DHT22 with your hand to raise the temperature above 35°C. The script should detect the threshold breach and print the alert.
Security Best Practices for Cloud-Connected IoT
| Practice |
Implementation detail |
| Use HTTPS/MQTT-TLS always |
All API calls must use HTTPS (port 443). All MQTT connections must use TLS (port 8883). Never use plain HTTP or MQTT port 1883 for production deployments. |
| Rotate access tokens |
Change device access tokens periodically (quarterly for production). If a token is compromised, an attacker can publish false data to your cloud. |
| Minimal permission principle |
Each device should only have permission to publish to its own telemetry topics. It should not be able to read other devices’ data or access admin APIs. |
| Store credentials in NVS |
ESP32 NVS (Non-Volatile Storage) encrypts secrets at rest. Use Preferences library: prefs.putString("token", token). Never hardcode tokens in source code. |
| Monitor for anomalies |
Set up ThingsBoard alarms for impossible values: temperature < -50°C, RSSI < -100 dBm, impossible humidity. These can indicate sensor failure or data injection attacks. |
| Keep firmware updated |
Use OTA updates (from Unit 3) to push security patches. A device that hasn’t been updated in 2 years is a liability. |
Storing Credentials Securely on ESP32 (NVS)
C/C++ — Using ESP32 NVS for Secure Credential Storage
#include <Preferences.h>
Preferences prefs;
// WRITE credentials to NVS (run once during provisioning)
void storeCredentials() {
prefs.begin("iot-creds", false); // false = read/write mode
prefs.putString("wifi_ssid", "YourNetwork");
prefs.putString("wifi_pass", "YourPassword");
prefs.putString("tb_token", "YourThingsBoardToken");
prefs.end();
Serial.println("Credentials stored in NVS.");
}
// READ credentials from NVS at startup
void loadCredentials(String &ssid, String &pass, String &token) {
prefs.begin("iot-creds", true); // true = read-only mode
ssid = prefs.getString("wifi_ssid", "");
pass = prefs.getString("wifi_pass", "");
token = prefs.getString("tb_token", "");
prefs.end();
}
void setup() {
Serial.begin(115200);
String ssid, pass, token;
loadCredentials(ssid, pass, token);
if (ssid.length() == 0) {
Serial.println("No credentials found. Run provisioning.");
return;
}
WiFi.begin(ssid.c_str(), pass.c_str());
// ... rest of setup
}
Check Your Understanding — Lesson 6.3
?
1. Explain the difference between a ThingsBoard ‘one-way RPC’ and a ‘two-way RPC’. Give a specific IoT example where each type is appropriate.
?
2. A student hardcodes the ThingsBoard access token in their ESP32 code and pushes it to a public GitHub repository. What are the security risks and what are the two immediate actions they should take?
?
3. Write the ThingsBoard MQTT topics that an ESP32 must SUBSCRIBE to in order to receive RPC commands, and the topic structure it must PUBLISH to in order to send a response.