Graph Display with ThingSpeak
spot-thingspeak
, which acts as a Wi-Fi client and uses MathWorks’ service ThingSpeak to graph temperature and humidity data.This article uses third-party open source software.
We are unable to provide detailed instructions for third-party software. In addition, we are not responsible for any damages incurred as a result of using third-party software.
Getting the Source Code
You can obtain it from the GitHub repository monowireless/spot-thingspeak.
System Overview
The spot-thingspeak sample receives data from TWELITE ARIA and sends it as an HTTP GET request to the ThingSpeak server, making it possible to display the data as a graph.

Display Example
Basically, this sample is a modification of spot-httpbin. Please refer to its explanation as well.
The main differences are that NTP-related code is removed and the contents of the request have been changed.
What You Need for Development
-
Wireless LAN Gateway TWELITE SPOT
- USB-C cable for power supply
- USB AC adapter (must supply at least 1A)
-
Magnetic, Temperature, and Humidity Sensor Wireless Tag TWELITE ARIA or other child device (If you do not have one, please purchase 👉 List of retailers)
- Power supply such as CR2032 coin battery
-
USB Adapter TWELITE R3 (If you do not have one, please purchase 👉 List of retailers)
- USB-C cable for communication
- 💻 Development computer
Environment Setup
ThingSpeak Setup
- Access ThingSpeak’s website and create a MathWorks account.
- Create a “Channel” and set it as shown below.

