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.io server 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.io on port 8243;
  • 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

  1. Open Sketch > Include Library > Manage Libraries.
  2. Search for MicroedenConnect.
  3. Select the library and click Install.
  4. Allow the IDE to install PubSubClient and ArduinoJson when prompted.
  5. 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:

  1. installs the bundled MicroEden ISRG Root X1 CA through setCACert();
  2. 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 from loop();
  • outbound TCP port 8243 is 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.