Arduino IDE
Library Manager
Search the Library Manager and click Install — the dependencies come with it.
Nodrix An optional Arduino library for ESP32 and ESP8266. It hides WiFi, TLS, the WebSocket/HTTP transport, JSON, and the nodrix control/telemetry protocol behind a tiny API — so a sketch is mostly your own device logic, not plumbing.
Pick a channel. It's in the Arduino IDE Library Manager, the PlatformIO registry, and the ESP Component Registry, or you can pull it straight from GitHub. The Arduino and PlatformIO channels bring ArduinoJson (v7) and WebSockets by Markus Sattler along with it.
Library Manager
Search the Library Manager and click Install — the dependencies come with it.
Nodrix Registry
Add one line to your platformio.ini:
lib_deps = decoded-cipher/Nodrix Component Registry
For ESP-IDF projects with arduino-esp32, add it from the ESP Component Registry:
idf.py add-dependency "decoded-cipher/nodrix" Source
Or pin any tagged release straight from the repo:
lib_deps = https://github.com/decoded-cipher/nodrix-sdk.git#v0.1.0 Then pull it into your sketch with a single header:
#include <Nodrix.h>
Hand-written, driving one LED from the cloud is about ninety lines of WiFi, TLS, JSON parsing,
acking, and reconnect handling. With the library it's this — bind a dashboard toggle to
led and it works end to end:
#include <Nodrix.h>
#include "secret.h"
const int LED_PIN = 2;
NODRIX_WRITE("led") { // cloud writes "led" -> this runs
digitalWrite(LED_PIN, value.asBool() ? HIGH : LOW);
Nodrix.send("led", value.asBool()); // report real state back
}
void setup() {
pinMode(LED_PIN, OUTPUT);
Nodrix.begin(WIFI_SSID, WIFI_PASS, HOST, TOKEN);
}
void loop() {
Nodrix.run();
} Everything below is what the library does underneath.
Everything that isn't specific to your project — the transport, the protocol, and its sharp edges — lives below the API.
NODRIX_WRITE("var") { … } runs when the cloud writes a variable. Self-registering — no dispatch table, no wiring in setup().
value coerces the wire type, so a boolean toggle, a numeric slider, and a string select all just work — no fragile string matching.
Nodrix.send() stages a metric; the library coalesces many into one frame and enforces the wire limits before sending.
An always-on WebSocket for instant control, or HTTP polling for battery deep-sleep nodes. Same handlers, one-line switch.
The library acks every write so the cloud stops re-sending it, and nothing is lost across a nap or a WiFi blip.
It seeds each handled variable so the cloud will queue writes to it, and re-echoes real state on reconnect instead of clobbering it.
addAP() registers fallbacks; the strongest reachable one is used and the device fails over between them mid-session.
The socket reconnects on its own and rides protocol heartbeats, so a silent link gets noticed instead of hanging.
Encrypted-but-unverified by default for a fast first run; pin a root CA (ESP32) or a fingerprint (ESP8266) when you ship.
NODRIX_WRITE("var") { … }
registers a handler for a variable. When a dashboard widget, an automation, or the API writes
it, the block runs with a value
in scope. Registration happens before setup(),
so a handler is a single self-contained block anywhere in the sketch — no dispatch table.
The value is a
NodrixValue that
coerces the wire type, so a toggle sending true,
a slider sending 42,
and a select sending "on" all just work:
| Method | Returns |
|---|---|
value.asBool() | true for true, any non-zero number, or "on"/"1"/"true"/"yes" |
value.asInt() / asLong() | integer (parses numeric strings) |
value.asFloat() / asDouble() | number |
value.asString() | the raw string |
value.isNull() | no value |
Acknowledgements are automatic — the library acks every write it delivers, so the cloud stops re-sending it. Because delivery is at-least-once, a handler can run more than once for the same command across a reconnect, so keep it idempotent. Setting a pin to a definite state is naturally safe.
send() reports a
reading; overloads cover every common type.
Nodrix.send("temperature", 22.5);
Nodrix.send("online", true);
Nodrix.send("status", "ok");
Calls stage a metric rather than transmitting immediately; the library coalesces
them into a single frame on the next run()
(WebSocket) or flush()/poll()
(HTTP), so ten calls become one payload. The buffer also enforces the wire limits — it batches
within the per-frame cap, drops over-long keys, and clamps oversized strings — so one bad reading
can't get the whole batch rejected.
A device lives one of two ways, and the library matches both without changing your handlers — a
one-line switch between begin() and
beginHTTP().
| begin() — WebSocket | beginHTTP() — polling | |
|---|---|---|
| Latency | instant | your poll interval |
| Connection | one socket held open | none; one request per check |
| Best for | always-on controllers | battery / deep-sleep nodes |
| Loop call | run() every loop() | poll() on each wake |
| Idle cost | ~zero (Cloudflare hibernates it) | one request per interval |
HTTP wake cycle
void setup() {
Nodrix.beginHTTP(WIFI_SSID, WIFI_PASS, HOST, TOKEN);
Nodrix.send("soil", analogRead(34));
Nodrix.flush(); // POST telemetry
Nodrix.poll(); // fetch + apply queued control, then ack
// ... then esp_deep_sleep_start()
} The protocol's sharp edges are the reason to use the library rather than hand-roll it — and they all live below the API.
A control write stays queued until it's acknowledged and re-delivers on the next connect or poll, so nothing is lost across a nap or a WiFi blip.
The library acknowledges every write it hands your code, so the cloud stops re-sending it. Forgetting the ack is the most common hand-rolled bug.
The cloud only queues writes to a variable it has seen. The library seeds each registered handler on connect, and re-echoes the last known state on reconnect rather than clobbering it.
On the WebSocket it reconnects on its own and rides protocol heartbeats for liveness; run() and poll() also re-run WiFi failover if the connection drops.
By default the library connects encrypted but unverified — the shortest path to a first working
device. To pin, call setCACert(pem)
(ESP32, WebSocket and HTTP) or setFingerprint(fp)
(ESP8266, HTTP) before begin().
Pin the root CA, not the leaf — the leaf rotates every ~90 days, the root does not.
Your host serves its root as the last block of the chain:
openssl s_client -showcerts -servername yourproject.workers.dev \
-connect yourproject.workers.dev:443 </dev/null 2>/dev/null \
| awk '/BEGIN CERTIFICATE/{c++; b=""} {b=b $0 ORS} /END CERTIFICATE/{last=b} END{printf "\n%s", last}'
For Cloudflare *.workers.dev
that returns GTS Root R4. One limitation: on ESP8266 the WebSocket transport is always unvalidated —
use an ESP32, or ESP8266 in HTTP mode with a fingerprint.
Any ESP32 — the original plus the S2, S3, C3, C6, and H2 variants — or ESP8266. That covers common DevKits, the XIAO ESP32-C3/S3, Arduino Nano ESP32, Seeed ESP32 boards, NodeMCU, and the Wemos D1 mini.
Boards that reach WiFi through a coprocessor (Raspberry Pi Pico W, Arduino UNO R4 WiFi, WiFiNINA) and cellular modules aren't supported yet — point those at nodrix over plain HTTPS instead.
Five runnable sketches ship with the repository. The two DHT11 examples also need the DHT sensor library by Adafruit.
LedControl Toggle the on-board LED from a dashboard.
HomeLights Two independently controlled lights or relays.
MultiWiFi Connect through several networks with failover.
SensorTelemetry DHT11 temperature/humidity over a cert-pinned socket.
DeepSleepSensor HTTP mode: read a DHT11, report, apply control, deep sleep.
addAP(ssid, pass) | Register a WiFi network before begin() |
begin(ssid, pass, host, token[, port]) | Connect over WebSocket |
begin(host, token[, port]) | WebSocket, using the addAP() networks |
beginHTTP(ssid, pass, host, token[, port]) | HTTP mode |
beginHTTP(host, token[, port]) | HTTP mode, using the addAP() networks |
run() | WebSocket: pump the socket and flush telemetry; call every loop() |
poll() | HTTP: send readings, fetch and apply control; call on wake |
send(var, value) | Stage a metric (bool/int/long/float/double/const char*/String) |
flush() | Transmit staged metrics now |
event(name) / event(name, payload) | Fire a server-side event |
connected() | Whether the link is up |
onConnect(cb) / onDisconnect(cb) | Connection callbacks |
setInsecure() | Skip certificate validation (default) |
setCACert(pem) | Pin a root CA (ESP32) |
setFingerprint(fp) | Pin a SHA-1 fingerprint (ESP8266 HTTP) |
setDebug(on) | Log connection and protocol activity to Serial |
NODRIX_WRITE("var") { … } | Handle a cloud write; value is in scope |
Full configuration — a secret.h
for credentials and addAP()
for multiple networks:
#define WIFI_SSID "your-wifi"
#define WIFI_PASS "your-password"
#define HOST "yourproject.workers.dev" // Nodrix host, no https://
#define TOKEN "your-project-token" // Settings -> Tokens Nodrix.addAP("home-ssid", "home-pass");
Nodrix.addAP("workshop-ssid", "workshop-pass");
Nodrix.begin(HOST, TOKEN); // no ssid/pass — uses the list above Every ESP32 — the original plus the S2, S3, C3, C6, and H2 variants — and ESP8266. That covers common DevKits, the XIAO ESP32-C3/S3, Arduino Nano ESP32, Seeed ESP32 boards, NodeMCU, and Wemos D1 mini. Boards that reach WiFi through a coprocessor (Raspberry Pi Pico W, Arduino UNO R4 WiFi, WiFiNINA) and cellular modules aren’t supported yet — those talk to nodrix over plain HTTPS instead.
In the Arduino IDE, search "Nodrix" in the Library Manager and install it (it pulls in ArduinoJson and WebSockets). In PlatformIO, add decoded-cipher/Nodrix to lib_deps. Then include it with #include <Nodrix.h>.
No. The device protocol is plain HTTPS and WebSocket with JSON, so any board or language can talk to nodrix directly. The library is optional — it exists to remove the WiFi, TLS, JSON, ack, and reconnect boilerplate on ESP32/ESP8266, where hand-writing it is the most painful.
Use the always-on WebSocket (Nodrix.begin) for mains-powered controllers that need instant control — the socket hibernates on Cloudflare, so an idle connection costs almost nothing. Use HTTP polling (Nodrix.beginHTTP, then Nodrix.poll() each wake) for battery devices that wake, report, and deep-sleep. The same NODRIX_WRITE handlers work in both modes.
It waits. Control delivery is at-least-once: the cloud holds a write until the device acknowledges it and re-delivers anything outstanding on the next connect or poll. The library acks every write it delivers, reconnects on its own, and seeds control variables so the cloud will queue writes to them — so a command sent during a nap still lands.
By default the library connects encrypted but unverified — the fastest path to a first working device. To pin, call Nodrix.setCACert(pem) before begin() on ESP32 (WebSocket and HTTP), or Nodrix.setFingerprint(fp) on ESP8266 in HTTP mode. The repo README includes the openssl command to fetch your host’s root CA.
Nodrix is free and open source — deploy it to your own Cloudflare account, then point an ESP32 at it with this library.
One-click deploy provisions everything into your own Cloudflare account — nothing leaves it.