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:

FieldValue
Servermqtt.opensense.murzin.digital:8883
Client ID prefixos-{acc_id}-
Username(leave empty)
TLSenabled
Client certthe cert from the bundle
Client keythe key from the bundle
CAthe CA from the bundle
Topic mappinglocal: local/sensors/# → remote: opensense/acc_4f3c/#

Mosquitto bridge config

$ /etc/mosquitto/conf.d/opensense-bridge.conf

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 to offline.

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.