ESP32 firmware for a multi-channel industrial I/O device with MQTT connectivity, a Firebase-backed HTTP API, OTA updates, alert delivery, and a local web configuration portal.
- 4 analog input slots, currently configured for 0-10 V signals through a resistor divider
- 4 analog output slots driven by PWM and external 4-20 mA conditioning circuitry
- 4 digital inputs with edge-triggered alerts
- 4 digital outputs controlled through MQTT
- JSON-based logic engine with math, compare, timer, and I/O blocks
- MQTT/HTTPS transport support with certificate placeholders in
credentials.h - OTA updates with SHA-256 integrity checks
- Local web portal for Wi-Fi configuration and firmware upload
- Alert queue with retry persistence in non-volatile storage
- FreeRTOS task split for MQTT, telemetry, OTA, alerts, watchdog, and logic execution
| Component | Detail |
|---|---|
| MCU | ESP32 Dev Module (board = esp32dev in platformio.ini) |
| AI 0-3 | 0-10 V analog inputs on pins 35, 34, disabled, disabled using ADC1 and a 68K/33K divider |
| AO 0-3 | PWM outputs on pins 15, disabled, disabled, disabled using LEDC at 5 kHz and 8-bit resolution |
| DI 0-3 | Digital inputs on pins 36, 39, 32, 33 with INPUT_PULLDOWN |
| DO 0-3 | Digital outputs on pins 23, 19, 18, 5 |
This firmware drives analog outputs with PWM, so the hardware must include the required filtering and current-driver stage for a real 4-20 mA output.
Pin assignments and hardware constants are defined in include/hw_config.h. Set a channel pin to -1 to disable it.
- PlatformIO CLI or the VS Code PlatformIO extension
- ESP32 Dev Module or compatible hardware matching
include/hw_config.h - A Fluxio backend endpoint
- MQTT broker credentials returned by the backend
Copy the credential template:
cp include/credentials.example.h include/credentials.hEdit include/credentials.h with:
- Wi-Fi fallback access point credentials
- Default Wi-Fi station credentials
- Fluxio backend URL
- Shared backend API token
- MQTT root CA certificate placeholder
- OTA download root CA certificate placeholder
The current firmware uses setInsecure() for MQTT, backend HTTPS calls, and alert HTTPS calls. OTA also has client.setCACert(GCS_ROOT_CA) commented out. Fill the certificates and switch those clients back to setCACert(...) before treating TLS peer verification as enabled.
include/credentials.h is ignored by Git and must not be committed.
pio run
pio run --target upload
pio device monitorOn Windows, if pio is not on PATH, use:
& "$HOME\.platformio\penv\Scripts\pio.exe" run
& "$HOME\.platformio\penv\Scripts\pio.exe" run --target upload
& "$HOME\.platformio\penv\Scripts\pio.exe" device monitorThe serial monitor runs at 115200 baud.
To build and test without physical I/O hardware, uncomment #define IO_SIMULATION in include/hw_config.h. In simulation mode, analog and digital inputs are generated in software during updateIO().
On boot, the firmware:
- Initializes hardware and persistent state
- Connects to Wi-Fi, falling back to AP mode when station connection fails
- Fetches MQTT credentials from the backend
- Fetches channel configuration
- Starts MQTT, telemetry, alert, OTA, watchdog, and logic tasks
Main runtime modules:
lib/hal/- hardware abstraction and I/O updateslib/eeprom/- persistent parameter storagelib/webserver/- Wi-Fi and local configuration portallib/backend/- HTTP requests to the Fluxio backendlib/mqtt/- MQTT connection, subscriptions, and telemetry publishinglib/alerts/- channel limit detection and alert queue processinglib/logic/- block-based automation enginelib/ota/- HTTPS firmware download and validationlib/serial_test/- serial debug console and tests
Commands are published to device/<DEVICE_ID>/control.
| Payload | Action |
|---|---|
{"manual": true} or {"manual": false} |
Pause or resume the logic engine |
{"telemetry": true} or {"telemetry": false} |
Start or stop telemetry publishing |
{"do": {"index": 0, "value": 1}} |
Set a digital output |
{"ao": {"index": 0, "value": 50.0}} |
Set an analog output engineering value |
{"type": "logic", "blocks": [...]} |
Load and persist a logic program |
{"type": "logic_get"} |
Republish the saved logic program |
{"type": "ota", "url": "...", "sha256": "...", "size": 123} |
Trigger an OTA update |
Logic programs are JSON objects with a blocks array. Each block includes:
id- unique block identifiert- block type:0math,1compare,2timer,3I/Oop- operation code for the selected block typeio- I/O selector for I/O blocksin- input references or constants
Example: if AI0 is greater than 75.0, set DO0 to 1.
Available commands:
test run all unit tests
print print current IO state
do <idx> <0|1> set digital output
ao <idx> <val> set analog output
di <idx> <0|1> override digital input
ai <idx> <val> override analog input
help show this menu
The console is only available when IO_SIMULATION is not defined.
- Keep
include/credentials.hout of source control. - Rotate the shared backend API token if it is exposed.
- Keep TLS root CA certificates current for the MQTT broker and OTA download host.
- TLS peer verification is not currently enforced while clients use
setInsecure(). - OTA updates are accepted only when SHA-256 validation succeeds; TLS verification requires re-enabling
setCACert(...).
Add the project license before distributing firmware or source releases.
{ // Root message type: tells the firmware this payload is a logic program. "type": "logic", // Ordered list of blocks that form the program. "blocks": [ { "id": 10, "t": 3, "op": 0, "io": [0, 0] }, { "id": 0, "t": 1, "op": 1, "in": [[1, 10], [0, 75.0]] }, { "id": 1, "t": 3, "op": 1, "io": [3, 0], "in": [[1, 0]] } ] }