MQTT 物联网通信:100 个传感器的数据怎么传到云端?
上一篇 ESP32 固件写好了,但数据的另一端------云端消息中间件怎么搭?这篇从零部署 EMQX,设计 topic 规范和 JSON 格式,加上设备上下线监控和断网重连,一套生产可用的物联网通信就出来了。
为什么是 MQTT + EMQX?
物联网通信有几个硬需求:
- 低带宽:农田里 4G 信号可能只有两格,包要小
- 低功耗:设备电池供电,不能维持长连接轮询
- 双向:不只是上报数据,还要下发指令(开阀、关泵)
- 大规模:大棚多了,上百个设备要同时在线
MQTT 的发布/订阅模型天生契合:设备 publish 数据到 broker,后端 subscribe 接收存储,控制指令反向 subscribe + publish。EMQX 5.x 开源版免费,单节点能扛 10 万连接,对我们来说完全够用。
EMQX 部署------一条 Docker 命令
bash
docker run -d \
--name emqx \
--restart unless-stopped \
-p 1883:1883 \ # MQTT 端口
-p 8083:8083 \ # WebSocket 端口(小程序用)
-p 18083:18083 \ # Dashboard 管理界面
emqx/emqx:5.7.0
访问 http://你的服务器IP:18083,默认账号 admin / public。进去后第一件事:改密码 ,并在「访问控制 → 认证」里创建一个 MQTT 用户(比如 farm_devices / 一个复杂密码),所有 ESP32 用这个用户名密码连接。
Topic 设计------房子的门牌号
MQTT 没有数据库,topic 就是唯一标识。好的 topic 设计让你查问题一目了然,坏的 topic 设计让你半年后对着 Dashboard 一脸懵。
规范
{业务域}/{场地}/{设备类型}/{设备ID}/{消息类型}
拆解:
| 层级 | 示例值 | 含义 |
|---|---|---|
| 业务域 | farm |
农场业务,区别于以后可能的 home factory |
| 场地 | greenhouse_01 |
大棚编号 |
| 设备类型 | sensor / actuator / camera |
设备角色 |
| 设备ID | esp32_a1b2c3 |
唯一设备标识(可以用 MAC 地址后 6 位) |
| 消息类型 | data / status / cmd / alarm |
消息用途 |
完整示例
farm/greenhouse_01/sensor/esp32_a1b2c3/data ← 设备上报数据(JSON)
farm/greenhouse_01/sensor/esp32_a1b2c3/status ← 设备状态(在线/电池/信号)
farm/greenhouse_01/actuator/pump_01/cmd ← 控制指令(平台→设备)
farm/greenhouse_01/sensor/+/data ← 通配符:该棚所有传感器数据
farm/+/+/+/alarm ← 通配符:整个农场所有告警
JSON 数据格式------统一方言
每个 sensor 上报的 data JSON 必须统一字段名,不然后端解析要写一堆 if-else。
传感器数据上报(设备 → 平台):
json
{
"dev": "esp32_a1b2c3",
"ts": 1718000000,
"ver": "1.0.0",
"data": {
"air_temp": 26.5,
"air_humidity": 68.2,
"soil_temp": 22.1,
"soil_moisture": 35.0,
"light": 42000
},
"battery": 3.82,
"rssi": -65
}
字段说明:
dev:设备 ID,必须与 topic 中一致,便于日志追踪ts:Unix 时间戳(秒),ESP32 从 NTP 同步ver:固件版本,OTA 升级时判断data:传感器读数,key 固定battery:电池电压(V),< 3.3V 告警rssi:WiFi 信号强度
控制指令下发(平台 → 设备):
json
{
"cmd": "irrigate",
"seq": 1823,
"params": {
"duration": 300,
"valve": 1
}
}
seq 是命令序列号,递增。设备收到后回一个 ACK(带相同 seq),平台就知道这条命令确实送达了。没有 ACK 机制的命令下发就是丢硬币------丢了也不知道。
ACK 回复(设备 → 平台):
json
{
"type": "ack",
"seq": 1823,
"result": "ok",
"msg": ""
}
设备订阅 farm/greenhouse_01/actuator/+/cmd,平台 publish 到具体设备的 cmd topic。设备收到后执行,执行完 publish ACK 到 .../ack。
设备上下线管理------谁在线、谁掉线了
MQTT 有个开箱即用的好东西:遗嘱消息(Last Will)。
设备连接时声明一个遗嘱 topic,一旦异常断线(心跳超时),broker 自动帮你发布一条遗嘱消息。
ESP32 端(连接时设置):
cpp
mqtt.connect(
MQTT_CLIENT_ID,
MQTT_USER, MQTT_PASS,
"farm/greenhouse_01/sensor/esp32_a1b2c3/status", // 遗嘱 topic
1, // QoS
true, // retain
"{\"online\":false,\"ts\":1718000000}" // 遗嘱 payload
);
设备上线时,自己 publish 一条 {"online":true},并设置 retain=true(新订阅者也能立刻拿到最新状态)。
后端监听(Spring Boot 侧):
后端订阅 farm/+/+/+/status,收到消息后更新设备在线状态到 MySQL,触发告警逻辑:
java
@MqttListener(topic = "farm/+/+/+/status")
public void onDeviceStatus(String topic, String payload) {
DeviceStatus status = JSON.parseObject(payload, DeviceStatus.class);
deviceService.updateOnlineStatus(status.getDev(), status.isOnline());
if (!status.isOnline()) {
alarmService.trigger("设备离线: " + status.getDev());
}
}
断网重连------农村的真实日常
农村 WiFi 断网三件套:下雨、打雷、光猫被拔(要插电饭锅)。ESP32 的断网重连逻辑必须健壮。
cpp
// 带指数退避的重连
void connectMQTT() {
int retry = 0;
int delayMs = 1000;
while (!mqtt.connected()) {
Serial.printf("MQTT 连接中 (第%d次)...", retry + 1);
if (mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS,
statusTopic, 1, true, willMsg)) {
Serial.println("成功");
mqtt.publish(statusTopic, "{\"online\":true}", true);
mqtt.subscribe(cmdTopic); // 重新订阅控制指令
return;
}
Serial.printf("失败, rc=%d\n", mqtt.state());
retry++;
if (retry <= 5) {
delayMs = 1000 * (1 << retry); // 1s, 2s, 4s, 8s, 16s
} else {
delayMs = 60000; // 5 次后每 60s 重试
}
delay(delayMs);
}
}
加上本地缓存------MQTT 连不上时把数据存到 SPIFFS 文件,网络恢复后补传:
cpp
void loop() {
SensorData data = collectAll();
if (mqtt.connected()) {
// 先补传本地缓存
flushLocalCache();
// 再发当前数据
publishData(data);
} else {
// 存到本地文件
appendToLocalCache(data);
}
delay(300000);
}
flushLocalCache() 和 appendToLocalCache() 用 ESP32 的 SPIFFS 或 LittleFS 实现,逻辑不复杂:每条数据一行 JSON,补传时逐行读取 publish,发完清空文件。
带宽和流量估算
一条 data JSON 大约 120 字节,MQTT 协议头约 10 字节,TCP/IP 头约 40 字节,总计约 170 字节/条。5 分钟一条,一天 288 条:约 48KB/天,1.4MB/月。 加上心跳 + ACK,一个设备一个月不超过 3MB。100 个设备也就 300MB/月------即使插个 4G 流量卡,10 块钱的物联网套餐都够用。
快速验证
装好 EMQX 后,开一个 MQTT 客户端工具(MQTTX 好用,免费),连上 broker,订阅 farm/#。然后用 ESP32 烧录上篇代码,你应该立刻在工具里看到数据涌入。
farm/greenhouse_01/sensor/esp32_a1b2c3/data → {"dev":"esp32...","ts":17180...}
farm/greenhouse_01/sensor/esp32_a1b2c3/status → {"online":true}
如果看不到,排查顺序:ESP32 WiFi 连上了吗 → MQTT 密码对了吗 → 防火墙 1883 端口开了吗 → topic 拼写有误吗。
下一篇:《计算机视觉在农业的应用:作物识别 + 病虫害检测实战》------用 YOLOv8 训练一个能认出 10 种蔬菜 + 20 种病害的模型,部署到 RK3588 上。