MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)
- 1999 年由 IBM 开发,轻量级、发布 / 订阅模式的物联网专用传输协议;
- 基于 TCP/IP,也支持 WebSocket(网页端 MQTT),专为带宽小、算力弱、网络不稳定的嵌入式设备设计(ESP32/STM32、传感器、单片机)。
区别于 HTTP 一问一答的点对点,MQTT 引入Broker(消息服务器 / 中间件) 做中转,解耦发送方与接收方:
- 发布者 Publisher:设备产生数据(传感器上报温湿度、硬件上报状态),向主题发送消息;
- Broker 服务端:接收、缓存、转发消息,管理所有客户端连接与订阅关系;
- 订阅者 Subscriber:客户端订阅指定主题,Broker 收到对应消息后自动推送给它;
- 主题 Topic :消息的分类标识,分层字符串(用
/分隔,类似路径),例:device/esp32_001/temp、platform/one_net/control
MQTT通信流程
- 所有客户端先和 Broker 建立 TCP 长连接;
- 订阅者提前订阅感兴趣的 Topic;
- 发布者往 Topic 发消息;
- Broker 匹配订阅关系,把消息推送给所有订阅该 Topic 的客户端;
- 订阅者收到消息后本地处理。
MQTT规定了14种报文包类型
- 连接请求(CONNECT):设备向云发起连接登录。
- 连接应答(CONNACK):云端响应CONNECT,告知登录成功/失败。
- 发布消息(PUBLISH):双向通用,设备上报数据,云端下发指令。主要任务:a.提取topic,载荷payload。b.区分云端业务数据,解析JSON存业务变量。c.根据QOS等级回复握手包。(QOS0无需应答,QOS1回复PUBACK,QOS2回复PUBREC)
- 订阅主题(SUBSCRIBE):订阅主题,设备主动订阅下发主题。该条报文仅设备发送,云端回复 SUBACK;设备不会接收该包。
- 取消订阅(UBSBUCRIBE):设备主动取消订阅主题。
- 订阅应答(SUBACK):云端确认订阅结果。
- 取消订阅应当(UNSUBACK):云端确认取消订阅结果。
- PUBACK(发布确认QOS1):QOSI消息级别应答。
- PUBREC(发布收到,QOS2第一步):QOS2握手第一段。
- PUBERL(发布释放,QOS2第二步):QOS2握手第二段。
- PUBCOMP(发布完成,QOS2第三步):QOS2握手收尾。
- PINGREQ(心跳请求):设备定时发送保活心跳。
- PINGRESP(心跳响应):云端回复心跳,链路正常。
- DISCONNECED(断开连接):设备主动下发离线通知。
接下来具体看ESP32官方如何实现mqtt协议。
协议接入流程如下
1.配置mqtt结构体,并将该配置初始化。

在配置结构体中,有以上参数可配置。
最小配置参数如下,最必要的参数就是你要接入的网址端口。(uri配置为你要接入的网址端口)其余参数比如verification和credentials(校验层相关参数)可按需自行配置。接下来需要创建一个mqtt客户端,并将该参数初始化到该客户端中,注册事件处理函数实时监控mqtt事件的发生,最后开启mqtt客户端即可。
cs
const esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = "mqtt://mqtt.eclipseprojects.io",
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
esp_mqtt_client_start(client);
这里再详细介绍一下结构体esp_mqtt_client_config_t中各参数。
broker.address:用代理相关的配置(比如服务器网址端口)
broker.verification:用于ESP32验证mqtt服务器身份,防止连接到假服务器。(单向TLS认证)
credentials:客户端凭证层,用于服务器校验ESP32身份,服务器根据此配置来判断是否允许设备接入topic集群。
cs
//普通账号密码认证
credentials.username = "设备用户名";
credentials.authentication.password = "设备密码/签名密钥";
credentials.client_id = "自定义设备ID";
对应 MQTT CONNECT 报文中的 ClientID、Username、Password 字段,明文或 TLS 加密后发给 Broker 鉴权。
cs
//双向TLS客户端根证书认证
credentials.authentication.certificate = 设备客户端证书;
credentials.authentication.key = 设备私钥;
对应双向 TLS(Mutual TLS):TLS 握手时 ESP32 把自己的设备证书发给 Broker,服务器校验设备合法性,属于 TLS 层客户端鉴权。

可以看到,在ESP32中,规定了八个mqtt事件,因此,在mqtt_event_handler事件处理函数中,就可以处理这些事件。
cs
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d, return code=0x%02x ", event->msg_id, (uint8_t)*event->data);
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
}
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}