Connect over MQTT
MQTT transport lets CONDUYT devices communicate over WiFi through a message broker. The device and host don't need to be on the same machine or even the same network - they just need access to the same broker.
Prerequisites
- An ESP32 (or other WiFi-capable board)
- A running MQTT broker - see Set Up MQTT Broker for a Docker Compose setup with Mosquitto. If you don't have Docker, you can install Mosquitto directly:
# Ubuntu/Debian sudo apt install mosquitto mosquitto-clients # macOS (Homebrew) brew install mosquitto # Windows: download from https://mosquitto.org/download/ - Broker credentials (username/password) if authentication is enabled
Firmware
The firmware needs WiFi credentials and broker connection details. The device-001 string is the device ID - it determines the MQTT topic prefix for this device. The host must use the same device ID to find it.
#include <WiFi.h>
#define CONDUYT_TRANSPORT_MQTT
#include <Conduyt.h>
WiFiClient wifiClient;
ConduytMQTT transport(wifiClient, "192.168.1.100", 1883, "device-001");
ConduytDevice device("MQTTSensor", "1.0.0", transport);
void setup() {
Serial.begin(115200);
// Connect to WiFi
WiFi.begin("YourSSID", "YourPassword");
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" connected!");
Serial.println(WiFi.localIP());
// Set MQTT credentials (must match your broker user)
transport.setAuth("conduyt-device", "yourpassword");
device.begin();
}
void loop() {
device.poll();
}
Replace "192.168.1.100" with your broker's IP address, and "YourSSID" / "YourPassword" with your WiFi credentials.
What happens if setAuth() is not called? The transport connects without credentials. If your broker has allow_anonymous false, the connection will be rejected and device.poll() will silently retry.
Flash this to your ESP32:
; platformio.ini
[env:esp32]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps = conduyt
build_flags = -DCONDUYT_TRANSPORT_MQTT
pio run --target upload
Open the serial monitor to verify WiFi connection:
pio device monitor --baud 115200
# Connecting to WiFi... connected!
# 192.168.1.42
JavaScript host
npm install conduyt-js
Create mqtt-read.mjs:
// mqtt-read.mjs
import { ConduytDevice } from 'conduyt-js'
import { MQTTTransport } from 'conduyt-js/transports/mqtt'
const transport = new MQTTTransport({
broker: 'mqtt://192.168.1.100:1883',
deviceId: 'device-001', // must match the firmware's device ID
username: 'conduyt-host',
password: 'yourpassword'
})
const device = await ConduytDevice.connect(transport)
console.log('Firmware:', device.capabilities.firmwareName)
// Read analog pin
const value = await device.pin(0).read()
console.log('Pin 0 value:', value)
await device.disconnect()
node mqtt-read.mjs
Expected output:
Firmware: MQTTSensor
Pin 0 value: 512
Python host
pip install conduyt-py[mqtt]
Create mqtt-read.py:
# mqtt-read.py
import asyncio
from conduyt import ConduytDevice
from conduyt.transports.mqtt import MQTTTransport
async def main():
transport = MQTTTransport(
broker='192.168.1.100',
port=1883,
device_id='device-001', # must match the firmware's device ID
username='conduyt-host',
password='yourpassword'
)
device = ConduytDevice(transport)
hello = await device.connect()
print(f"Firmware: {hello.firmware_name}")
value = await device.pin(0).read()
print(f"Pin 0 value: {value}")
await device.disconnect()
asyncio.run(main())
python mqtt-read.py
Topic structure
All topics are prefixed with conduyt/{deviceId}/:
| Topic | Direction | Purpose |
|---|---|---|
conduyt/device-001/cmd/{typeHex} | Host → Device | Commands (e.g. cmd/11 = PIN_WRITE) |
conduyt/device-001/evt/{typeHex} | Device → Host | Events (e.g. evt/90 = PIN_EVENT) |
conduyt/device-001/hello | Device → Broker | HELLO_RESP binary payload, retained |
conduyt/device-001/status | Broker (LWT) | "online" or "offline", retained |
conduyt/device-001/ds/{name}/cmd | Host → Device | Write to a named datastream |
conduyt/device-001/ds/{name}/evt | Device → Host | Datastream value pushes |
QoS strategy
| Packet Type | QoS | Why |
|---|---|---|
| PIN_WRITE, PIN_MODE | 1 | Must arrive; writes are idempotent so redelivery is safe |
| PIN_EVENT, STREAM_DATA | 0 | High frequency; stale data is worthless |
| OTA_CHUNK | 2 | Firmware chunks must arrive exactly once |
| DS_EVENT | 1 | Important but idempotent |
| HELLO, HELLO_RESP | 1 | Connection handshake must complete |
Last Will and Testament
When the device connects to the broker, it registers an LWT:
- On connect: publishes
"online"toconduyt/{deviceId}/statuswithretain=true - On unexpected disconnect: the broker publishes
"offline"to the same topic
Host SDKs subscribe to the status topic and fire disconnect events automatically.
Framing
MQTT is message-oriented - each MQTT publish is one complete CONDUYT packet. No COBS framing is applied. This differs from serial and BLE, which are byte streams and need COBS to delimit packets.