IoT 系列第 1 篇。在《09_无线通信》里我们解决了"数据怎么飞出去",这一篇往上抬一层,看整张地图。
适用人群:玩过 STM32 或 ESP32、能读传感器、能连 Wi-Fi,但一被问"你这套东西架构是怎样的"就答不上来的同学。不需要服务器经验,Docker 命令我会从 0 给。
读完你能得到:
- 一张能默出来的端-边-云三层架构图,面试画在白板上不心虚;
- “为什么中间非要多一个网关”——四个能折算成钱或可靠性的理由;
- 常见软硬件的对号入座(STM32/ESP32/树莓派/EMQX/云平台各站哪一层);
- 一次完整数据流的六步走,知道你的字节过了几道手;
- 协议选型视角:MQTT / CoAP / HTTP / LwM2M 不是四选一,是各管一段;
- 半小时在本机跑起来的最小端边云(ESP32 + EMQX Docker + Node-RED)。
没看过无线通信那几篇也能懂的核心:传感器只负责"把物理量变成数字",数字要变成"别人能用的信息",中间还隔着链路、协议、网关、平台四层。本文讲的就是这四层怎么分工。
一、我曾经以为"IoT = ESP32 连 Wi-Fi 发个 HTTP"
大二那年我做了个"教室温湿度监测":ESP32 读 SHT30,每 10 秒 POST 一个 JSON 到我自己写的 Flask 接口,网页能看曲线。当时我觉得物联网不过如此。后来两个"看起来差不多"的需求把我打醒了。
第一个是学校试验田的土壤墒情。 田里没 Wi-Fi、没市电,要太阳能 + 电池活一学期。我原来的方案直接作废:一台设备开一个 HTTP 长连,光 TCP 握手的开销都比一包数据大。而且 20 个节点各自直连我的云服务器,20 条 TCP 连接 24 小时挂着——云是我自己的,钱包也是我自己的。
第二个是智慧路灯。 要求是"天黑自动亮、人走过加亮、断网了也要亮"。我第一反应是"云端写个规则呗",老师问我一句:校园网断了,你的路灯是不是就全瞎了? 我答不上来。这类"断网也要能工作"的需求,本质上是要求判断逻辑不在云上,而在路灯旁边。
这两个需求把我逼出了"设备直连云"的单一思维。真实的 IoT 系统里,设备和云之间几乎总是夹着一层东西,它叫边(Edge)。加上它之后,原本无解的问题突然有解了:
- 田里 20 个电池节点不用各自连云,用 BLE 或 LoRa 甩给田埂上那个有电有网的网关统一上行——省电、省流量、省连接数;
- 路灯的联动规则写在本地网关里,断网只是"上不了报",灯照样亮;
- 上行数据先在网关滤波、聚合、只传变化量,流量费直接降一个数量级。
我没有量产经验,这些是我在课设和啃文档过程中整理的。但"端-边-云"不是我发明的,它是行业通用语言——你去面试,白板上画出来的那张图应该跟本文图 1 同一个骨架。
二、前置概念表:先把这几个词对齐
| 端(Device / Node) | 真正接触物理世界的板子:采集数据或执行动作 | 大多数端节点压根不会直接上网,也上不起网 |
| 边(Edge) | 端和云之间的"中转 + 处理站"。有电、有网、算力比端强 | 它不是"转发器",是会思考的中转站 |
| 云(Cloud) | 远端服务器集群:存储、分析、下发、对接业务 | 长处是算力和全局视角,短处是离设备太远 |
| Broker | MQTT 的消息中转服务器,按主题转发 | 设备之间从不直连,都在跟 Broker 说话 |
| 网关(Gateway) | 协议转换器:把 BLE/Modbus/Zigbee 翻译成 MQTT/HTTP | 网关是边最常见的物理形态;"边"还包括网关里跑的逻辑 |
| 上行 / 下行 | 设备→云叫上行(遥测),云→设备叫下行(命令) | 上行求"稳",下行求"快" |
| 主题(Topic) | MQTT 里消息的地址,agri/gw01/telemetry,用 / 分层 | 主题设计好不好,决定以后加设备痛不痛苦 |
⚠️ 最容易搞混的一对:"边"是逻辑层,"网关"是具体设备。一个树莓派上跑 Mosquitto + Node-RED + 联动脚本,它既是网关(硬件),也是边(逻辑)。阿里云的"边缘计算实例"则是把边做成软件塞进别的机器——层还在,只是不再是一台看得见的盒子。
三、端-边-云三层全景图
[图 1] IoT 系统"端-边-云"三层全景(面试白板上画的那张)
┌──────────────────────────── 云 CLOUD ────────────────────────────┐
│ ┌─────────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ 物联网平台 │ │ 规则引擎 │ │ 业务应用 │ │
│ │ EMQX / 阿里云IoT │──>│ 阈值告警 │──>│ 大屏 / App / MES │ │
│ │ 华为云 / OneNET │ │ 数据清洗 │ │ 时序数据库 │ │
│ └─────────────────┘ └──────────────┘ └──────────────────┘ │
└────────┼─────────────────────────────────────────────────────────┘
│ MQTT over TLS:1883 / 8883
│ ↑ 这一段是广域网,会断、会慢、按流量收费
═════════╪══════════════════════════════════════════════════════════
│ 园区 / 现场局域网:通常免费、快,但地理范围有限
┌──────────────────────────── 边 EDGE ────────────────────────────┐
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 边缘网关(树莓派 / ESP32-S3 / 工控机 / 工业网关) │ │
│ │ ① 协议转换 BLE / Modbus / Zigbee / LoRa ⇄ MQTT │ │
│ │ ② 本地 Broker(可选)Mosquitto / EMQX,断网也能内部通信 │ │
│ │ ③ 边缘计算 滑动滤波、越限判断、聚合上报、单位换算 │ │
│ │ ④ 断网续传 本地队列 + SQLite / 文件缓存,联网后补传 │ │
│ │ ⑤ 本地联动 "温度>30 就开风扇",不依赖云 │ │
│ └───────────────────────────────────────────────────────────┘ │
└──────▲──────────────▲─────────────────────▲─────────────────────┘
│ │ │
BLE / Zigbee RS-485 / Modbus LoRa / 私有射频
┌──────┴───────┐ ┌────┴───────┐ ┌──────┴────────┐
│ 温湿度节点 │ │ 电表 / 水表 │ │ 土壤墒情节点 │
│ STM32+传感器 │ │ 工业仪表 │ │ 电池 + 太阳能 │
└──────────────┘ └────────────┘ └───────────────┘
端 DEVICES(数量多、资源少、大多不通 IP)
3.1 三层各自该干什么、不该干什么
判断一个功能放哪层,我的土办法是问三个问题:要不要全局信息?能不能容忍延迟?断网时还要不要工作?
| 端 | 采样、执行、本地安全联锁、低功耗休眠(Stop / Deep Sleep)、基础自检 | 复杂规则判断、长连接、TLS 握手、大数据缓存 | STM32(F1/F4/L4/U5 按功耗算力选)、ESP32-C3/S3(自带 Wi-Fi/BLE)、nRF52(纯 BLE) |
| 边 | 协议转换、聚合/滤波、本地联动、断网续传、本地告警、固件分发 | 跨站点全局分析、长期历史存储 | 树莓派/香橙派(Linux + Docker)、ESP32-S3(轻量网关)、工业网关(RS-485/DI/DO)、跑 Mosquitto / EMQX / Node-RED |
| 云 | 海量存储、跨设备关联分析、用户权限、OTA 版本管理、对接业务 | 毫秒级实时控制、单点高频联动 | 阿里云 IoT / 华为云 / OneNET(托管);自建:EMQX / Mosquitto + 时序库(TDengine/InfluxDB)+ 规则引擎 |
⚠️ 平台差异:同样叫"边",ESP32-S3 和树莓派不是一个量级。ESP32-S3 几百 KB RAM、无 MMU、跑 FreeRTOS,让它做"本地 Broker + 一周缓存"是想多了;树莓派是瓦级功耗、GB 级内存,能干的多得多但费电、贵、要管系统。选硬件前先算清楚"要缓存多少"“要不要跑容器”,别上来就上树莓派,也别硬扛。
💡 云端选型:托管平台的好处是"接入、鉴权、影子、OTA 都给你做好了",坏处是被绑死、按消息数收费、数据过别人手。自建全在自己手里、本地就能练手,代价是鉴权/高可用/监控自己扛。我的建议是先自建把 MQTT 搞明白,再学平台的封装——顺序反了,平台的"物模型"“影子”"Topic 类"你一个都看不懂。
四、为什么非要多一个"边":四个理由,逐个能算账
如果你以后只记得一句话,我希望是这句:边不是为了"多一层显得专业",它解决的都是能折算成钱或可靠性的问题。
4.1 省钱:带宽和云成本
假设一个大棚有 50 个节点,每 30 秒上报一次 100 字节:
方案 A:50 个节点各自直连云(每个都开 TCP + TLS)
每包实际开销 ≈ 100 B 载荷 + 40 B TCP/IP 头 + ~30 B MQTT 头 + TLS 记录头 ≈ 200 B 往上
每天:50 × 2880 × 200 B ≈ 28.8 MB/天 ≈ 864 MB/月;服务器维持 50 条 TLS 长连接
方案 B:节点走 BLE/LoRa 给网关,网关每 5 分钟聚合发一包(约 2 KB)
每天:288 × 2 KB ≈ 0.58 MB/天 ≈ 17 MB/月;服务器只维持 1 条连接
→ 流量降到约 1/50,连接数从 50 降到 1
数字是估算的,但数量级是对的:聚合 + 批量上传,是边缘层最直白的省钱方式。
4.2 省电:把最费电的活从电池节点身上拿走
《低功耗》那几篇的公式:平均电流 = Σ(各状态电流 × 停留时间) ÷ 周期。一次 MQTT 上报要 3~5 秒的高电流(含建连、TLS 握手),而一次 BLE 广播只要几十毫秒。把射频发射时长砍掉一两个数量级,续航直接翻倍。 所以我那个"发 HTTP"的 ESP32 教室节点用市电毫无问题,一换电池架构就得改:电池端只做短距离低功耗通信,联网的重活交给有市电的网关。
4.3 断网续传:最容易被忽视,也最容易在验收时翻车
我第一次做户外项目时写的是:采样 → 连 Wi-Fi → 发 HTTP → 失败就丢弃。只要那 5 分钟网络抖一下,数据就永久没了。验收时对方问我"昨天下午 3 点到 4 点的数据呢",我只能说"那会儿断网了"。
[图 2] 断网续传的基本结构
采样 ──> 打时间戳 ──> 本地环形队列(内存 + 掉电保存到 Flash/SQLite)
│
联网?───────┤
是 ─────────> 批量上行 → 收到确认 → 删除队列条目
否 ─────────> 继续攒,到上限就按策略丢最旧的
三个要点,每条都是踩过的:① 时间戳要在采样那一刻打,不是上传时打——否则断网一小时后补传的数据全挤在同一时刻,曲线是错的;② 队列要有上限,别把 Flash 写穿(和《17_存储》的写平衡、掉电保护是同一个问题);③ 补传要分批限速——断网两小时后一恢复灌几千条进去,轻则卡死自己,重则被 Broker 限流踢下线。
4.4 本地联动:呼应我在《项目实战_02》做的 BLE 网关
我在《项目实战_02:智能家居环境网关——ESP32 做 BLE 中心 + 本地联动》里干的事,用本篇的语言重述一遍:
[图 3] 同一个项目,两种架构
(a) 我最初的"云上联动"想法
传感器 ──Wi-Fi──> 云规则引擎 ──> 下发命令 ──> 执行器
断网 = 全瘫;每次联动绕一圈,延迟几百毫秒到几秒
(b) 实际做成的"本地联动"(边在起作用)
传感器 ──BLE──> ESP32 网关【规则:温度>28℃ 且 有人 → 开风扇】──> 执行器
└──MQTT──> 云(只负责记录和远程手动控制)
断网 = 只是上不了报,联动照常;延迟是毫秒级
这个经历让我彻底理解了"边":判断逻辑离被控对象越近,系统越可靠。 云是"全局视角和记忆力",不是"反射神经"——你不会指望大脑皮层负责膝跳反射。
五、数据是怎么流过去的:一次上行 + 一次下行
[图 4] 一次完整闭环,六个阶段
① 采集 传感器 → 数字量(I2C/SPI/ADC)
② 本地处理 标定、单位换算、滑动平均、剔除跳变、越限标记
③ 协议封装 组 JSON/CBOR,加设备 ID、时间戳、序号、校验
④ 上行 MQTT PUBLISH(QoS 1) → Wi-Fi/4G/LoRa → 网关 → Broker
⑤ 平台 解析、鉴权、入库、规则引擎判定、更新设备影子
⑥ 下行控制 PUBLISH 到 cmd 主题 → 设备校验执行 → 回报新状态(回到 ①)
关键:⑥ 执行完必须回报,形成闭环。否则 App 上永远是"已发送",
你不知道设备到底开没开——而这个"回报"本身就是一次上行。
⚠️ 第 ③ 步新手最常踩:把所有东西塞进一个主题。比如 device/data 里一会儿温度一会儿电量一会儿心跳,靠解析 payload 区分。后果是没法用主题过滤(云端规则写得又臭又长)、retained 消息也没法用。主题要按"用途"分层,不是按"设备"打包。
六、协议选型:不是四选一,是各管一段
这是我最想纠正的误解。教程常把 MQTT/CoAP/HTTP/LwM2M 摆一排让你"选一个",但真实架构里它们常常同时出现,只是在不同的段上。
[图 5] 协议是分段的,不是互斥的
端 ────────────> 边 ────────────> 云 ──────────> 业务
BLE GATT MQTT over TLS HTTPS / AMQP / Kafka
Modbus RTU (也可能直接 CoAP)数据库驱动
Zigbee / LoRa 私有帧
└─ 根本不是 IP 网络, └─ 这一段才是 └─ 跟嵌入式关系不大,
轮不到 MQTT MQTT 的主战场 是后端的事
所以听到"这设备用什么协议",第一个该问的是"从哪儿到哪儿"。
| MQTT | TCP(+TLS) | 固定头最小 2 B | 发布/订阅,长连接,支持推送 | 边↔云主力;遥测 + 下行命令;有市电设备 | 电池 + NB-IoT 的极低频上报(心跳本身就费电,见《无线_03》) |
| CoAP | UDP | 固定头 4 B | 请求/响应(像精简 HTTP) | 电池设备 + 极受限网络;NB-IoT/LoRa 直连 | 需要服务端主动推命令、对丢包零容忍的业务 |
| HTTP(S) | TCP | 文本头,动辄几百字节 | 请求/响应,短连接 | 偶尔取配置、下载固件、对接 Web 后端 | 高频遥测(头比载荷大);下行实时控制 |
| LwM2M | 通常在 CoAP 之上 | 同 CoAP | 面向"资源"(如 /3/0/1) | 设备管理:远程配置、固件升级、生命周期 | 当"数据上报协议"用——那不是它的主场 |
💡 一句话记忆:MQTT 管来回传数据,CoAP 管极省电地传一包,HTTP 管跟 Web 世界打交道,LwM2M 管管理设备本身。 成熟平台里这四个往往同时在跑。
6.1 主题命名:现在多花 5 分钟,以后省 5 天
{产品}/{设备ID}/{方向}/{用途}
agri/gw01/up/telemetry 上行:周期遥测
agri/gw01/up/event 上行:事件/告警
agri/gw01/up/reply 上行:命令执行结果(闭环!)
agri/gw01/down/cmd 下行:命令
agri/gw01/status 状态(配合 retained + 遗嘱)
订阅侧:agri/gw01/down/#(这台的下行) agri/+/up/telemetry(所有设备遥测)
四条规矩:① 不以 / 开头(会产生空层级);② 主题里不放动态值(时间戳、随机数一律进 payload,否则主题树爆炸);③ 全小写 + 短横线,别混大小写;④ 主题里不放敏感信息,它是明文的,鉴权靠用户名/密码或证书。
七、动手:半小时搭一个最小端边云
这套我自己在笔记本上跑通过,不需要买云、不需要公网 IP,全在本机 Docker 里。
第 1 步:起一个本地 Broker(EMQX)
docker run -d –name emqx \\
-p 1883:1883 \\ # MQTT over TCP
-p 8083:8083 \\ # MQTT over WebSocket
-p 8084:8084 \\ # MQTT over WSS
-p 8883:8883 \\ # MQTT over TLS
-p 18083:18083 \\ # Dashboard(Web 管理页)
emqx/emqx:latest
docker logs -f emqx # 看日志确认起来了
浏览器打开 http://localhost:18083,默认账号密码 admin / public,首次登录会要求改掉默认密码。
⚠️ 后面让 ESP32 连这个 Broker 时,地址不能填 localhost 或 127.0.0.1——那是 ESP32 自己。要用电脑在局域网里的 IP(ifconfig / ipconfig 查)。这是"ESP32 连不上 EMQX"的头号原因,第二号是防火墙没放行 1883。
第 2 步:先用命令行验证 Broker 通了
sudo apt install -y mosquitto-clients # macOS: brew install mosquitto
# 终端 A:订阅(模拟"云/应用"一侧)
mosquitto_sub -h 192.168.1.100 -t 'agri/#' -v
# 终端 B:发布(模拟"设备"一侧)
mosquitto_pub -h 192.168.1.100 -t 'agri/gw01/up/telemetry' \\
-m '{"temp":26.5,"humi":61,"ts":1756000000}' -q 1
终端 A 应立刻打印出这一条。意义:先把云侧打通再调设备,这样设备联不上时你能确定问题在设备侧。
第 3 步:ESP32 上报(最小可用版)
/* ============ ESP32 / ESP-IDF v5.x:最小 MQTT 上报 ============
* 前置:Wi-Fi 已连接并拿到 IP(esp_wifi 或 example_connect 均可)
* 配置细节见本系列下一篇《MQTT 深度实战》
*/
#include <stdbool.h>
#include <string.h>
#include "mqtt_client.h"
#include "esp_log.h"
static const char *TAG = "app";
static esp_mqtt_client_handle_t s_client = NULL;
static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data)
{
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)event_data;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "broker 已连接");
esp_mqtt_client_subscribe(event->client, "agri/gw01/down/cmd", 1);
break;
case MQTT_EVENT_DATA: {
/* ⚠️ event->topic / event->data 不以 '\\0' 结尾,必须按长度拷贝 */
char topic[64];
int tlen = event->topic_len;
if (tlen >= (int)sizeof(topic)) tlen = (int)sizeof(topic) – 1;
memcpy(topic, event->topic, (size_t)tlen);
topic[tlen] = '\\0';
ESP_LOGI(TAG, "收到 [%s] qos=%d : %.*s",
topic, event->qos, event->data_len, event->data);
/* 执行完记得往 …/up/reply 回报一次,形成闭环 */
break;
}
case MQTT_EVENT_DISCONNECTED:
ESP_LOGW(TAG, "连接断开,esp-mqtt 默认会自动重连");
break;
default: break;
}
}
void mqtt_app_start(void)
{
const esp_mqtt_client_config_t cfg = {
.broker.address.uri = "mqtt://192.168.1.100", /* 换成你的局域网 IP */
.credentials.client_id = "esp32-gw01", /* 同 Broker 内必须唯一 */
.session.keepalive = 60, /* 秒 */
.network.reconnect_timeout_ms = 5000,
.network.timeout_ms = 10000,
};
s_client = esp_mqtt_client_init(&cfg);
if (s_client == NULL) { ESP_LOGE(TAG, "mqtt init 失败"); return; }
esp_mqtt_client_register_event(s_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(s_client);
}
/* 上报一包遥测(在采集任务里调用) */
bool telemetry_publish(float temp, float humi, int64_t ts_ms)
{
if (s_client == NULL) return false;
char payload[128];
int n = snprintf(payload, sizeof(payload),
"{\\"temp\\":%.1f,\\"humi\\":%.1f,\\"ts\\":%lld}",
(double)temp, (double)humi, (long long)ts_ms);
if (n <= 0 || n >= (int)sizeof(payload)) return false; /* 格式化失败或截断 */
/* QoS 1:要等 PUBACK。返回 >=0 的 msg_id 才算进了发送队列 */
int msg_id = esp_mqtt_client_publish(s_client, "agri/gw01/up/telemetry",
payload, n, 1, 0);
if (msg_id < 0) {
ESP_LOGW(TAG, "publish 失败: %d(-2 = outbox 满,发的比网络快)", msg_id);
return false;
}
return true;
}
第 4 步:用 Node-RED 当那个"云上应用"
npm install -g node-red && node-red # 打开 http://localhost:1880
# 或:docker run -d -p 1880:1880 –name nodered nodered/node-red
拖四个节点连成一条线,这就是云层在干的事:解析 → 存储 → 展示。
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐
│ mqtt in │──>│ json │──>│ function │──>│ debug / │
│ agri/# │ │ 解析字符串 │ │ 提取字段 │ │ chart │
└──────────┘ └──────────┘ └──────────┘ └─────────┘
mqtt in 节点填 Server 192.168.1.100:1883、Topic agri/#、QoS 1;function 节点只写一行:
// Node-RED function 节点内(JavaScript)
msg.payload = { value: msg.payload.temp, ts: msg.payload.ts };
return msg;
点 Deploy,再用 mosquitto_pub 发一次,debug 面板应立刻出现数据。到这一步你已跑通完整的"端 → 云 → 应用"链路——只不过"边"目前是空的。
第 5 步:把"边"加回来,做对比实验
你会看到一个数量级的差别,且数据几乎没有信息损失——这就是 4.1 那笔账的实物版。
八、新手必踩的 8 个坑
| 1 | 所有设备直连云,不考虑网关 | 连接数爆炸、流量费高、电池撑不过一周 | 先问"有没有市电、数量多少",10 个以上电池节点就该考虑边 |
| 2 | ESP32 里 Broker 地址填 localhost / 127.0.0.1 | 连不上,日志一片超时 | 填电脑的局域网 IP;确认防火墙放行 1883 |
| 3 | 断网时直接丢数据 | 网络一抖,这段时间数据永久缺失 | 本地队列 + 采样时打时间戳 + 队列有上限 + 补传限速 |
| 4 | 时间戳在上传时打,不是采样时打 | 补传的历史数据全挤同一时刻,曲线是假的 | 采样那一刻就打;或维护序号由平台还原 |
| 5 | 所有消息塞进一个主题 | 云端规则写不动、没法过滤、retained 用不了 | 按 {产品}/{设备}/{方向}/{用途} 分层,一用途一主题 |
| 6 | 主题以 / 开头,或把时间戳拼进主题 | 多出空层级;主题树爆炸撑爆 Broker 内存 | 不以 / 开头;主题只放稳定标识,动态值进 payload |
| 7 | 下行命令发出去就不管 | App 显示"已发送",不知设备有没有执行 | 设备执行完回报 …/up/reply,形成闭环 |
| 8 | 边这层硬件选型拍脑袋 | 树莓派做电池场景几小时没电;ESP32 硬扛一周缓存撑不住 | 先算"缓存多大、要不要跑容器、功耗预算多少"再选 |
九、动手练一练
按顺序做。第 3 步和第 5 步是故意搞破坏——只有亲眼见过故障现象,以后才认得出它。
练习 1:把本机端边云跑通(30 分钟)
按第七章做完。验收标准:ESP32 上电后 Node-RED 的 debug 面板持续收到数据,EMQX Dashboard 的 Clients 页能看到 esp32-gw01 在线。
练习 2:用命令行手动模拟"设备"和"应用"
不开 ESP32,只用两个终端互发。要求:你能说出这一包从"发布者 → Broker → 订阅者"经过哪些环节,并指出 Broker 在里面的作用。
练习 3(故意试错):掐断网络,看数据会不会丢
让 ESP32 正常上报,然后直接关掉电脑 Wi-Fi(或 docker stop emqx),等 30 秒再恢复。没有本地队列的话,这 30 秒的数据不见了,而且日志里可能什么都看不出来。然后加一个最简单的队列(哪怕只是内存里的环形数组 + 补传),再试一次。
意义:这是"能演示的 demo"和"能交付的系统"之间的分水岭。
练习 4:改主题结构,体会过滤的威力
先把所有数据都发到 agri/data,在 Node-RED 里写过滤逻辑;再改成 agri/gw01/up/telemetry 的分层结构,用 agri/+/up/telemetry 订阅。对比两种写法下 Node-RED 流程的复杂度,你就明白为什么要分层。
练习 5(故意试错):把 Broker 地址写错,学会看错误
把 .broker.address.uri 改成 mqtt://192.168.1.999 重新烧录,看串口日志里 MQTT_EVENT_ERROR 的 error_type,然后对比两种情况:IP 不存在是传输层(TCP)失败且反复重试;IP 存在但端口没开(如填 1884)是连接被拒,重连间隔由 reconnect_timeout_ms 决定。
意义:以后遇到"设备离线",你能第一时间分清是"网络不通"还是"Broker 不认我",而不是盲目重启。
小结
- 端-边-云是三次分工:端接触物理世界,边就地解决本地该解决的事,云负责全局和记忆。
- 边的价值能折算成钱和可靠性:省带宽(聚合)、省电(重活从电池节点拿走)、断网续传(数据不丢)、本地联动(断网也能控)。每条都能讲出一个具体场景。
- 协议是分段的:端↔边常常根本不是 IP 网络,边↔云才是 MQTT 的主场。别问"MQTT 和 CoAP 哪个好",要问"这一段用哪个"。
- 主题设计要趁早:{产品}/{设备}/{方向}/{用途},一用途一主题,动态值进 payload。现在偷懒,以后要还。
- 下行必须有回报,否则你的系统永远只有开环。
参考:MQTT 3.1.1 / 5.0 协议规范(OASIS);EMQX 官方文档(Docker 部署与 Dashboard 端口);乐鑫 ESP-IDF 编程指南中 esp-mqtt 组件的配置说明。文中协议开销与流量估算为典型值,实际请以抓包(Wireshark)或 Broker 侧统计为准。
下一篇:MQTT 深度实战——QoS、保留消息、遗嘱与订阅树,ESP32 对接 Broker
网硕互联帮助中心




评论前必须登录!
注册