This is the multi-page printable view of this section. Click here to print...

Return to the regular view of this page

As of 2025-07-24

Using the REST API

An explanation of the sample sketch spot-httpbin, which uses data from a child device in an HTTP GET request
This is an explanation of the sample sketch spot-httpbin, which acts as a Wi-Fi child device and sends received packet data to the mock server httpbin.org on the web.

1 - Using REST API

Latest Edition
This is a sample sketch spot-httpbin that behaves as a Wi-Fi sub-device and sends received packet data to the mock server httpbin.org on the Web.

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

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

  1. Download the zip file from GitHub (monowireless/spot-httpbin)
  2. Extract the zip file and rename the folder from spot-httpbin-main to spot-httpbin
  3. 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.

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 FileDescriptionNote
Arduino.hBasic Arduino librarySometimes can be omitted
WiFiClientSecure.hSSL communication on ESP32
WiFiUdp.hUDP communicationRequired for NTP

Third-party Libraries

Lines 9–10 include bundled third-party libraries.

#include "src/NTPClient/NTPClient.h"
#include "src/Time/TimeLib.h"
Header FileDescriptionNote
NTPClient.hAccess NTP servers
TimeLib.hConvert 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;
};
NameDescription
serialIdSerial ID
logicalIdLogical device ID
supplyVoltageSupply voltage
linkQualityLQI
temp100xTemperature Γ—100
humid100xHumidity Γ—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.

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;
NameDescription
TWE_CHTWELITE frequency channel
TWE_APPIDTWELITE 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";
NameDescription
WIFI_SSIDSSID of the network to connect
WIFI_PASSWORDPassword 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;
NameDescription
SERVER_HOSTHost name of the server
SERVER_PORTPort 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
NameDescription
NTP_UPDATE_INTERVALInterval for obtaining NTP time
QUERIES_MAX_LENGTHMax length of query string (excluding null terminator)
CONNECT_TIMEOUTTimeout for connecting to the server
RECONNECT_MIN_INTERVALMinimum interval to reconnect to Wi-Fi AP
SEND_MIN_INTERVALMinimum interval between requests
REQUEST_TIMEOUTTimeout from request to response

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;
NameDescription
RST_PINPin connected to the RST pin of TWELITE
PRG_PINPin connected to the PRG pin of TWELITE
LED_PINPin connected to the ESP32 LED on the board
RX1_PINPin connected to the RX1 pin of TWELITE
TX1_PINPin 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)
NameDescription
clientInterface for HTTPS communication
ntpUDPInterface for UDP communication for NTP
timeClientInterface for NTP

Declaring Global Variables

Lines 43–44 declare global variables.

static DataFromAria LatestDataFromAria;
static bool IsThereNewDataFromAria;
NameDescription
LatestDataFromAriaLatest data received from TWELITE ARIA
IsThereNewDataFromAriaFlag indicating new data was received from TWELITE ARIA

Declaring Function Prototypes

Lines 47–59 declare function prototypes.

void anotherLoopForTWELITE();
void anotherLoopForNTP();
NameDescription
anotherLoopForTWELITELoop function for processing TWELITE data
anotherLoopForNTPLoop function for retrieving time from NTP
void initTWELITE();
void initWiFi();
void initNTP();
NameDescription
initTWELITEInitialization function for TWELITE
initWiFiInitialization function for Wi-Fi
initNTPInitialization function for NTP
void onAppAriaPacket(const ParsedAppAriaPacket& packet);
NameDescription
onAppAriaPacketCallback function triggered when data is received from TWELITE ARIA
void sendAriaData(const DataFromAria& data)
NameDescription
sendAriaDataSends 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
            }
        },

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

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.

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

2 - Using the REST API

mwings-v1.1.3
This is an explanation of the sample sketch spot-httpbin, which acts as a Wi-Fi child device and sends received packet data to the mock server httpbin.org.

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

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.

Obtaining the Project Files

  1. Download the ZIP file from GitHub (monowireless/spot-httpbin)
  2. Extract the ZIP file and rename the folder from spot-httpbin-main to spot-httpbin
  3. 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.

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 FileDescriptionRemarks
Arduino.hBasic Arduino libraryCan sometimes be omitted, but included for safety
WiFiClientSecure.hEnables SSL communication on ESP32
WiFiUdp.hHandles UDP communicationRequired for NTP

Third-Party Libraries

Lines 9–10 include bundled third-party libraries.

#include "src/NTPClient/NTPClient.h"
#include "src/Time/TimeLib.h"
Header FileDescriptionRemarks
NTPClient.hAccesses NTP servers
TimeLib.hConverts 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"

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;
};
NameDescription
serialIdSerial ID
logicalIdLogical device ID
supplyVoltageSupply voltage
linkQualityLQI (Link Quality Index)
temp100xTemperature Γ—100
humid100xHumidity Γ—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.

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;
NameDescription
TWE_CHTWELITE channel
TWE_APPIDTWELITE 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";
NameDescription
WIFI_SSIDSSID of the network to connect to
WIFI_PASSWORDPassword 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;
NameDescription
SERVER_HOSTServer host name
SERVER_PORTServer 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
NameDescription
NTP_UPDATE_INTERVALInterval for obtaining NTP time
QUERIES_MAX_LENGTHMaximum length of query string (excluding null character)
CONNECT_TIMEOUTTimeout when connecting to the server
RECONNECT_MIN_INTERVALMinimum interval when reconnecting to Wi-Fi access point
SEND_MIN_INTERVALMinimum interval between requests
REQUEST_TIMEOUTTimeout from request to response

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;
NameDescription
RST_PINPin number connected to TWELITE’s RST pin
PRG_PINPin number connected to TWELITE’s PRG pin
LED_PINPin 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)
NameDescription
clientInterface for HTTPS communication
ntpUDPInterface for UDP communication for NTP
timeClientInterface for NTP

Declaration of Global Variables

Lines 40–41 declare global variables.

static DataFromAria LatestDataFromAria;
static bool IsThereNewDataFromAria;
NameDescription
LatestDataFromAriaLatest data received from TWELITE ARIA
IsThereNewDataFromAriaFlag indicating new data has been received from TWELITE ARIA

Declaration of Function Prototypes

Lines 44–56 declare function prototypes.

void anotherLoopForTWELITE();
void anotherLoopForNTP();
NameDescription
anotherLoopForTWELITELoop function for processing TWELITE data
anotherLoopForNTPLoop function for obtaining time via NTP
void initTWELITE();
void initWiFi();
void initNTP();
NameDescription
initTWELITEFunction to initialize TWELITE
initWiFiFunction to initialize Wi-Fi
initNTPFunction to initialize NTP
void onAppAriaPacket(const ParsedAppAriaPacket& packet);
NameDescription
onAppAriaPacketCallback function when data is received from TWELITE ARIA
void sendAriaData(const DataFromAria& data)
NameDescription
sendAriaDataFunction 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
            }
        },

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

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.

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