Arduino Library
MicroedenConnect library documentation
Download Link Arduino® Library : https://github.com/Microeden/MicroedenConnect
Overview
MicroedenConnect is an Arduino library that connects Arduino-compatible devices to the MicroEden platform through an MQTT over TLS connection. It provides a small, transport-independent API for:
- publishing arbitrary JSON key/value data;
- publishing typed cloud widgets;
- receiving and consuming commands from MicroEden;
- maintaining the MQTT connection and reconnecting automatically;
- validating the
microeden.ioserver certificate on ESP32 boards.
The public API is declared in src/MicroedenConnect.h. The implementation is
in src/MicroedenConnect.cpp.
The library does not own the Wi-Fi, Ethernet, or cellular connection. The
application creates the network client supplied by its board core or network
library and passes that client to MicroedenConnect::begin().
The connection service is designed to be called from a non-blocking main loop: it never waits forever for Wi-Fi or MQTT. Reconnect attempts are scheduled with backoff, stale sockets are closed before retrying, and applications can provide transport-specific callbacks when the selected network library needs to restart its lower-level interface.
Requirements
MicroedenConnect requires:
- an Arduino-compatible board and a network interface;
- a network client derived from Arduino
Client; - TLS support in that network client;
- an MQTT connection to
microeden.ioon port8243; - a MicroEden device identifier and device access token.
The library declares these Arduino Library Manager dependencies:
Install the board core, the board's network library, and these dependencies before compiling an example.
Installation
Arduino IDE Library Manager
- Open Sketch > Include Library > Manage Libraries.
- Search for
MicroedenConnect. - Select the library and click Install.
- Allow the IDE to install
PubSubClientandArduinoJsonwhen prompted. - Open an example from File > Examples > MicroedenConnect.
Manual installation
Download the repository as a ZIP archive, then select Sketch > Include
Library > Add .ZIP Library in the Arduino IDE. Alternatively, extract the
library folder into the sketchbook libraries directory. Restart the IDE after
copying a library manually if it does not appear in the examples menu.
Supported boards and network transports
The library metadata intentionally declares architectures=*. The source code
uses the standard Arduino Client interface and is not restricted to a list of
board architectures. Actual compatibility depends on the selected board and
network library providing a TLS-capable Client implementation.
Common configurations include:
| Board or transport | Network client | MicroedenConnect overload |
|---|---|---|
| Arduino Nano ESP32 | WiFiClientSecure |
ESP32 secure overload with automatic CA setup |
| ESP32 Dev Module and other ESP32 boards | WiFiClientSecure |
ESP32 secure overload with automatic CA setup |
| Arduino Nano 33 IoT | WiFiSSLClient from WiFiNINA |
Generic Client& overload |
| Arduino Nano RP2040 Connect | WiFiSSLClient from WiFiNINA |
Generic Client& overload |
| Arduino MKR WiFi 1010 | WiFiSSLClient from WiFiNINA |
Generic Client& overload |
| Arduino UNO WiFi Rev2 | WiFiSSLClient from WiFiNINA |
Generic Client& overload |
| Arduino UNO R4 WiFi | Secure client from WiFiS3 | Generic Client& overload |
| Portenta and Opta network variants | Board-specific secure client | Generic Client& overload |
| Ethernet boards | EthernetSSLClient or equivalent |
Generic Client& overload |
| GSM/LTE boards | GsmClientSecure, TinyGsmClientSecure, or equivalent |
Generic Client& overload |
| Arduino UNO Q | A compatible Client on the Arduino-programmable MCU |
Generic Client& overload |
For an ESP32 board, include WiFi.h and WiFiClientSecure.h. For every other
board, include the network library recommended by that board and pass its
secure client to the generic overload. A plain EthernetClient or non-secure
cellular client cannot be used with the TLS MQTT endpoint.
The UNO Q has both an Arduino-programmable microcontroller and a Linux MPU.
This library applies to sketches running on the Arduino-compatible MCU when a
compatible Client is available; applications running on the Linux side use
their own MQTT client stack.
Credentials and security
Each example contains a local microeden_secrets.h file with placeholders:
#define SECRET_WIFI_SSID "YOUR_WIFI_SSID"
#define SECRET_WIFI_PASSWORD "YOUR_WIFI_PASSWORD"
#define SECRET_DEVICE_ID "YOUR_DEVICE_ID"
#define SECRET_DEVICE_TOKEN "YOUR_DEVICE_TOKEN"
Replace the placeholders locally before compiling. Never commit real Wi-Fi passwords, device tokens, private keys, or other credentials. If a credential has ever been committed or shared, revoke it and generate a replacement even after the Git history has been cleaned.
ESP32 certificate validation
When an ESP32 WiFiClientSecure object is passed to the ESP32 overload of
begin(), the library automatically calls setCACert() with the public
Let's Encrypt ISRG Root X1 certificate bundled in
src/MicroedenRootCA.h. The connection therefore validates the server
certificate instead of using insecure TLS.
The CA certificate is public material and does not contain a password or device
credential. It is used to validate the certificate chain served by
microeden.io; it is not a client certificate and cannot authenticate a device.
On ESP32, run() requests NTP configuration asynchronously after Wi-Fi is
available and waits across later calls until the clock is valid. This avoids a
blocking time-synchronization loop in setup(). Applications that already have
their own time service can configure it instead:
#include <time.h>
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Other network clients
The generic begin(..., Client&) overload does not change certificate settings
because every network library exposes different TLS APIs. Configure the secure
client according to its board library before passing it to begin().
For example, a WiFiNINA WiFiSSLClient normally uses the certificate validation
provided by the WiFiNINA firmware. Ethernet and cellular libraries may require
their own CA configuration or certificate store. Do not replace a secure client
with an insecure client just to make the type compile.
For generic transports, register a fast network status callback when the board library can report Wi-Fi, Ethernet, or modem state before an MQTT socket exists. Register a separate reconnect callback that starts an asynchronous reconnect. Neither callback should wait in a loop or perform a long network operation.
MQTT connection
The library connects to the fixed public endpoint:
- host:
microeden.io; - TLS MQTT port:
8243.
For a device identifier DEVICE123, the library uses these internal values:
| Value | Format |
|---|---|
| MQTT client ID | MICROEDEN-MYDEVICE-DEVICE123 |
| Inbound topic | microeden/dot/DEVICE123/inbox |
| Outbound topic | microeden/dot/DEVICE123/outbox |
Applications normally do not need to create or subscribe to these topics
directly. begin(), run(), and the payload methods handle the normal flow.
Minimal ESP32 sketch
The following sketch connects a Nano ESP32 and publishes one text field. The
same pattern is used by the Basic example.
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <MicroedenConnect.h>
#include "microeden_secrets.h"
WiFiClientSecure net;
MicroedenConnect device;
unsigned long lastPublish = 0;
void setup() {
Serial.begin(115200);
// Both Wi-Fi association and ESP32 NTP synchronization continue
// asynchronously; no setup() loop is required.
WiFi.begin(SECRET_WIFI_SSID, SECRET_WIFI_PASSWORD);
// On ESP32 this overload installs the bundled MicroEden root CA.
device.begin(SECRET_DEVICE_ID, SECRET_DEVICE_TOKEN, net);
}
void loop() {
// Keep MQTT connected and process inbound messages.
device.run();
if (device.isConnected() && millis() - lastPublish >= 5000) {
lastPublish = millis();
device.writeKeyWord("message", "Hello from Arduino");
device.send();
}
}
begin() only configures the MQTT client. The first connection attempt occurs
when run() is called, so applications should call run() repeatedly from
loop().
Generic Client example
Boards that do not use the ESP32 secure client use the generic overload. The following example shows the WiFiNINA pattern used by boards such as the Nano 33 IoT and MKR WiFi 1010:
#include <WiFiNINA.h>
#include <WiFiSSLClient.h>
#include <MicroedenConnect.h>
#include "microeden_secrets.h"
WiFiSSLClient net;
MicroedenConnect device;
void setup() {
// Start Wi-Fi without blocking setup().
WiFi.begin(SECRET_WIFI_SSID, SECRET_WIFI_PASSWORD);
// Configure WiFiNINA certificate validation as required by the board. The
// status/reconnect callbacks shown in the next section are recommended for
// a production sketch.
device.begin(SECRET_DEVICE_ID, SECRET_DEVICE_TOKEN, net);
}
void loop() {
device.run();
}
The include names and secure client type vary by board. The important part is
that the object passed to begin() inherits from Arduino Client and performs
TLS.
Common lifecycle
Most sketches follow this pattern:
void setup() {
// 1. Start the network and synchronize time when required.
// 2. Create/configure a TLS-capable Client.
// 3. Call device.begin(deviceId, token, client).
}
void loop() {
device.run();
if (device.isConnected()) {
// Queue fields, then publish them when appropriate.
device.writeKeyWord("key", "value");
device.send();
}
}
Call run() as often as possible. It performs the following operations:
- attempts an MQTT reconnect when the session is disconnected;
- subscribes to the device inbox topic after a successful reconnect;
- processes MQTT keep-alive traffic;
- dispatches inbound JSON messages to the command and widget readers.
Avoid long blocking delays in loop(). When a periodic publish is required,
use millis() rather than delaying for the whole interval.
Only one MicroedenConnect instance should be used in a sketch. The MQTT
callback is stored through a static instance pointer, so constructing a second
instance replaces the callback target for the first one.
Reconnection and local responsiveness
run() is safe to call on every pass through loop(). It applies the following
connection policy:
- ESP32 Wi-Fi status is checked before MQTT; a lost station immediately forces MQTT and the underlying TLS socket into a disconnected state;
- ESP32 station auto-reconnect is enabled and
WiFi.reconnect()is scheduled at most once every ten seconds when no custom reconnect callback is supplied; - generic clients can report their lower-level state through
setNetworkStatusCallback(); - MQTT connection attempts are not made on every loop iteration;
- failed MQTT attempts use a 5, 10, 20, 40, then 60 second backoff;
- after three consecutive MQTT failures, the network reconnect callback is invoked so a stale Wi-Fi, Ethernet, or cellular session can be restarted;
PubSubClient::loop()failures immediately force a clean disconnect instead of waiting for the next publish;- the connection callback is notified only when the usable MQTT state changes.
The network reconnect callback must start the board's connection process and
return immediately. Do not implement it as a while (status != connected) loop:
that would block local buttons, sensors, displays, and other application logic.
Example for a WiFiNINA-style board:
WiFiSSLClient net;
MicroedenConnect device;
bool networkReady() {
return WiFi.status() == WL_CONNECTED;
}
void restartNetwork() {
if (WiFi.status() != WL_CONNECTED) {
WiFi.disconnect();
WiFi.begin(SECRET_WIFI_SSID, SECRET_WIFI_PASSWORD);
}
}
void connectionChanged(bool connected) {
Serial.println(connected ? "MicroEden connected" : "MicroEden disconnected");
}
void setup() {
WiFi.begin(SECRET_WIFI_SSID, SECRET_WIFI_PASSWORD);
device.setNetworkStatusCallback(networkReady);
device.setReconnectCallback(restartNetwork);
device.setConnectionCallback(connectionChanged);
device.begin(SECRET_DEVICE_ID, SECRET_DEVICE_TOKEN, net);
}
void loop() {
device.run();
// Local controls remain responsive while Wi-Fi or MQTT is reconnecting.
}
API reference
MicroedenConnect
MicroedenConnect()
Constructs an unconfigured client object. Construction does not start Wi-Fi,
TLS, or MQTT. Call begin() from setup() after the network client is ready.
void begin(const char* deviceId, const char* token, Client& netClient)
Configures MicroedenConnect with a generic Arduino Client.
Parameters:
deviceId: MicroEden device identifier;token: MicroEden device access token;netClient: TLS-capable network client used for MQTT traffic.
This overload is used by WiFiNINA, WiFiS3, Ethernet, GSM/LTE, and other board libraries. It does not configure the client's TLS certificate settings.
void setReconnectCallback(MicroedenReconnectCallback callback)
Registers a callback that starts a lower-level network reconnect. The callback is called at a controlled interval after a lost network link or repeated MQTT failures. It must return quickly and must not wait for the connection to finish.
On ESP32, WiFi.setAutoReconnect(true) and WiFi.reconnect() provide a default
recovery path when no callback is registered. Supplying a callback is useful when
the application needs to restart Wi-Fi explicitly.
void setNetworkStatusCallback(MicroedenNetworkStatusCallback callback)
Registers a callback that reports the lower-level network state for generic
transports. Return true only when the network interface is ready for a TCP/TLS
connection. The callback must be fast and side-effect free.
ESP32 sketches do not need this callback because the library reads
WiFi.status() directly.
void setConnectionCallback(MicroedenConnectionCallback callback)
Registers a callback invoked when the usable MQTT state changes. It receives
true only after MQTT has connected and the inbox subscription has succeeded;
it receives false when the session or its underlying network is forced down.
void begin(const char* deviceId, const char* token, WiFiClientSecure& netClient)
Available when compiling for the Arduino ESP32 architecture. This overload:
- installs the bundled MicroEden ISRG Root X1 CA through
setCACert(); - forwards the client to the generic
Client&implementation.
Use it with WiFiClientSecure on Nano ESP32, ESP32 Dev Module, and other ESP32
boards. begin() only configures the client; Wi-Fi association and NTP
synchronization may still be in progress. Call run() repeatedly from
loop() and the first TLS attempt will be deferred until the station and
system clock are ready.
void writeKeyWord(const char* key, const char* value)
Adds or replaces a string field in the outgoing JSON document.
void writeKeyWord(const char* key, double value)
Adds or replaces a double-precision numeric field.
void writeKeyWord(const char* key, float value)
Adds or replaces a floating-point field.
void writeKeyWord(const char* key, int value)
Adds or replaces an integer field.
void writeKeyWord(const char* key, bool value)
Adds or replaces a Boolean field.
void writeKeyWord(const char* key, const String& value)
Adds or replaces an Arduino String field.
All writeKeyWord() overloads modify the pending JSON document only. They do
not publish immediately. Multiple fields can be queued and sent together:
device.writeKeyWord("temperature", 23.4);
device.writeKeyWord("voltage", 3.30);
device.writeKeyWord("alarm", false);
if (!device.send()) {
Serial.println("MQTT publish failed");
}
template <typename T> T readKeyWord(const char* key = "content")
Reads a value from the most recently received inbound JSON document and converts
it to the requested type T.
String message = device.readKeyWord<String>();
int requestedLevel = device.readKeyWord<int>("level");
bool enabled = device.readKeyWord<bool>("enabled");
Read values only after run() has had an opportunity to process an inbound
message. If the key is missing or cannot be converted, ArduinoJson's normal
conversion behavior applies.
bool onCommand(const char* expectedCmd, const char* key = "content")
Checks the latest inbound JSON document. It returns true when the value under
key equals expectedCmd. A matching command is consumed by removing that key
from the inbound document, so the same command is not reported repeatedly.
if (device.onCommand("ledon")) {
digitalWrite(LED_BUILTIN, HIGH);
}
if (device.onCommand("setlevel", "command")) {
int level = device.readKeyWord<int>("value");
}
void run()
Maintains the MQTT session and processes inbound messages. Call it frequently
from loop() regardless of whether a publish is currently pending.
The method does not contain an infinite connection loop. It checks the network, performs at most one scheduled MQTT attempt, services the MQTT keep-alive when connected, and returns to the application. A failed attempt is delayed by an exponential backoff so local application code can continue running.
bool send()
Serializes and publishes the pending JSON document to the device outbox topic.
It returns true when the MQTT publish call succeeds and false when the
client is disconnected or publishing fails. The pending document is cleared
after a publish attempt, including a failed attempt.
Queue the fields again before retrying a failed publish.
bool isConnected()
Returns true only when MQTT is connected and the underlying network is still
usable. It does not start a connection attempt; call run() to reconnect.
void setBufferSize(uint16_t size)
Sets the maximum MQTT packet size accepted by PubSubClient. Call it before publishing larger JSON payloads or receiving larger command messages:
device.setBufferSize(512);
The required size depends on the complete MQTT packet, not only on the JSON text. Increasing the buffer also increases RAM usage.
Typed cloud widgets
Widget classes bind a cloud key to a MicroedenConnect instance. Each widget
inherits from CloudWidget<T> and provides typed write() and read() methods.
template <typename T> CloudWidget<T>
Construct a generic widget with a key and a client reference:
CloudWidget<int> counter("counter", device);
counter.write(10);
int current = counter.read();
write(value) queues the value in the outgoing JSON document. read() reads
the widget's key from the latest inbound JSON document. Call device.send() to
publish queued values.
Widget types
| Class | Value type | Typical use |
|---|---|---|
Level |
int |
Numeric level or percentage |
Slider |
int |
User-adjustable integer value |
Switch |
bool |
On/off control |
Pushbutton |
bool |
Momentary Boolean control |
Led |
bool |
Device status or LED state |
Photo |
String |
Text or image-related value |
Map |
String |
Coordinates or preformatted map value |
All typed widgets use the same constructor signature:
Level temperature("temperature", device);
Slider brightness("brightness", device);
Switch enabled("enabled", device);
Pushbutton trigger("trigger", device);
Led online("online", device);
Photo statusText("status", device);
Map position("position", device);
Map
Map has two write forms:
position.write(41.902782, 12.496366);
position.write(String("41.902782,12.496366"));
The latitude/longitude overload formats the value as six-decimal-degree coordinates separated by a comma.
Complete widget example
Level temperature("temperature", device);
Slider brightness("brightness", device);
Led online("online", device);
Photo statusText("status", device);
void publishValues() {
temperature.write(23);
brightness.write(75);
online.write(true);
statusText.write("Device is online");
device.writeKeyWord("voltage", 3.30);
device.writeKeyWord("alarm", false);
device.send();
}
All fields are published in one JSON payload. A later write using the same key replaces the pending value for that key.
Receiving commands
Commands are delivered by MQTT to the device inbox topic. The default command
field is content:
void loop() {
device.run();
if (!device.isConnected()) {
return;
}
if (device.onCommand("ledon")) {
digitalWrite(LED_BUILTIN, HIGH);
device.writeKeyWord("online", true);
device.send();
}
if (device.onCommand("ledoff")) {
digitalWrite(LED_BUILTIN, LOW);
device.writeKeyWord("online", false);
device.send();
}
}
For a custom command field, provide its name to onCommand() and use
readKeyWord<T>() for any additional values in the same inbound document:
if (device.onCommand("setlevel", "command")) {
int level = device.readKeyWord<int>("value");
brightness.write(level);
device.send();
}
Examples
The examples target an Arduino Nano ESP32 and use the ESP32 secure client. Each
example has its own microeden_secrets.h placeholder file so credentials stay
local to that example.
| Example | Demonstrates |
|---|---|
Basic |
Wi-Fi connection, MQTT lifecycle, and a text payload |
PublishWidgets |
Typed widgets, arbitrary fields, and buffer sizing |
Commands |
Receiving ledon and ledoff commands and publishing LED state |
MapWidget |
Publishing latitude and longitude through a map widget |
For another board, keep the application logic and replace the Wi-Fi/client
setup with the board's TLS-capable network client. Use the generic
begin(..., Client&) overload.
Troubleshooting
The compiler reports PinStatus or WiFiNINA errors on an ESP32
Do not use the WiFiNINA library for an ESP32 board. Select the ESP32 board core
and include WiFi.h plus WiFiClientSecure.h. WiFiNINA is intended for boards
whose network module is supported by the WiFiNINA firmware, such as the Nano
33 IoT and MKR WiFi 1010.
The MQTT connection never becomes active
Check all of the following:
- Wi-Fi, Ethernet, or cellular connectivity is established before
begin(); - the device identifier and token are correct;
- the selected network client supports TLS;
- the system clock is synchronized before an ESP32 TLS connection;
device.run()is called continuously fromloop();- outbound TCP port
8243is allowed by the network.
For a generic transport, register setNetworkStatusCallback() and
setReconnectCallback() so the library can avoid attempting MQTT while the
network interface is offline and can restart it after repeated failures.
TLS certificate validation fails on ESP32
Ensure the board has a valid time from NTP before calling begin(). Do not call
setInsecure(). The ESP32 overload automatically installs the bundled
MicroEden root CA; use the overload that accepts WiFiClientSecure& rather than
casting the client manually.
TLS fails on a non-ESP32 board
Use the secure client class supplied by the board's network library. The generic overload cannot configure certificates for every platform. Verify the board's CA store, certificate configuration, and clock requirements in that network library's documentation.
send() returns false
send() returns false when MQTT is disconnected or the publish operation
fails. Call run() until isConnected() is true, then queue the fields again
and retry. The pending JSON document is cleared after each publish attempt.
A large payload is rejected
Call setBufferSize() with a larger value before writing the payload. Keep in
mind that the MQTT packet includes protocol overhead and that larger buffers
consume more RAM.
A command is not detected
Call run() frequently, use the correct command field (content by default),
and pass the exact command string to onCommand(). A matching command is
consumed after onCommand() returns true.
License
MicroedenConnect is released under the MIT License. See the repository
LICENSE file for the complete terms.