Example Channel Settings
- On the “API Keys” tab, make a note of the 16-character “Write API key”.
Installing the IDE and Toolchain
See How to set up the development environment with Arduino IDE 1.x.
Obtaining the Project Files
- Download the Zip file from GitHub (monowireless/spot-thingspeak)
- Extract the Zip file and rename the folder from
spot-thingspeak-main
tospot-thingspeak
- Place the
spot-thingspeak
folder in your Arduino sketchbook location (specified in Arduino IDE preferences, e.g.,C:\Users\foo\Documents\Arduino
)
Changing User Settings
Open config.h
from the tab at the top of the Arduino IDE and set the Wi-Fi SSID and password. WPA2-PSK networks are assumed.
Also, set the 16-character “Write API key” you noted earlier. If necessary, rewrite the root certificate as well.
Writing the Project Files
See How to upload sketches to ESP32.
Setting up and Starting TWELITE ARIA
- Change the settings for TWELITE ARIA and set the Transmission Interval in TWELITE ARIA Mode to 30 seconds or more (e.g., 60 seconds) to avoid overloading the server.
- Insert a CR2032 battery and start TWELITE ARIA.
Sketch
Explanation of the Arduino sketch spot-thingspeak.ino
and config.h
.
Including Libraries
Official Arduino and ESP32 Libraries
Lines 5-7 include the official Arduino and ESP32 libraries.
#include <Arduino.h>
#include <WiFiClientSecure.h>
#include <WiFi.h>
Header File | Description | Notes |
---|---|---|
Arduino.h | Basic Arduino library | Sometimes can be omitted, but included just in case |
WiFiClientSecure.h | SSL communication on ESP32 | |
WiFi.h | Handles Wi-Fi | Sometimes can be omitted, but included just in case |
MWings Library
Line 13 includes the MWings library.
#include <MWings.h>
Definition of User Settings
Line 16 includes config.h
.
#include "config.h"
config.h
. Please modify these settings before running.Definition of Data Types
Lines 19-26 define a struct type to store data received from the child device.
struct DataFromAria {
uint32_t serialId;
uint8_t logicalId;
uint16_t supplyVoltage;
uint8_t linkQuality;
int16_t temp100x;
uint16_t humid100x;
};
Name | Description |
---|---|
serialId | Serial ID |
logicalId | Logical Device ID |
supplyVoltage | Supply Voltage |
linkQuality | LQI |
temp100x | Temperature multiplied by 100 |
humid100x | Humidity multiplied by 100 |
Here, TWELITE ARIA is used.
config.h
Definition of Reboot Interval
Line 4 of config.h
specifies the reboot interval for the ESP32.
const uint32_t REBOOT_INTERVAL = 21600; // seconds
Here, 21600 seconds = 6 hours.
For long-term operation, memory leaks may accumulate and cause malfunctions.
Therefore, periodic rebooting is performed just like a Wi-Fi router.
Definition of TWELITE Settings
Lines 7-8 of config.h
define the settings applied to the TWELITE parent device mounted on TWELITE SPOT.
const uint8_t TWE_CH = 18;
const uint32_t TWE_APPID = 0x67720102;
Name | Description |
---|---|
TWE_CH | TWELITE frequency channel |
TWE_APPID | TWELITE application ID |
Definition of Wi-Fi Settings
Lines 11-12 of config.h
define the Wi-Fi settings applied to the ESP32 mounted on TWELITE SPOT.
const char* WIFI_SSID = "YOUR SSID";
const char* WIFI_PASSWORD = "YOUR PASSWORD";
Name | Description |
---|---|
WIFI_SSID | SSID of the network to connect to |
WIFI_PASSWORD | Password of the network to connect to |
ThingSpeak Write API key
Line 15 of config.h
defines the “Write API key” required to add data to a “Field” in a ThingSpeak “Channel”.
Root Certificate
Lines 18-41 of config.h
contain the root certificate obtained from api.thingspeak.com
using Google Chrome. Rewrite as necessary.
const char *CA_CERT = R"(
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
)";
Host Settings
Lines 43-44 of config.h
define the host settings.
const char *SERVER_HOST = "api.thingspeak.com";
const uint16_t SERVER_PORT = 443;
Name | Description |
---|---|
SERVER_HOST | Server host name |
SERVER_PORT | Server port number |
https://
in SERVER_HOST
. This will cause a DNS Failed / with error '-54'
error.Definition of Various Constants
From line 46 in config.h
, various constants are defined.
const int QUERIES_MAX_LENGTH = 128; // bytes (without \0)
const int32_t CONNECT_TIMEOUT = 10; // seconds
const uint32_t RECONNECT_MIN_INTERVAL = 5; // seconds
// According to thingspeck free limitations, SEND_MIN_INTERVAL is set for 20s.
const uint32_t SEND_MIN_INTERVAL = 20; // seconds
const uint32_t REQUEST_TIMEOUT = 10; // seconds
Name | Description |
---|---|
QUERIES_MAX_LENGTH | Maximum length of the query string (excluding null terminator) |
CONNECT_TIMEOUT | Timeout when connecting to the server |
RECONNECT_MIN_INTERVAL | Minimum interval for reconnecting to Wi-Fi access point |
SEND_MIN_INTERVAL | Minimum interval between requests |
REQUEST_TIMEOUT | Timeout from request to response |
SEND_MIN_INTERVAL
is set to 20 seconds.
This is due to API call limitations of ThingSpeak.
If SEND_MIN_INTERVAL
is too short, the server may be burdened if packets are received continuously.
Be sure to allow a sufficient interval.
Definition of Pin Numbers
Lines 29-34 of spot-thingspeak.ino
define the pin numbers.
static const int RST_PIN = 5;
static const int PRG_PIN = 4;
static const int LED_PIN = 18;
static const int8_t RX1_PIN = 16;
static const int8_t TX1_PIN = 17;
Name | Description |
---|---|
RST_PIN | Pin number connected to TWELITE’s RST pin |
PRG_PIN | Pin number connected to TWELITE’s PRG pin |
LED_PIN | Pin number connected to the ESP32 LED on the board |
RX1_PIN | Pin number connected to TWELITE’s RX1 pin |
TX1_PIN | Pin number connected to TWELITE’s TX1 pin |
Declaration of Global Objects
Line 37 declares a global object.
static WiFiClientSecure client;
Name | Description |
---|---|
client | Interface for HTTPS communication |
Declaration of Global Variables
Lines 40-41 declare global variables.
static DataFromAria LatestDataFromAria;
static bool IsThereNewDataFromAria;
Name | Description |
---|---|
LatestDataFromAria | Latest data received from TWELITE ARIA |
IsThereNewDataFromAria | Flag indicating new data received from TWELITE ARIA |
Declaration of Function Prototypes
Lines 44-54 declare function prototypes.
void anotherLoopForTWELITE();
Name | Description |
---|---|
anotherLoopForTWELITE | Loop function for processing TWELITE data |
xTaskCreatePinnedToCore()
provided by the ESP32 Arduino Core is used to register this as a separate task.
void initTWELITE();
void initWiFi();
Name | Description |
---|---|
initTWELITE | TWELITE initialization function |
initWiFi | Wi-Fi initialization function |
void onAppAriaPacket(const ParsedAppAriaPacket& packet);
Name | Description |
---|---|
onAppAriaPacket | Callback function when data is received from TWELITE ARIA |
void sendAriaData(const DataFromAria& data)
Name | Description |
---|---|
sendAriaData | Function to send TWELITE ARIA data via HTTP GET request |
setup()
Lines 57-74 perform overall initialization.
void setup() {
Serial.begin(115200);
initTWELITE();
initWiFi();
// Attach another loop function for TWELITE
// Note: Core 0 is also used for the WiFi task, which priority is 19 (ESP_TASKD_EVENT_PRIO - 1)
xTaskCreatePinnedToCore(
[](void *params) {
while (true) {
anotherLoopForTWELITE();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
"Task for anotherLoopForTWELITE()", 8192, nullptr, 18, nullptr,
0); // Priority is 18 (lower than WiFi)
}
xTaskCreatePinnedToCore()
is used to register a task separate from loop()
.
The following part is an anonymous function without a capture. This avoids unnecessary pollution of the global namespace.
[](void *params) {
while (true) {
anotherLoopForTWELITE();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
vTaskDelay()
is inserted to allow the watchdog timer to intervene.loop()
Lines 77-99 are the main loop process.
It handles HTTP requests, Wi-Fi reconnection if disconnected, and periodic resets.
void loop() {
static uint32_t lastTimeReconnected = 0;
if (WiFi.status() == WL_CONNECTED) {
// Regular operations
// Check for new data
if (IsThereNewDataFromAria) {
IsThereNewDataFromAria = false; // Clear first; data is updated on another thread
DataFromAria data = LatestDataFromAria; // Now, the buffer is open for incoming data
sendAriaData(data);
}
} else if (millis() - lastTimeReconnected > RECONNECT_MIN_INTERVAL * 1000) {
// Lost connection, reconnect periodically
Serial.println("Disconnected. Reconnecting to WiFi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
lastTimeReconnected = millis();
}
// Reboot every x interval
if (millis() > REBOOT_INTERVAL * 1000) {
Serial.println("Rebooting...");
ESP.restart();
}
}
anotherLoopForTWELITE()
Lines 102-104 are the loop process for TWELITE.
To sequentially receive and interpret data, this is handled in a separate task from the blocking loop()
.
void anotherLoopForTWELITE() {
Twelite.update();
}
initTWELITE()
Lines 107-114 handle initialization for TWELITE.
The TWELITE mounted on TWELITE SPOT is started with the specified settings, and a callback function is registered for packet reception.
void initTWELITE() {
Serial2.begin(115200);
if (Twelite.begin(Serial2, LED_PIN, RST_PIN, PRG_PIN, TWE_CHANNEL, TWE_APP_ID)) {
Serial.println("Started TWELITE.");
}
// Attach event handlers to process packets
Twelite.on(onAppAriaPacket);
}
See the following references as well:
Twelite.begin()
mwings::MWings class | MWings API ReferenceTwelite.on()
mwings::MWings class | MWings API Reference
initWiFi()
Lines 117-144 handle Wi-Fi initialization.
If connection fails, it will retry every 5 seconds.
void initWiFi() {
Serial.print("\nConnecting to the WiFi network ");
Serial.print(WIFI_SSID);
Serial.println("...");
// Begin
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
// Wait for connection
Serial.print("Connecting.");
while (WiFi.status() != WL_CONNECTED) {
static int count = 0;
Serial.print('.');
delay(500);
// Retry every 5 seconds
if (count++ % 10 == 0) {
WiFi.disconnect();
WiFi.reconnect();
Serial.print('!');
}
}
Serial.println("\nConnected!");
// Set Root CA certificate
client.setCACert(CA_CERT);
}
onAppAriaPacket()
Lines 147-157 describe the processing when data is received from TWELITE ARIA.
Here, HTTP transmission is not performed; instead, the data is set to a global variable.
The data set in the global variable will be processed by sendAriaData()
in a separate task.
void onAppAriaPacket(const ParsedAppAriaPacket& packet)
{
// Store data
LatestDataFromAria.serialId = packet.u32SourceSerialId;
LatestDataFromAria.logicalId = packet.u8SourceLogicalId;
LatestDataFromAria.supplyVoltage = packet.u16SupplyVoltage;
LatestDataFromAria.linkQuality = packet.u8Lqi;
LatestDataFromAria.temp100x = packet.i16Temp100x;
LatestDataFromAria.humid100x = packet.u16Humid100x;
IsThereNewDataFromAria = true;
}
sendAriaData()
Lines 160-220 define a function that sets TWELITE ARIA data in the query string of an HTTP GET request and sends it.
To prevent excessive load on the server, sending is skipped if packets arrive too frequently.
void sendAriaData(const DataFromAria& data)
{
static uint32_t lastTimeRequested = 0;
if (millis() - lastTimeRequested > SEND_MIN_INTERVAL * 1000 or lastTimeRequested == 0) {
Serial.println("Connecting to the server...");
if (not client.connect(SERVER_HOST, SERVER_PORT, CONNECT_TIMEOUT * 1000)) {
Serial.println("Connection failed!");
} else {
Serial.println("Connected to the server!");
// Make a query string for the Channel on the ThingSpeak
char queries[QUERIES_MAX_LENGTH+1];
snprintf(queries, sizeof(queries),
"api_key=%s&field1=%s&field2=%s&field3=%s&field4=%d",
// Write API key for the Channel
WRITE_API_KEY,
// Field 1: Temperature
String(data.temp100x / 100.0f, 2),
// Field 2: Humidity
String(data.humid100x / 100.0f, 2),
// Field 3: Supply Voltage
String(data.supplyVoltage / 1000.0f, 2),
// Field 4: Link Quality
data.linkQuality);
// Send a request
client.println(String("GET https://") +
SERVER_HOST +
String("/update?") +
queries +
String(" HTTP/1.1"));
client.println("Accept: */*");
client.println(String("Host: ") + SERVER_HOST);
client.println("Connection: close");
client.println();
uint32_t timeSentRequest = millis();
// Handle a response
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
Serial.print("Index (if succeeded): ");
break;
}
if (millis() - timeSentRequest > REQUEST_TIMEOUT * 1000) {
Serial.println("Request was timed out");
break;
}
}
while (client.available()) {
char c = client.read();
Serial.write(c);
}
client.stop();
Serial.println("");
}
lastTimeRequested = millis();
} else {
Serial.println("Requests are too frequently; skip.");
}
}