Skip to content

Reference

Arduino library

Classes, functions, macros and compile options of the Thinger.io Arduino library, with their exact signatures and default values.

Reference16 min read

The Thinger.io Arduino library connects a board to Thinger.io over IOTMP legacy, the earlier version of IOTMP. This page lists its public API: the classes for each network, the connection functions, device resources, calls to the platform, macros and compile options. To learn how to use them, follow the Coding guide.

PackageInstalled fromVersionSource code
thinger.ioArduino Library Manager, PlatformIO2.49.0thinger-io/Arduino-Library

Install ​

Install thinger.io from the library manager of the Arduino IDE (Sketch > Include Library > Manage Libraries), or add it to the project file of PlatformIO:

platformio.ini
ini
lib_deps = thinger-io/thinger.io

The library examples are in File > Examples > thinger.io, one per board. Step-by-step setup is in Arduino.

Classes by network ​

Include the header of your board and create one object of its class, usually called thing. Which class each board uses, and which ones support OTA updates, is listed in Supported boards.

Every class takes the same three arguments: your username, the device ID and the device credentials. Two classes also take the serial port of the modem:

ConstructorNotes
ThingerESP32(const char* user, const char* device, const char* device_credential)The same arguments for every board class.
ThingerTinyGSM(const char* user, const char* device, const char* device_credential, Stream& serial)GSM modems driven with AT commands.
ThingerESP8266AT(const char* user, const char* device, const char* device_credential, Stream& serial)An ESP8266 used as a Wi-Fi modem.
ThingerSmartConfig(const char* user, const char* device, const char* device_credential, bool use_led=true)ESP8266 that gets the Wi-Fi network from the ESP-Touch app.
cpp
#include <ThingerESP32.h>

ThingerESP32 thing("YOUR_USER", "YOUR_DEVICE", "YOUR_TOKEN");

YOUR_TOKEN is the device credentials, not an access token.

Network setup ​

Call these functions in setup(), before the first thing.handle(). Each one exists only in the classes listed.

FunctionClassesDescription
void add_wifi(const char* ssid, const char* password=nullptr)Wi-Fi classes (ThingerESP32, ThingerESP8266, ThingerWiFiNINA, ThingerWifi101, ThingerR4WiFi, ThingerPicoW, ThingerMbed, ThingerZephyr, ThingerWifi)Adds a Wi-Fi network. On ESP32, ESP8266, Raspberry Pi Pico W and Pico 2 W, call it once per network: with two or more, the board scans and joins the strongest one, also when it reconnects; with one, it joins it without scanning, so a hidden network only works as the only one. On the other boards a second call replaces the first. On ESP32 and ESP8266, without a call the board joins the last network it used.
void add_wifi_enterprise(const char* ssid, const char* username, const char* password, const char* identity="", const char* ca_pem=nullptr)ThingerESP32, with version 3 of the esp32 board packageAdds a WPA2-Enterprise network with PEAP and EAP-MSCHAPv2, such as eduroam. identity is the outer identity; empty, the board uses the username. ca_pem is the certificate, in PEM format, of the authority of the authentication server: with it, the board checks the server and joins this network first, on its own, and tries the other networks if it cannot join it; without it, the server is not checked and the board joins the strongest of its networks. Only one network can carry a certificate.
void set_apn(const char* apn, const char* apn_username="", const char* apn_password="")ThingerMKRGSM, ThingerMKRNBSets the APN of the cellular network.
void set_pin(const char* pin)ThingerMKRGSM, ThingerMKRNB, ThingerNBCore, ThingerLinkItOneGPRSUnlocks the SIM card with its PIN.
void setAPN(const char* APN, const char* user=nullptr, const char* password=nullptr)ThingerTinyGSMSets the APN.
void setPIN(const char* pin)ThingerTinyGSMUnlocks the SIM card.
void set_address(const char* ip, const char* gateway, const char* subnet, const char* dns1="8.8.8.8", const char* dns2="8.8.4.4")ThingerESP32EthUses a fixed IP address instead of DHCP.
void set_hostname(const char* hostname)ThingerESP32EthSets the host name of the board on the network.

