先上官方文档
一,MQTT协议的简介
网上很多这里不作讲解,参考这篇文章
二,ESP_MQTT的配置过程
ESP_MQTT实现了MQTT客户端的功能,支持多种协议,这里仅以TCp为例。基本步骤如下;
- 使用
esp_mqtt_client_init()
配置mqtt。 - 使用
esp_mqtt_client_register_event()
注册事件处理函数。 - 使用
esp_mqtt_client_start()
开启mqtt客户端。
MQTT的运行与WiFi类似,都是基于事件进行处理。但是,WiFi库是基于ESP_EVENT
库而MQTT库则是使用的内建的事件处理循环。
初始化MQTT
1 | esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *config); |
使用该函数初始化MQTT客户端,并创建MQTT客户端句柄。
esp_mqtt_client_config_t
1 | typedef struct esp_mqtt_client_config_t { |
- 使用 uri 字段的格式为
scheme://hostname:port/path
当前支持
mqtt
、mqtts
、ws
和wss
协议基于 TCP 的 MQTT 示例:
mqtt://mqtt.eclipseprojects.io
:基于 TCP 的 MQTT,默认端口 1883mqtt://mqtt.eclipseprojects.io:1884
:基于 TCP 的 MQTT,端口 1884mqtt://username:password@mqtt.eclipseprojects.io:1884
:基于 TCP 的 MQTT, 端口 1884,带有用户名和密码
基于 SSL 的 MQTT 示例:
mqtts://mqtt.eclipseprojects.io
:基于 SSL 的 MQTT,端口 8883mqtts://mqtt.eclipseprojects.io:8884
:基于 SSL 的 MQTT,端口 8884
基于 WebSocket 的 MQTT 示例:
ws://mqtt.eclipseprojects.io:80/mqtt
基于 WebSocket Secure 的 MQTT 示例:
wss://mqtt.eclipseprojects.io:443/mqtt
客户端凭据
credentials
字段下包含所有客户端相关凭据。username
:指向用于连接服务器用户名的指针,也可通过 URI 设置client_id
:指向客户端 ID 的指针,默认为 ESP32_%CHIPID%,其中 %CHIPID% 是十六进制 MAC 地址的最后 3 个字节
认证
可以通过authentication
字段设置认证参数。客户端支持以下认证方式:password
:使用密码certificate
和key
:进行双向 TLS 身份验证,PEM 或 DER 格式均可use_secure_element
:使用 ESP32-WROOM-32SE 中的安全元素ds_data
:使用某些乐鑫设备的数字签名外设
MQTT 协议中的 Keep Alive 机制
MQTT 协议是承载于 TCP 协议之上的,而 TCP 协议以连接为导向,在连接双方之间,提供稳定、有序的字节流功能。 但是,在部分情况下,TCP 可能出现半连接问题。所谓半连接,是指某一方的连接已经断开或者没有建立,而另外一方的连接却依然维持着。在这种情况下,半连接的一方可能会持续不断地向对端发送数据,而显然这些数据永远到达不了对端。为了避免半连接导致的通信黑洞,MQTT 协议提供了 Keep Alive 机制,使客户端和 MQTT 服务器可以判定当前是否存在半连接问题,从而关闭对应连接。
MQTT持久会话与Clean Session详解
MQTT客户端在发起服务器连接时,可以设置是否创建一个持久会话。但Clean Session为true,指在创建一个新的会话时,在客户端断开连接时会话会自动销毁,为false时,在客户端断开连接时会话仍然保持,直到其重新连接或者会话超时注销。
持久会话可以避免因网络中断导致的重复订阅或者错过离线期间的消息。
注册事件函数
1 | esp_err_t esp_mqtt_client_register_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, esp_event_handler_t event_handler, void *event_handler_arg) |
使用该函数注册事件处理函数。该事件处理的用法与esp_event库相似。
一个标准的事件处理模板为:
1 | /* |
开始MQTT客户端
1 | esp_err_t esp_mqtt_client_start(esp_mqtt_client_handle_t client); |
开启MQTT客户端服务。
订阅/取消订阅
1 | int esp_mqtt_client_subscribe(esp_mqtt_client_handle_t client, const char *topic, int qos) |
将客户端订阅到定义的主题,并定义了QOS。需要在连接服务器后进行。
1 | int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client, const char *topic) |
取消订阅。
发布内容
ESP提供了两种发布内容的方法,阻塞式和非阻塞。
1 | int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain); |
阻塞式发布一个消息。超时时间为config中的网络超时时间。使用这个API不需要先连接到服务器,此时消息会自动加入后台队列(如果设置了MQTT_SKIP_PUBLISH_IF_DISCONNECTED为true,则不会尝试发送而是直接返回-1)
1 | int esp_mqtt_client_enqueue(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain, bool store) |
将信息排队到outbox,以便稍后发送。通常用于qos>0的消息,但如果store=true,也可用于qos=0的消息。
这个API生成并存储发布消息到内部的outbox中,而实际发送至网络是在mqtt任务中进行的(与esp_mqtt_client_publish()相反,后者在用户任务的任务中立即发送发布消息)。因此,它可以作为esp_mqtt_client_publish()的一个非阻塞版本。
QOS服务质量
- QoS0,At most once,至多一次,Sender 发送的一条消息,Receiver 最多能收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,也就算了;
- QoS1,At least once,至少一次,Sender 发送的一条消息,Receiver 至少能收到一次,也就是说 Sender 向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,但是因为重传的原因,Receiver 有可能会收到重复的消息;
- QoS2,Exactly once,确保只有一次,Sender 发送的一条消息,Receiver 确保能收到而且只收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,同时保证 Receiver 不会因为消息重传而收到重复的消息。
当我们使用MQTT客户端发布消息(
PUBLISH
)时,如果将RETAIN标志位设置为true
,那么MQTT服务器会将最近收到的一条RETAIN标志位为true
的消息保存在服务器端(内存或文件)。
特别注意:MQTT服务器只会为每一个Topic保存最近收到的一条RETAIN标志位为true
的消息!也就是说,如果MQTT服务器上已经为某个Topic保存了一条Retained消息,当客户端再次发布一条新的Retained消息,那么服务器上原来的那条消息会被覆盖!每当MQTT客户端连接到MQTT服务器并订阅了某个topic,如果该topic下有Retained消息,那么MQTT服务器会立即向客户端推送该条Retained消息。
如果客户端想让MQTT服务器删除某个Topic下保存的Retained消息,唯一的方法是向MQTT服务器发布一条RETAIN标志位为
true
的空消息。
空消息即为发布消息(PUBLISH
)的时候,Payload中设置0个字节的内容。
删除了某个Topic下保存的Retained消息,如果客户端没有再发布Retained消息,则MQTT服务器上对于该Topic就没有了Retained消息。
其他API
1 | esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *uri); |
更新uri。
1 | esp_err_t esp_mqtt_client_reconnect(esp_mqtt_client_handle_t client); |
手动重新连接。
1 | esp_err_t esp_mqtt_client_disconnect(esp_mqtt_client_handle_t client); |
手动断连。
1 | esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_client_config_t *config); |
更新设置,在MQTT_EVENT_BEFORE_CONNECT
前。
三,其他参考
事件参考
User event handler receives context data in esp_mqtt_event_t
structure with
client
- MQTT client handle
various other data depending on event type
MQTT_EVENT_ANY
MQTT_EVENT_ERROR
在错误事件中,额外的上下文:连接返回代码,来自esp_tls的错误处理(如果支持)
MQTT_EVENT_CONNECTED
connected event, additional context: session_present flag
MQTT_EVENT_DISCONNECTED
disconnected event
MQTT_EVENT_SUBSCRIBED
subscribed event, additional context:
msg_id
message iderror_handle
error_type in case subscribing faileddata
pointer to broker response, check for errors.data_len
length of the data for this event
MQTT_EVENT_UNSUBSCRIBED
unsubscribed event, additional context: msg_id
MQTT_EVENT_PUBLISHED
published event, additional context: msg_id
MQTT_EVENT_DATA
data event, additional context:
msg_id
message idtopic
pointer to the received topictopic_len
length of the topicdata
pointer to the received datadata_len
length of the data for this eventcurrent_data_offset
offset of the current data for this eventtotal_data_len total
length of the data receivedretain
retain flag of the messageqos
QoS level of the messagedup
dup flag of the message Note: Multiple MQTT_EVENT_DATA could be fired for one message, if it is longer than internal buffer. In that case only first event contains topic pointer and length, other contain data only with current data length and current data offset updating.
MQTT_EVENT_BEFORE_CONNECT
The event occurs before connecting
MQTT_EVENT_DELETED
Notification on delete of one message from the internal outbox, if the message couldn’t have been sent and acknowledged before expiring defined in OUTBOX_EXPIRED_TIMEOUT_MS. (events are not posted upon deletion of successfully acknowledged messages)
- This event id is posted only if MQTT_REPORT_DELETED_MESSAGES==1
- Additional context: msg_id (id of the deleted message).
MQTT_USER_EVENT
Custom event used to queue tasks into mqtt event handler All fields from the esp_mqtt_event_t type could be used to pass an additional context data to the handler.