DIY ESP32 + MQTT
Bring-your-own broker (EMQX / Mosquitto)
Same hardware as DIY ESP32 + ESPHome, different transport. Use this when:
- The site does not allow outbound HTTPS but does allow a single TCP connection to a local MQTT broker.
- You already operate an MQTT broker (Home Assistant, EMQX cluster, plant bus) and want OpenSense to subscribe to it as a tenant.
OpenSense exposes a multi-tenant MQTT bridge. Your devices publish to your broker (yours, your premises); OpenSense subscribes to a topic prefix via an outbound bridge from our broker side.
Topology
[ESP32] ──mqtt──▶ [your EMQX, local] ──bridge──▶ [OpenSense MQTT bridge] ──ingest──▶ [storage]
The bridge from your broker to ours uses an MQTT 5 outbound bridge with mTLS. Both EMQX and Mosquitto (1.6+) support this out of the box.
Provision in OpenSense
+ ADD DEVICE → MQTT → Bridge. The dialog shows:
- A bridge URL:
mqtts://mqtt.opensense.murzin.digital:8883. - A client certificate + private key for your broker. Download the bundle.
- A topic prefix you must publish under, e.g.
opensense/acc_4f3c/.
EMQX bridge config
In EMQX dashboard, Integration → Bridges → MQTT Bridge → Outbound:
| Field | Value |
|---|---|
| Server | mqtt.opensense.murzin.digital:8883 |
| Client ID prefix | os-{acc_id}- |
| Username | (leave empty) |
| TLS | enabled |
| Client cert | the cert from the bundle |
| Client key | the key from the bundle |
| CA | the CA from the bundle |
| Topic mapping | local: local/sensors/# → remote: opensense/acc_4f3c/# |
Mosquitto bridge config
connection opensense
address mqtt.opensense.murzin.digital:8883
bridge_protocol_version mqttv50
bridge_cafile /etc/mosquitto/opensense/ca.pem
bridge_certfile /etc/mosquitto/opensense/client.pem
bridge_keyfile /etc/mosquitto/opensense/client.key
bridge_insecure false
cleansession true
notifications false
start_type automatic
restart_timeout 10
local/sensors// → opensense/acc_4f3c//
topic # out 1 local/sensors/ opensense/acc_4f3c/
Device-side payload
Each device publishes a JSON message per measurement cycle to
local/sensors/<device_id> (with retain off, QoS 1):
{
"ts": "2026-05-17T08:22:00Z",
"measurements": [
{ "type": "temperature", "value": 4.2 },
{ "type": "humidity", "value": 64.1 }
]
}
ts is optional; if omitted, OpenSense stamps with the broker-receive
time, which is fine for cold-chain but adds ~200 ms jitter.
QoS, retain, LWT
- QoS 1. QoS 0 loses messages on bridge reconnect; QoS 2 is overhead the ingest pipeline does not need.
- Retain: off. OpenSense ingests measurements as a stream, not state. Retained messages would re-fire on each broker restart and create duplicate rows (which the idempotency layer would then deduplicate, but inefficiently).
- Last-will testament. Set on your device: topic
local/sensors/<device_id>/lwt, payload{"status":"offline"}, QoS 1, retain on. The bridge forwards this; OpenSense flips the device's connection state tooffline.
Why not direct device-to-OpenSense MQTT?
We support direct device connections at the Team tier, not Solo. The reason is operational: per-device certs, per-device ACLs, and bandwidth accounting against a multi-tenant broker are not free, and Solo is priced to assume an HTTP-first integration. If your devices number in the hundreds, contact us before scaling.