This guide connects a board running Zephyr RTOS to Thinger.io with the IOTMP client module. You add the module to your west workspace, enable it in prj.conf, expose device resources and turn on TLS.
To program an Arduino board from the Arduino IDE with the Arduino Zephyr core, use the Arduino library instead: see Portenta H7, Opta and GIGA R1 WiFi and the UNO Q.
Before you start
- A Thinger.io account. Your server is
YOUR_SERVER:iot.thinger.ioon the free Cloud account, or your own domain on a private instance. - A Zephyr 4.0 or later workspace managed with
west, and its SDK. - A board with a network interface supported by Zephyr (Ethernet, Wi-Fi or cellular) and C++ support.
Steps
Create the device
Console
Go to Devices and click Add Device. Choose IOTMP Device (Thinger.io protocol) as Device Type, set the Device Id to
YOUR_DEVICE, click Random in Device Credentials and copy the credentials. Click Add Device.CLI
bashthinger device create YOUR_DEVICE -t GenericAI
Ask your AI agentCreate an IOTMP device called YOUR_DEVICE in my Thinger.io account and show me its credentials.
Add the module to west.yml
Add the client and the protocol core it uses to the
projectsof your manifest:west.ymlyamlmanifest: projects: # ... - name: iotmp-zephyr url: https://github.com/thinger-io/IOTMP-Zephyr revision: main path: modules/lib/iotmp-zephyr - name: iotmp-embedded url: https://github.com/thinger-io/IOTMP-Embedded revision: main path: modules/lib/iotmp-embeddedThen download them:
bashwest updateEnable it in prj.conf
The client needs networking with sockets, C++17 and
eventfd:prj.confini# Networking CONFIG_NETWORKING=y CONFIG_NET_IPV4=y CONFIG_NET_TCP=y CONFIG_NET_SOCKETS=y CONFIG_DNS_RESOLVER=y CONFIG_NET_DHCPV4=y # C++ CONFIG_CPP=y CONFIG_STD_CPP17=y CONFIG_GLIBCXX_LIBCPP=y CONFIG_EVENTFD=y # Thinger.io IOTMP client CONFIG_THINGER_IOTMP=y # TLS CONFIG_MBEDTLS=y CONFIG_MBEDTLS_BUILTIN=y CONFIG_MBEDTLS_TLS_VERSION_1_2=y CONFIG_NET_SOCKETS_SOCKOPT_TLS=y CONFIG_THINGER_IOTMP_TLS=y CONFIG_MAIN_STACK_SIZE=4096 CONFIG_HEAP_MEM_POOL_SIZE=16384Add the options your board needs for its network interface, such as the Wi-Fi driver.
Write the application
src/main.cppcpp#include <zephyr/kernel.h> #include <zephyr/logging/log.h> #include <thinger/iotmp/client.hpp> LOG_MODULE_REGISTER(app, LOG_LEVEL_INF); static thinger::iotmp::client thing; static float temperature = 22.5f; static float threshold = 30.0f; int main(void) { thing.set_credentials("YOUR_USER", "YOUR_DEVICE", "YOUR_TOKEN"); // thing.set_host("YOUR_SERVER"); // only on a private instance // output resource: sensor readings thing["environment"] = [](thinger::iotmp::output& out) { out["temperature"] = temperature; }; // input resource: receives {"state": true} or {"state": false} thing["led"] = [](thinger::iotmp::input& in) { bool on = in["state"].get<bool>(); LOG_INF("LED %s", on ? "on" : "off"); }; // input and output resource: read and change a setting thing["threshold"] = [](thinger::iotmp::input& in, thinger::iotmp::output& out) { if (!in.is_empty()) { threshold = in["value"].get<float>(); } out["value"] = threshold; }; int rc = thing.start(); // connects and reconnects in its own thread if (rc < 0) { LOG_ERR("Cannot start the IOTMP client: %d", rc); return rc; } while (true) { k_sleep(K_MINUTES(5)); // store a reading in a Data Bucket every 5 minutes thinger::iotmp::json_t data; data["temperature"] = temperature; thing.write_bucket("environment_data", data); } return 0; }YOUR_TOKENis the device credentials, not an access token.- Without
set_host(), the client connects to the free Cloud server. - The client does not configure the network: Zephyr brings it up (DHCP in this
prj.conf) and the client connects when it is ready. - Create the
environment_dataData Bucket in Data Buckets with From Device Write Call as Data Source before the first write.
Add the server certificate
Over TLS, the client checks the server certificate against a CA certificate that your application registers. Register it with the Zephyr TLS credentials API before
thing.start(), and pass the tag to the client:cpp#include <zephyr/net/tls_credentials.h> #define CA_TAG 1 static const unsigned char ca_cert[] = "-----BEGIN CERTIFICATE-----\n" "...\n" "-----END CERTIFICATE-----\n"; // in main(), before thing.start() tls_credential_add(CA_TAG, TLS_CREDENTIAL_CA_CERTIFICATE, ca_cert, sizeof(ca_cert)); thing.set_tls_tag(CA_TAG);Use the root certificate of the authority that signs your server certificate.
Build and flash
bashwest build -b YOUR_BOARD path/to/app west flashYOUR_BOARDis the Zephyr name of your board.Check the connection
Go to Devices: the device shows a green status dot. Open it and go to API to call
environment,ledandthreshold. You can also runthinger device resource YOUR_DEVICE environment.
Result
The board is online in Devices, its device resources answer from the console and the API, and the environment_data Data Bucket gets a new reading every 5 minutes.
Connection and reconnection
thing.start() runs the client in its own thread, named iotmp, and returns at once (a negative value is an error). The thread connects, authenticates and reconnects on its own: after a failed attempt it waits 5 seconds, and doubles the wait on each new failure up to 60 seconds. A successful connection resets the wait. Change it with thing.set_reconnect_timeout(base_ms, max_ms).
thing.set_state_callback() receives the state changes (SOCKET_CONNECTING, AUTHENTICATED, READY, DISCONNECTED, AUTH_FAILED…). thing.write_bucket(), thing.set_property(), thing.get_property(), thing.call_endpoint() and thing.stream() are safe from any thread. See the Arduino coding guide for what each one does.
Configuration
| Option | Default | Description |
|---|---|---|
CONFIG_THINGER_IOTMP | — | Enables the client. Needs NET_SOCKETS, CPP and STD_CPP17. |
CONFIG_THINGER_IOTMP_TLS | y | Connects over TLS. Needs NET_SOCKETS_SOCKOPT_TLS and MBEDTLS. |
CONFIG_THINGER_IOTMP_STACK_SIZE | 4096 | Stack size of the client thread, in bytes. |
CONFIG_THINGER_IOTMP_PRIORITY | 7 | Priority of the client thread. |
CONFIG_THINGER_IOTMP_LOG_LEVEL_DBG | — | Prints every step of the connection to the log. Use it while you test. |
The client connects to port 25206, the TLS port. Keep TLS enabled: without it, set the plain TCP port with thing.set_host("YOUR_SERVER", 25204).