Using REST API
This article uses third-party open source software.
We are unable to provide detailed instructions on how to use third-party software. Also, we assume no responsibility for any damages caused by the use of such software.
Obtaining the Source Code
Available on GitHub repository monowireless/spot-httpbin.
System Overview
spot-httpbin sends part of the data received by the TWELITE parent device and the current time obtained via NTP to the mock server as an HTTP GET request, and displays the response on the serial monitor.
Required Components for Development
-
Wireless LAN Gateway TWELITE SPOT
- USB-C cable for power supply
- USB AC adapter (must supply 1A or more)
-
Magnetic, Temperature, and Humidity Sensor Wireless Tag TWELITE ARIA or other sub-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
Installing IDE and Toolchain
See How to set up a development environment with Arduino IDE 1.x.
Installing Libraries
This sample includes all required libraries by default.
Getting the Project Files
- Download the zip file from GitHub (monowireless/spot-httpbin)
- Extract the zip file and rename the folder from
spot-httpbin-main
tospot-httpbin
- Place the
spot-httpbin
folder in the Arduino sketchbook location (as noted in Arduino IDE preferences, e.g.,C:\Users\foo\Documents\Arduino
)
Changing User Settings
Open ‘config.h’ from the top tab in Arduino IDE and set the Wi-Fi SSID and password. WPA2-PSK network is assumed. Also, register the root certificate. You can obtain the root certificate from the security panel of each page in web browsers such as Chrome.
The root certificate (extension .cer
) is a text file in the following format:
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
Writing the Project Files
See How to upload the sketch to ESP32.
Sketch
Explanation of the Arduino sketch spot-httpbin.ino and config.h.
Including Libraries
Official Arduino and ESP32 Libraries
Lines 4β6 include official Arduino and ESP32 libraries.
#include <Arduino.h>
#include <WiFiClientSecure.h>
#include <WiFiUdp.h>
Header File | Description | Note |
---|---|---|
Arduino.h | Basic Arduino library | Sometimes can be omitted |
WiFiClientSecure.h | SSL communication on ESP32 | |
WiFiUdp.h | UDP communication | Required for NTP |
Third-party Libraries
Lines 9β10 include bundled third-party libraries.
#include "src/NTPClient/NTPClient.h"
#include "src/Time/TimeLib.h"
Header File | Description | Note |
---|---|---|
NTPClient.h | Access NTP servers | |
TimeLib.h | Convert epoch time |
MWings Library
Line 13 includes the MWings library.
#include <MWings.h>
User Configuration
Line 16 includes config.h.
#include "config.h"
Defining Data Types
Lines 19β26 define a structure type for storing data received from the sub-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 Γ100 |
humid100x | Humidity Γ100 |
This structure assumes the use of TWELITE ARIA.
config.h
Defining Reboot Interval
Line 4 in config.h specifies the reboot interval for the ESP32.
const uint32_t REBOOT_INTERVAL = 21600; // seconds
21600 seconds = 6 hours.
In long-term operation, memory leaks may accumulate and cause malfunction.
Therefore, we implement periodic rebooting similar to a Wi-Fi router.
Defining TWELITE Settings
Lines 7β8 in config.h define the settings to be applied to the TWELITE parent module 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 |
Defining Wi-Fi Settings
Lines 11β12 in config.h define the Wi-Fi settings to be 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 |
WIFI_PASSWORD | Password for the network |
Root Certificate
The template for the root certificate is provided at lines 14β16 in config.h.
const char *CA_CERT =
"-----BEGIN CERTIFICATE-----\n"
"-----END CERTIFICATE-----\n";
Obtain the root certificate from the security panel of the relevant page in browsers such as Chrome.
Enclose each line in double quotes and append the newline character \n
before the closing quote.
Defining Host Settings
Lines 18β19 in config.h define the host settings.
const char *SERVER_HOST = "www.httpbin.org";
const uint16_t SERVER_PORT = 443;
Name | Description |
---|---|
SERVER_HOST | Host name of the server |
SERVER_PORT | Port number of the server |
Defining Constants
From line 21 in config.h, various constants are defined.
const uint32_t NTP_UPDATE_INTERVAL = 10000; // ms
const int QUERIES_MAX_LENGTH = 128; // bytes (without \0)
const int32_t CONNECT_TIMEOUT = 10; // seconds
const uint32_t RECONNECT_MIN_INTERVAL = 5; // seconds
// SEND_MIN_INTERVAL must be longer than NTP_UPDATE_INTERVAL
const uint32_t SEND_MIN_INTERVAL = 10; // seconds
const uint32_t REQUEST_TIMEOUT = 10; // seconds
Name | Description |
---|---|
NTP_UPDATE_INTERVAL | Interval for obtaining NTP time |
QUERIES_MAX_LENGTH | Max length of query string (excluding null terminator) |
CONNECT_TIMEOUT | Timeout for connecting to the server |
RECONNECT_MIN_INTERVAL | Minimum interval to reconnect to Wi-Fi AP |
SEND_MIN_INTERVAL | Minimum interval between requests |
REQUEST_TIMEOUT | Timeout from request to response |
SEND_MIN_INTERVAL
is set longer than NTP_UPDATE_INTERVAL
.
This prevents timestamp duplication when requests are sent in rapid succession.
If SEND_MIN_INTERVAL
is too short, it may overload the server when receiving consecutive packets.
Always leave a sufficient interval between requests.
Defining Pin Numbers
Lines 29β34 define 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 connected to the RST pin of TWELITE |
PRG_PIN | Pin connected to the PRG pin of TWELITE |
LED_PIN | Pin connected to the ESP32 LED on the board |
RX1_PIN | Pin connected to the RX1 pin of TWELITE |
TX1_PIN | Pin connected to the TX1 pin of TWELITE |
Declaring Global Objects
Lines 37β40 declare global objects.
static WiFiClientSecure client;
static WiFiUDP ntpUDP;
static NTPClient timeClient(ntpUDP, "ntp.nict.jp",
32400, NTP_UPDATE_INTERVAL); // JST(UTC+9)
Name | Description |
---|---|
client | Interface for HTTPS communication |
ntpUDP | Interface for UDP communication for NTP |
timeClient | Interface for NTP |
Declaring Global Variables
Lines 43β44 declare global variables.
static DataFromAria LatestDataFromAria;
static bool IsThereNewDataFromAria;
Name | Description |
---|---|
LatestDataFromAria | Latest data received from TWELITE ARIA |
IsThereNewDataFromAria | Flag indicating new data was received from TWELITE ARIA |
Declaring Function Prototypes
Lines 47β59 declare function prototypes.
void anotherLoopForTWELITE();
void anotherLoopForNTP();
Name | Description |
---|---|
anotherLoopForTWELITE | Loop function for processing TWELITE data |
anotherLoopForNTP | Loop function for retrieving time from NTP |
void initTWELITE();
void initWiFi();
void initNTP();
Name | Description |
---|---|
initTWELITE | Initialization function for TWELITE |
initWiFi | Initialization function for Wi-Fi |
initNTP | Initialization function for NTP |
void onAppAriaPacket(const ParsedAppAriaPacket& packet);
Name | Description |
---|---|
onAppAriaPacket | Callback function triggered when data is received from TWELITE ARIA |
void sendAriaData(const DataFromAria& data)
Name | Description |
---|---|
sendAriaData | Sends TWELITE ARIA data via HTTP GET request |
setup()
Lines 62β90 perform the overall initialization.
void setup() {
Serial.begin(115200);
initTWELITE();
initWiFi();
initNTP();
// 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)
// Attach another loop function for NTP
xTaskCreatePinnedToCore(
[](void *params) {
while (true) {
anotherLoopForNTP();
vTaskDelay(1); // IMPORTANT for Watchdog
}
},
"Task for anotherLoopForNTP()", 8192, nullptr, 17, nullptr,
0); // Priority is 17 (lower than WiFi and TWELITE)
}
By using xTaskCreatePinnedToCore()
, tasks other than loop()
are registered.
The following section is an anonymous lambda function with no 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 93β114 are the main loop processing.
This section handles HTTP request processing, reconnection when Wi-Fi is 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 117β119 are the loop processing for TWELITE.
To sequentially receive and interpret data, this is made a separate task from loop()
, which may contain blocking processing.
void anotherLoopForTWELITE() {
Twelite.update();
}
anotherLoopForNTP()
Lines 120β123 are the loop processing for NTP.
This is also made a separate task from loop()
because UDP communication may involve blocking processing.
void anotherLoopForNTP() {
timeClient.update();
setTime(timeClient.getEpochTime());
}
initTWELITE()
Lines 126β133 are the initialization process for TWELITE.
This starts the TWELITE mounted on TWELITE SPOT with the specified settings and registers a callback function 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);
}
Please also refer to the following references:
Twelite.begin()
mwings::MWings class | MWings API ReferenceTwelite.on()
mwings::MWings class | MWings API Reference
initWiFi()
Lines 136β160 are the Wi-Fi initialization process.
If not connected, it retries to connect 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);
}
initNTP()
Lines 163β167 are the initialization process for NTP.
void initNTP() {
timeClient.begin();
timeClient.update();
setTime(timeClient.getEpochTime());
}
onAppAriaPacket()
Lines 170β180 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 to the global variable is processed by sendAriaData()
in another 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 183β240 are a function that sets the data from TWELITE ARIA into the query string of an HTTP GET request and sends it.
HTTP GET is used for code simplicity.
If you wish to use HTTP POST, please add a request body.
To prevent excessive server load, 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
char queries[QUERIES_MAX_LENGTH+1];
snprintf(queries, sizeof(queries),
"datetime=%04d%02d%02d%02d%02d%02d&sid=%X&lid=%d&temp=%d&humid=%d&bat=%d&lqi=%d",
// Note that NTP_UPDATE_INTERVAL is set for 10000ms by default; second() delays up to 10s.
// To prevent duplication of datetime, SEND_MIN_INTERVAL is set for 10s.
year(), month(), day(), hour(), minute(), second(),
data.serialId,
data.logicalId,
data.temp100x,
data.humid100x,
data.supplyVoltage,
data.linkQuality);
// Send a request
client.println(String("GET https://") +
SERVER_HOST +
String("/get?") +
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.println("Headers received");
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();
}
lastTimeRequested = millis();
} else {
Serial.println("Requests are too frequently; skip.");
}
}