Using the REST API
spot-httpbin
, which acts as a Wi-Fi child device and sends received packet data to the mock server httpbin.org.This article uses third-party open-source software.
We are unable to provide detailed instructions on how to use third-party software. In addition, we assume no responsibility for any damages arising from the use of such software.
Getting the Source Code
You can obtain it from the GitHub repository monowireless/spot-httpbin.
System Overview
spot-httpbin sends part of the data received by the TWELITE parent device along with the current time obtained via NTP as an HTTP GET request to a mock server, and displays the response on the serial monitor.
Requirements for Development
-
Wi-Fi Gateway TWELITE SPOT
- USB-C cable for power
- USB AC adapter (must provide at least 1A)
-
Wireless tag with magnet, temperature, and humidity sensors TWELITE ARIA and other child devices (If you do not have one, please purchase 👉 List of retailers)
- Power source such as a CR2032 coin cell battery
-
USB Adapter TWELITE R3 (If you do not have one, please purchase 👉 List of retailers)
- USB-C cable for data communication
- 💻 Development Computer
Environment Setup
Installing IDE and Toolchain
See Setting up the development environment using Arduino IDE 1.x.
Installing Libraries
This sample includes required libraries from the beginning.
src
folder at the same level as the sketch are not shown in the IDE, they are built recursively.Obtaining 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 your Arduino sketchbook directory (set in Arduino IDE preferences, e.g.C:\Users\foo\Documents\Arduino
)
Modifying User Settings
Open config.h
from the tabs at the top of the Arduino IDE and set your Wi-Fi SSID and password. WPA2-PSK network is assumed. Also, register the root certificate. The root certificate can be obtained from the security page for each website using Chrome or another browser.
The root certificate (with .cer
extension) is a text file in the following format:
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
Writing the Project File
See How to upload a sketch to the ESP32.
Sketch
This section explains 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 | Remarks |
---|---|---|
Arduino.h | Basic Arduino library | Can sometimes be omitted, but included for safety |
WiFiClientSecure.h | Enables SSL communication on ESP32 | |
WiFiUdp.h | Handles 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 | Remarks |
---|---|---|
NTPClient.h | Accesses NTP servers | |
TimeLib.h | Converts epoch time |
MWings Library
Line 13 includes the MWings library.
#include <MWings.h>
Defining User Settings
Line 16 includes the configuration file.
#include "config.h"
config.h
. Modify this file before execution.Definition of Data Types
Lines 19–26 define the struct type used 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 (Link Quality Index) |
temp100x | Temperature ×100 |
humid100x | Humidity ×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 can accumulate and cause issues.
Therefore, like a Wi-Fi router, periodic reboots are performed.
Definition of TWELITE Settings
Lines 7–8 of config.h
define the settings applied to the TWELITE parent device installed in TWELITE SPOT.
const uint8_t TWE_CH = 18;
const uint32_t TWE_APPID = 0x67720102;
Name | Description |
---|---|
TWE_CH | TWELITE 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 installed in 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 |
Root Certificate
Lines 14–16 of config.h
provide a template for describing the contents of the root certificate.
const char *CA_CERT =
"-----BEGIN CERTIFICATE-----\n"
"-----END CERTIFICATE-----\n";
Obtain the root certificate from the security screen for each website using Chrome or another web browser.
All lines must be enclosed in double quotes, and a newline character \n
must be added before the ending double quote.
Host Settings
Lines 18–19 of config.h
define the host settings.
const char *SERVER_HOST = "www.httpbin.org";
const uint16_t SERVER_PORT = 443;
Name | Description |
---|---|
SERVER_HOST | Server host name |
SERVER_PORT | Server port number |
Definition of Various Constants
From line 21 of 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 | Maximum length of query string (excluding null character) |
CONNECT_TIMEOUT | Timeout when connecting to the server |
RECONNECT_MIN_INTERVAL | Minimum interval when 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 be longer than NTP_UPDATE_INTERVAL
.
If the request interval is too short, timestamps may overlap between requests.
If you set SEND_MIN_INTERVAL
too short, the server may be overloaded when packets are received in rapid succession.
Be sure to leave an appropriate interval.
Definition of Pin Numbers
Lines 29–31 define the pin numbers.
static const int RST_PIN = 5;
static const int PRG_PIN = 4;
static const int LED_PIN = 18;
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 |
Declaration of Global Objects
Lines 34–37 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 |
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 has been received from TWELITE ARIA |
Declaration of Function Prototypes
Lines 44–56 declare function prototypes.
void anotherLoopForTWELITE();
void anotherLoopForNTP();
Name | Description |
---|---|
anotherLoopForTWELITE | Loop function for processing TWELITE data |
anotherLoopForNTP | Loop function for obtaining time via NTP |
xTaskCreatePinnedToCore()
is used to register a separate task from the loop()
function.
void initTWELITE();
void initWiFi();
void initNTP();
Name | Description |
---|---|
initTWELITE | Function to initialize TWELITE |
initWiFi | Function to initialize Wi-Fi |
initNTP | Function to initialize NTP |
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 59–87 perform 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)
}
xTaskCreatePinnedToCore()
is used to register a separate task from the loop()
function.
The following part is an unnamed 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 intervention for the watchdog timer.loop()
Lines 90–111 are the main loop process.
This handles HTTP requests, reconnection to Wi-Fi when 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 114–116 are the loop process for TWELITE.
To sequentially receive and interpret data, this is run as a separate task from the (potentially blocking) loop()
.
void anotherLoopForTWELITE() {
Twelite.update();
}
anotherLoopForNTP()
Lines 117–120 are the loop process for NTP.
Since this involves UDP communication, it is also run as a separate task from the (potentially blocking) loop()
.
void anotherLoopForNTP() {
timeClient.update();
setTime(timeClient.getEpochTime());
}
initTWELITE()
Lines 123–130 perform the initialization process for TWELITE.
This starts the TWELITE installed in 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);
}
See also the following references:
Twelite.begin()
mwings::MWings class | MWings API ReferenceTwelite.on()
mwings::MWings class | MWings API Reference
initWiFi()
Lines 133–157 perform Wi-Fi initialization.
If not connected, reconnection is attempted 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) x
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 160–164 perform NTP initialization.
void initNTP() {
timeClient.begin();
timeClient.update();
setTime(timeClient.getEpochTime());
}
onAppAriaPacket()
Lines 167–177 describe the process performed 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 is 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 180–237 define a function that sets TWELITE ARIA data into the query string of an HTTP GET request and sends it.
HTTP GET is used for simplicity.
If you want to use HTTP POST, please add a request body.
To avoid excessive load on the server, transmission 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.");
}
}