The example of the MKR NB 1500, trimmed:

ArduinoMKRNB1500.ino
cpp
#include <ThingerMKRNB.h>

ThingerMKRNB thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);

void setup() {
  // optional set pin number
  thing.set_pin(PIN_NUMBER);

  // set APN
  thing.set_apn(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD);
}

Run the connection in its own task ​

On ESP32 with #define THINGER_FREE_RTOS (ThingerESP32, ThingerESP32Eth and ThingerTinyGSM), and on the Mbed OS boards (ThingerMbed, ThingerMbedEth: Nano RP2040 Connect, and Portenta H7 and Opta on the Mbed OS core), the connection can run in its own task. Call thing.start() in setup() instead of calling thing.handle() in loop().

FunctionClassesDescription
bool start(unsigned core=ARDUINO_RUNNING_CORE, size_t stack_size=8192)ThingerESP32, ThingerESP32Eth and ThingerTinyGSM on ESP32, with THINGER_FREE_RTOSStarts the task on the given core, with the given stack size in bytes.
bool start()ThingerMbed, ThingerMbedEthStarts the task.
bool stop()BothStops the task.
bool is_running()Bothtrue while the task runs.
void lock(), void unlock()BothProtect the variables that your device resources share with loop(), and the calls to the platform from another task, such as write_bucket or call_endpoint. On ESP32, get_state() and get_error() can be read from any task without them.

The ESP32RTOS example, trimmed:

ESP32RTOS.ino
cpp
#define THINGER_SERIAL_DEBUG
#define THINGER_FREE_RTOS

#include <ThingerESP32.h>

ThingerESP32 thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);

void setup() {
  thing.add_wifi(SSID, SSID_PASSWORD);
  thing["millis"] >> outputValue(millis());

  // start thinger on its own task
  thing.start();
}

void loop() {
  // use thing.lock() thing.unlock() if using variables exposed on thinger resources
}

Connection ​

These functions are common to every class.

FunctionDescription
void handle()Connects to the network and to the server when needed, answers the requests of the platform and sends the keepalive. Call it in every iteration of loop(). While the device is offline it returns at once between connection attempts, so loop() keeps running: see Reconnection.
bool is_connected()true while the connection to the server is open.
void stop()Closes the connection. The next thing.handle() connects again.
void reboot()Closes the connection and restarts the board.
void set_credentials(const char* username, const char* device_id, const char* device_password)Changes the username, device ID and device credentials. Takes effect on the next connection.
void set_host(const char* host)Changes the server. Takes effect on the next connection.
const char* get_host()The current server.
const char* get_username()The username in use.
const char* get_device_id()The device ID in use.
void set_root_ca(const char* ca)Sets the root certificate that validates the server, in PEM format. It takes effect on the next connection.
const char* get_root_ca()The root certificate in use.
void set_state_listener(std::function<void(THINGER_STATE)> state_listener)Calls state_listener on every change of the connection state.
THINGER_STATE get_state() constThe last connection state, the one last passed to the state listener.
THINGER_ERROR get_error() constThe reason of the last NETWORK_CONNECT_ERROR state, or ERROR_NONE once the network is connected. See Network errors.

The strings you pass to set_credentials, set_host and set_root_ca are not copied: they must stay valid while the device runs (literals, globals or String objects that live as long as thing).

Connection states ​

set_state_listener receives one value of the ThingerClient::THINGER_STATE enumeration, in this order of a normal connection:

