A complete IoT temperature monitoring system in Go demonstrating MQTT publish/subscribe messaging with an embedded broker, a sensor publisher, and a dashboard subscriber.
┌──────────────┐
│ Broker │
│ (mochi-mqtt)│
│ :1883 │
└──┬───────┬──┘
│ │
PUBLISH │ │ SUBSCRIBE
│ │ sensors/greenhouse/#
┌─────┘ └─────┐
│ │
┌──────┴──────┐ ┌──────┴──────┐
│ Sensor │ │ Dashboard │
│ (publisher) │ │(subscriber) │
└─────────────┘ └─────────────┘
code/
├── broker/
│ └── main.go # Embedded MQTT broker
├── sensor/
│ └── main.go # Temperature sensor (publisher)
├── dashboard/
│ └── main.go # Monitoring dashboard (subscriber)
├── go.mod
└── go.sum
- Go 1.25+
- mochi-mqtt -- Embeddable MQTT broker written in Go
- paho.mqtt.golang -- Eclipse MQTT client library for Go
Start each component in a separate terminal. The broker must be started first.
1. Start the broker
go run ./brokerThe broker listens on port 1883 (standard MQTT port).
2. Start the dashboard (subscriber)
go run ./dashboardSubscribes to sensors/greenhouse/# using a multi-level wildcard, capturing both temperature readings and status updates.
3. Start the sensor (publisher)
go run ./sensorPublishes a random temperature (20-30 C) to sensors/greenhouse/temperature every 2 seconds with QoS 1 and the retain flag set.
The sensor publishes to topics, the dashboard subscribes. They never communicate directly -- the broker handles all routing.
The sensor publishes with QoS 1. The broker acknowledges each message with a PUBACK. If the ACK is lost, the client library retries automatically.
The sensor publishes with retain: true. The broker stores the latest message on each topic. When a new dashboard connects (or reconnects), it immediately receives the last known temperature -- no waiting for the next publish cycle.
Try it: stop the dashboard, let the sensor publish a few readings, then restart the dashboard. It gets the latest value instantly.
The dashboard subscribes to sensors/greenhouse/#. The # wildcard matches all topics under that prefix -- both sensors/greenhouse/temperature and sensors/greenhouse/status with a single subscription.
The sensor registers a Last Will during CONNECT: if its TCP connection drops without a clean DISCONNECT packet, the broker publishes "offline" to sensors/greenhouse/status.
Demo:
# Kill the sensor without a clean shutdown
kill -9 $(pgrep -f "sensor/main.go")The dashboard prints [ALERT] Sensor went OFFLINE! -- automatic offline detection with zero health-check code.
Note: Ctrl+C sends SIGINT, which triggers a clean shutdown (DISCONNECT packet). The broker knows the disconnect was intentional and does not fire the Last Will. Use kill -9 to simulate a crash.
| Topic | Publisher | Payload | QoS | Retain |
|---|---|---|---|---|
sensors/greenhouse/temperature |
sensor | Temperature in Celsius (e.g. 23.5) |
1 | Yes |
sensors/greenhouse/status |
sensor / broker (LWT) | online or offline |
1 | Yes |
Source code for the YouTube video MQTT: The Protocol Behind Every Smart Device.