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