StateMeaning
NETWORK_CONNECTINGJoining the network (Wi-Fi, Ethernet or cellular).
NETWORK_CONNECTEDThe network is up.
NETWORK_CONNECT_ERRORThe board could not join the network.
SOCKET_CONNECTINGOpening the connection to the server.
SOCKET_CONNECTEDThe connection to the server is open.
SOCKET_CONNECTION_ERRORThe server could not be reached.
THINGER_AUTHENTICATINGSending the device credentials.
THINGER_AUTHENTICATEDThe server accepted the device credentials. The device is online.
THINGER_AUTH_FAILEDThe server rejected the username, device ID or device credentials.
SOCKET_TIMEOUTThe server stopped answering.
SOCKET_ERRORThe connection failed.
SOCKET_DISCONNECTEDThe connection is closed.
THINGER_STOP_REQUESTThe sketch called thing.stop() or thing.reboot().
cpp
thing.set_state_listener([](ThingerClient::THINGER_STATE state) {
  if (state == ThingerClient::THINGER_AUTHENTICATED) {
    Serial.println("Connected to Thinger.io");
  }
});

Reconnection ​

thing.handle() does not hold loop() while the device is offline:

  • The Wi-Fi classes listed for add_wifi in Network setup join the network step by step, one step on each thing.handle(), with the state NETWORK_CONNECTING in the meantime. They give up after 30 seconds without joining the network, and after 30 more without an IP address. Two exceptions wait inside thing.handle(): a board with several Wi-Fi networks scans them for a few seconds, and the WebConfig classes wait for the network.
  • The other classes wait for the network inside thing.handle(): Ethernet, cellular modems, ThingerSmartConfig, ThingerCC3000, ThingerESP8266AT and ThingerLinkItOneWifi.
  • The TLS connection and the authentication with the server take a few seconds on every attempt, inside thing.handle().

Between two attempts, thing.handle() returns at once until the wait ends:

FailureWait before the next attempt
The network (NETWORK_CONNECT_ERROR)RECONNECTION_TIMEOUT milliseconds.
The server: socket, TLS or authentication (SOCKET_CONNECTION_ERROR, THINGER_AUTH_FAILED)RECONNECTION_TIMEOUT after the first failure, doubled on every failure up to THINGER_RECONNECTION_MAX_TIMEOUT, plus a random margin of up to 25 %, so many devices do not reconnect at the same time. It starts again from RECONNECTION_TIMEOUT once the device connects.

With THINGER_SERIAL_DEBUG, the serial monitor shows each wait:

text
[THINGER] Retrying connection in (ms): 5843

Network errors ​

get_error() returns one value of the ThingerClient::THINGER_ERROR enumeration. Each board reports the reasons it can tell apart, and ERROR_UNKNOWN for the rest.

ErrorMeaningBoards
ERROR_NONEThe network is connected.All
ERROR_UNKNOWNThe board does not report the reason.All
NETWORK_NOT_CONFIGUREDNo Wi-Fi network or APN is set. With WebConfig, the configuration page closed without a Wi-Fi network.Wi-Fi and cellular
NETWORK_NOT_FOUNDThe Wi-Fi network is not available.Wi-Fi
NETWORK_AUTH_ERRORThe Wi-Fi network rejected the connection, usually for a wrong password.Wi-Fi
NETWORK_NO_IPThe board joined the Wi-Fi network but got no IP address.Wi-Fi
MODEM_NOT_RESPONDINGThe modem does not answer.ThingerTinyGSM
MODEM_SIM_ERRORThe SIM card cannot be used, for example for a wrong PIN.ThingerTinyGSM
NETWORK_NOT_REGISTEREDThe modem cannot register in the network, for example for lack of coverage.ThingerTinyGSM, ThingerNBCore, ThingerBC66
NETWORK_REGISTRATION_DENIEDThe operator rejected the registration, for example because the SIM card is disabled.ThingerNBCore, ThingerBC66
NETWORK_APN_ERRORThe modem is registered, but the data connection does not open: check the APN.ThingerTinyGSM, ThingerMKRNB, ThingerMKRGSM

Device resources ​

A device resource is a function of the device that the platform can call. Declare it in setup() with thing["name"] and an operator that sets its direction:

DeclarationEquivalent functionKind
thing["name"] << [](pson& in){ … };void set_input(std::function<void(pson&)> in_function)Input: receives data from the platform.
thing["name"] >> [](pson& out){ … };void set_output(std::function<void(pson&)> out_function)Output: sends data to the platform.
thing["name"] = [](pson& in, pson& out){ … };void set_input_output(std::function<void(pson& in, pson& out)> pson_in_pson_out_function)Input and output.
thing["name"] = [](){ … };void set_function(std::function<void()> run_function)No data: runs an action.

