MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个消息中间件 (服务器)。
通过MQTT协议,目前已经扩展出了数十个MQTT服务器端程序,可以通过PHP,JAVA,Python,C,C#等系统语言来向MQTT发送相关消息。
此外,国内很多企业都广泛使用MQTT作为Android手机客户端与服务器端推送消息的协议。MQTT由于开放源代码,耗电量小等特点。在物联网领域,传感器与服务器的通信,信息的收集,MQTT都可以作为考虑的方案之一。在未来MQTT会进入到我们生活的各各方面。
所以,如果物联网设备想要联网,MQTT是不二选择。
MQTT协议是为大量计算能力有限,且工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议,它具有以下主要的几项特性:
小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;
这就是为什么在介绍里说它非常适合物联网领域,要知道嵌入式设备的运算能力和带宽都相对薄弱,使用这种协议来传递消息再适合不过了。
实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:
Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);
payload,可以理解为消息的内容,是指订阅者具体要使用的内容。
目前主流的Broker有以下3个:
稳定版:emqttd-2.3.11 发布于 2018/07/23
我这里是解压到E盘。
执行成功会弹出下面窗口,不成功就关掉cmd重新试下。
打开浏览器-> 输入http://127.0.0.1:18083 -> 用户名:admin-> 密码:public-> 进入如下界面
这个软件不怎么好用,连不上,关掉再连。使用127.0.0.1也可以。
连上了两个
订阅->发布,就可以数据发送了。
MQTT是基于TCP的,关于是如何封装的,乐鑫已经整理好了,留给我们应用层使用的接口相对较少,内部如何封装,乐鑫怎么封装,标准怎么封装,有时间可以仔细阅读乐鑫源码和标准文档,后面有需要我们会解析下乐鑫封装的那一层,此处不做介绍,只讲解应用层。所谓流程+接口,打遍无敌手。
MQTT配置信息esp_mqtt_client_config_t;
参数原型 |
esp_mqtt_client_config_t |
参数功能 |
MQTT配置信息 |
参数 |
typedef struct { mqtt_event_callback_t event_handle; /*回调*/ const char *host; /*!< MQTT 服务器域名(ipv4 as string)*/ const char *uri; /*!< MQTT 服务器域名 */ uint32_t port; /*!< MQTT服务器端口*/ const char *client_id; /*MQTT Client的名字默认是ESP32_加上MAC后3hex*/ const char *username; /*MQTT用户名*/ const char *password; /*MQTT密码*/ const char *lwt_topic; /*!< LWT主题,默认为空*/ const char *lwt_msg; /*!< LWT信息,默认为空*/ int lwt_qos; /*!< LWT消息质量*/ int lwt_retain; /*!< LWT保留消息标志*/ int lwt_msg_len; /*!< LWT消息长度*/ int disable_clean_session; /*!< mqtt clean session,默认为真*/ int keepalive; /*MQTT心跳,默认120秒 */ bool disable_auto_reconnect; /*错误,断开后重连,true不连*/ void *user_context; /*用户信息 */ int task_prio; /*!< MQTT任务优先级,默认为5,可以在make menuconfig中修改*/ int task_stack; /*!< MQTT 任务堆栈大小,默认6144 bytes,可以在make menuconfig中修改*/ int buffer_size; /*!< MQTT收发缓存,默认1024 */ const char *cert_pem; /*指向用于服务器验证(使用SSL)的PEM格式的证书数据的指针,默认值为空,不需要验证服务器 */ const char *client_cert_pem; /*指向用于SSL相互身份验证的PEM格式的证书数据的指针,默认值为空,如果不需要相互身份验证,则不需要。如果不为空,还必须提供“客户机密钥”。*/ const char *client_key_pem; /*指向用于SSL相互身份验证的PEM格式的私钥数据的指针,默认值为空,如果不需要相互身份验证,则不需要。如果不为空,还必须提供“client-cert-pem”。*/ esp_mqtt_transport_t transport; /*覆盖URI传输*/ } esp_mqtt_client_config_t; |
MQTT Client初始化函数:esp_mqtt_client_init();
函数原型 |
esp_mqtt_client_handle_t esp_mqtt_client_init ( const esp_mqtt_client_config_t *config ) |
函数功能 |
MQTT Client初始化函数 |
参数 |
[in] config:MQTT配置参数,上面介绍了 |
返回值 |
MQTT Client 句柄 |
MQTT Client启动函数:esp_mqtt_client_start();
函数原型 |
esp_err_t esp_mqtt_client_start (esp_mqtt_client_handle_t client); |
函数功能 |
MQTT Client启动函数 |
参数 |
[in] client:MQTT Client 句柄 |
返回值 |
ESP_OK:成功 other : 失败 |
MQTT Client停止函数:esp_mqtt_client_stop();
函数原型 |
esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client); |
函数功能 |
MQTT Client停止函数 |
参数 |
[in] client:MQTT Client 句柄 |
返回值 |
ESP_OK:成功 other : 失败 |
MQTT Client订阅主题函数:esp_mqtt_client_subscribe();
函数原型 |
esp_err_t esp_mqtt_client_subscribe ( esp_mqtt_client_handle_t client, const char *topic, int qos ) |
函数功能 |
MQTT Client订阅函数 |
参数 |
[in] client:MQTT Client 句柄 [in] topic:MQTT 主题 [in] qos:服务质量 |
返回值 |
ESP_OK:成功 other : 失败 |
MQTT Client取消订阅主题函数:esp_mqtt_client_unsubscribe();
函数原型 |
esp_err_t esp_mqtt_client_unsubscribe ( esp_mqtt_client_handle_t client, const char *topic ) |
函数功能 |
MQTT Client取消订阅主题函数 |
参数 |
[in] client:MQTT Client 句柄 [in] topic:MQTT 主题 |
返回值 |
ESP_OK:成功 other : 失败 |
MQTT Client发布主题函数:esp_mqtt_client_publish();
函数原型 |
int esp_mqtt_client_publish ( esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain ) |
函数功能 |
MQTT Client发布主题函数 |
参数 |
[in] client:MQTT Client 句柄 [in] topic:MQTT 主题 [in] data:数据 [in] len:长度,=0表示data长度 |
返回值 |
消息ID |
MQTT Client注销函数:esp_mqtt_client_publish();
函数原型 |
esp_err_t esp_mqtt_client_destroy(esp_mqtt_client_handle_t client); |
函数功能 |
MQTT Client取消订阅主题函数 |
参数 |
[in] client:MQTT Client 句柄 |
返回值 |
ESP_OK:成功 other : 失败 |
MQTT初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
static void mqtt_app_start(void) { esp_mqtt_client_config_t mqtt_cfg = { .host = "192.168.2.104", //MQTT服务器IP .event_handle = mqtt_event_handler, //MQTT事件 .port=1883, //端口 .username = "admin", //用户名 .password = "public", //密码 // .user_context = (void *)your_context }; client = esp_mqtt_client_init(&mqtt_cfg); esp_mqtt_client_start(client); //等mqtt连上 xEventGroupWaitBits(mqtt_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); } |
MQTT回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event) { esp_mqtt_client_handle_t client = event->client; int msg_id; // your_context_t *context = event->context; switch (event->event_id) { case MQTT_EVENT_CONNECTED://MQTT连上事件 ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); xEventGroupSetBits(mqtt_event_group, CONNECTED_BIT); //发送订阅 msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); break; case MQTT_EVENT_DISCONNECTED://MQTT断开连接事件 ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); //mqtt连上事件 xEventGroupClearBits(mqtt_event_group, CONNECTED_BIT); break; case MQTT_EVENT_SUBSCRIBED://MQTT发送订阅事件 ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "订阅成功", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; case MQTT_EVENT_UNSUBSCRIBED://MQTT取消订阅事件 ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; case MQTT_EVENT_PUBLISHED://MQTT发布事件 ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; case MQTT_EVENT_DATA://MQTT接受数据事件 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://MQTT错误事件 ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); xEventGroupClearBits(mqtt_event_group, CONNECTED_BIT); break; } return ESP_OK; } |
定时发布主题
1 2 3 4 5 6 7 |
while (1) { //发布主题 //PC订阅了,会收到这条信息 esp_mqtt_client_publish(client, "/topic/qos0", "Hello MQTT ,I am HongXu", 0, 0, 0); vTaskDelay(1000 / portTICK_PERIOD_MS);
} |
服务器有两个MQTT客户端,分别是ESP32开发板和PC的助手。
订阅和发布,可以互通
我发布的主题,无论是谁订阅都能收到,包括我自己。
别人发布的主题,只要我订阅就能收到。