An input resource is called with an empty in when the platform reads its current value, for example when you open it in the console. Check in.is_empty() and fill in with the current value, as in the Opta examples:

ArduinoOptaWiFi.ino
cpp
thing["relay_d0"] << [](pson& in){
    if(in.is_empty()){
        in = (bool) digitalRead(D0);
    }else{
        digitalWrite(D0, in ? HIGH : LOW);
        digitalWrite(LED_D0, in ? HIGH : LOW);
    }
};

An output resource fills out each time it is read:

ESP32.ino
cpp
thing["millis"] >> outputValue(millis());

Device resources can be nested, as in thing["led"]["green"]: the platform calls the inner device resource by its path, led/green.

FunctionDescription
void then(std::function<void()> then)Runs then after each call to the resource has been answered. Not available on AVR boards.
void set_stream_listener(std::function<void(uint16_t, unsigned long, bool enabled)> stream_listener)Called when the platform starts (enabled is true) or stops listening to the resource, with the stream ID and the sampling interval in milliseconds.

Macros for common resources ​

MacroKindDescription
digitalPin(PIN)InputSets a digital output pin on or off and reports its state. PIN must be a number or a #define.
analogPin(PIN)InputWrites an analog (PWM) value to the pin. Not available on ESP32.
outputValue(value)OutputSends the value of an expression or a variable.
outputString(value)OutputThe same, for a text.
inputValue(value)InputStores the value received in a variable, and reports the current value.
inputValue(value, callback)InputThe same, and runs callback after each change.
servo(servo)InputMoves a servo (an object of the Arduino Servo library) to the angle received, and reports its angle.
ESP32.ino
cpp
// digital pin control example (i.e. turning on/off a light, a relay, configuring a parameter, etc)
thing["GPIO_16"] << digitalPin(16);

// resource output example (i.e. reading a sensor value)
thing["millis"] >> outputValue(millis());

The pson type ​

pson holds the data of a device resource: a single value, an object with named fields or an array. Assign a value to write it, and assign the pson to a variable (or cast it) to read it.

TypeWriteRead
Booleanin = true;bool value = in;
Integerout = 42;int value = in; (also long, unsigned int, unsigned long, short, char)
Decimalout = 21.5;float value = in; or double value = in;
Textout = "text"; or a Stringconst char* text = in; or String text = in;
Objectout["temperature"] = 21.5;float value = in["temperature"];
Arraypson_array& array = out; then array.add(1).add(2);—
Bytesvoid set_bytes(void* bytes, size_t size)bool get_bytes(const void*& bytes, size_t& size)
FunctionDescription
bool is_empty()true if the pson has no value. In an input resource, true when the platform only reads the current value.
bool is_boolean(), bool is_number(), bool is_integer(), bool is_float(), bool is_string(), bool is_bytes(), bool is_object(), bool is_array(), bool is_null()The type of the value.

The wire format of pson is PSON, described on iotmp.io.

Calls to the platform ​

These functions send a request to the server. They need the device to be connected and return false if the request could not be sent. With the last argument (confirm_write or confirm_call) set to true, they wait for the server and return true only if the server accepted the request.

Data Buckets ​

FunctionDescription
bool write_bucket(const char* bucket_id, pson& data, bool confirm_write=false)Stores data in the Data Bucket.
bool write_bucket(const char* bucket_id, const char* resource_name, bool confirm_write=false)Stores the output of a device resource.
bool write_bucket(const char* bucket_id, thinger_resource& resource, bool confirm_write=false)The same, with the resource object: thing["name"].

The Data Bucket must exist, with its data source set to the device write call. See Data Buckets.

Properties ​

FunctionDescription
bool get_property(const char* property_identifier, pson& data)Reads a property of the device into data. Waits for the server.
bool set_property(const char* property_identifier, pson& data, bool confirm_write=false)Writes a property of the device.

See Properties.

Endpoints ​

FunctionDescription
bool call_endpoint(const char* endpoint_name, bool confirm_call=false)Calls the endpoint without data.
bool call_endpoint(const char* endpoint_name, pson& data, bool confirm_call=false)Calls the endpoint with data.
bool call_endpoint(const char* endpoint_name, const char* resource_name, bool confirm_call=false)Calls the endpoint with the output of a device resource.
bool call_endpoint(const char* endpoint_name, thinger_resource& resource, bool confirm_call=false)The same, with the resource object.

See Endpoints.

Other devices ​

FunctionDescription
bool call_device(const char* device_name, const char* resource_name, bool confirm_call=false)Calls a device resource of another device of your account.
bool call_device(const char* device_name, const char* resource_name, pson& data, bool confirm_call=false)The same, with data as input.
bool call_device(const char* device_name, const char* resource_name, thinger_resource& resource, bool confirm_call=false)The same, with the output of a local device resource as input.

The other device must be connected.

Streams ​

A stream sends the value of a device resource to whoever is listening in real time, such as an open dashboard. Nothing is sent, or stored, when no one is listening.

FunctionDescription
bool stream(const char* resource)Sends the current output of the resource. Returns false if no one is listening.
bool stream(thinger_resource& resource)The same, with the resource object.
bool stream_data(thinger_resource& resource, pson& payload)Sends payload on the stream of the resource instead of its output.

The platform can also ask the device to sample an output resource at a fixed interval, for example for a dashboard widget that refreshes on its own; the library does it from thing.handle() without any code.

Over-the-air updates ​

The OTA classes receive firmware from the Visual Studio Code extension. Create the object after thing, with the class of your board (see Supported boards):

ArduinoMKRNB1500.ino
cpp
#include <ThingerMKRNB.h>
#include <ThingerMKRNBOTA.h>

ThingerMKRNB thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);
ThingerMKRNBOTA ota(thing);
FunctionDescription
void set_enabled(bool enabled)Accepts (true, the default) or rejects updates.
bool is_enabled()Whether updates are accepted.
void set_block_size(size_t size)Size in bytes of each block of firmware.
void on_start(std::function<void()> on_start)Called when an update starts.
void on_progress(std::function<void(size_t progress, size_t total)> on_progress)Called after each block, with the bytes received and the total.
void on_end(std::function<void()> on_end)Called when the update ends.

The device reports its OTA settings in the $ota/options device resource, which the extension reads before each update.

Compatibility check ​

To keep a device from installing a firmware built for other hardware, such as another PCB version or a cellular modem instead of Wi-Fi, define THINGER_OTA_CHECK before the OTA include, with one entry per key and value:

cpp
#define THINGER_OTA_CHECK(FIELD) FIELD("PCB_MODEL", "SENSOR_I2C") FIELD("PCB_VERSION", "V2")

#include <ThingerESP32.h>
#include <ThingerESP32OTA.h>
  • The fields are stored in the firmware binary, and the device reports them in $ota/options.
  • The Visual Studio Code extension compares the fields of the new firmware with those of the device, and asks for confirmation when they differ or the new firmware has none.
  • The device rejects an update whose fields differ from its own or are missing, unless the user confirmed it in the extension. It answers with the error "Firmware not compatible with this device (THINGER_OTA_CHECK)".
  • A device without fields accepts any firmware.
  • Up to 16 fields, with keys of 1 to 32 characters and values of up to 128 characters, and no repeated keys. A sketch that breaks a limit does not compile.
  • Available on every board with OTA updates.

Automatic rollback (ESP32) ​

On the ESP32, define THINGER_OTA_ROLLBACK before the OTA include. A new firmware must then connect to Thinger.io within THINGER_OTA_ROLLBACK_TIMEOUT seconds of its first start; otherwise the board goes back to the previous firmware. It also goes back if it restarts before connecting, for example after a crash.

cpp
#define THINGER_OTA_ROLLBACK
#define THINGER_OTA_ROLLBACK_TIMEOUT 300

#include <ThingerESP32.h>
#include <ThingerESP32OTA.h>

The new firmware is the one that must define it: a firmware without it is accepted as soon as it starts. Define it in only one source file of the project, the one that includes the OTA header.

More in OTA updates.

Compile options ​

Define these options before the first #include of the library, or as build flags (-D) in PlatformIO.

OptionDefaultDescription
THINGER_SERVER"iot.thinger.io"Server to connect to. On a private instance, set your domain, YOUR_SERVER. With this option, the library validates the server with the root certificate of Let's Encrypt (ISRG Root X1) instead of the one of the Thinger.io Cloud servers (USERTrust RSA); use THINGER_ROOT_CA if your server has a certificate from another authority.
THINGER_ROOT_CANot definedRoot certificate, in PEM format, that validates the server, for a private instance whose certificate comes from its own authority. Define it in the sketch, before the include, as in Server and TLS. It replaces the default certificates. ESP32, ESP8266, Raspberry Pi Pico W and Pico 2 W, Arduino UNO R4 WiFi, Arduino UNO Q, and the boards of the Arduino Zephyr core (ThingerZephyr).
THINGER_SSL_PORT25202Server port for TLS connections.
THINGER_PORT25200Server port for connections without TLS.
THINGER_SERIAL_DEBUGNot definedPrints each connection step to the serial port. Open the serial monitor at the speed you set with Serial.begin().
THINGER_INSECURE_SSLNot definedConnects over TLS without validating the server certificate, for a private instance with a self-signed certificate. ESP32, ESP8266, Raspberry Pi Pico W and Pico 2 W, and the boards of the Arduino Zephyr core (ThingerZephyr). On the Arduino UNO R4 WiFi, the Wi-Fi module always validates the certificate: with this option, it validates it with its own certificates instead of the root certificate of the library.
_DISABLE_TLS_Not definedConnects without TLS, to THINGER_PORT.
THINGER_FREE_RTOSNot definedESP32: adds thing.start() to run the connection in its own task, with ThingerESP32, ThingerESP32Eth and ThingerTinyGSM.
RECONNECTION_TIMEOUT5000Milliseconds to wait after a failed connection attempt, and the first wait after a failure of the server. See Reconnection.
THINGER_RECONNECTION_MAX_TIMEOUT30000Longest wait, in milliseconds, between two attempts to connect to the server, without the random margin.
DEFAULT_READ_TIMEOUT10000Milliseconds to wait for the rest of a message from the server.
THINGER_OTA_COMPRESSION1 on ESP32, Portenta H7 and Opta; 0 on Raspberry Pi Pico W and Pico 2 W1 receives the OTA firmware compressed, 0 uncompressed. On the Pico boards, compression needs a recent version of the Visual Studio Code extension.
THINGER_OTA_CHECKNot definedCompatibility fields of the firmware for OTA updates. See Compatibility check.
THINGER_OTA_ROLLBACKNot definedESP32: goes back to the previous firmware if a new one does not connect to Thinger.io. See Automatic rollback.
THINGER_OTA_ROLLBACK_TIMEOUT300ESP32, with THINGER_OTA_ROLLBACK: seconds a new firmware has to connect to Thinger.io.
THINGER_OTA_VERSION(empty)Firmware version the device reports for OTA updates. The Visual Studio Code extension skips devices that already run the same version.
THINGER_DEVICE_SSID"Thinger-Device"WebConfig classes: name of the Wi-Fi network the board opens for its setup.
THINGER_DEVICE_SSID_PSWD"thinger.io"WebConfig classes: password of that network. See WebConfig.
THINGER_WEBCONFIG_ATTEMPTS3WebConfig classes: failed attempts to join the stored Wi-Fi network before the configuration page opens again. 0 turns it off.
THINGER_WEBCONFIG_PORTAL_TIMEOUT180WebConfig classes: seconds the configuration page stays open when it opens for that reason.

Some boards do not support TLS with this library: they always connect without TLS, to THINGER_PORT. The IOTMP reference lists the ports of the server.

See also ​