ESP32+idf开发之WIFI通信入门(4)MQTT通信
一、实现功能:
1、esp32作为MQTT客户端定时每隔2秒向MQTT服务端发布消息:
topic:heartbeat
payload:client1 is online!
2、esp32作为MQTT客户端订阅主题为“/control”,接收该主题的消息payload并打印。
二、MQTT概述:
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的轻量级协议,该协议构建于TCP/IP协议之上,MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。MQTT协议是一个基于客户端-服务器的消息发布/订阅传输协议。它是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛,作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。
MQTT有三种消息发布服务质量:
“至多一次”,消息发布完全依赖底层TCP/IP网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。这一种方式主要普通APP的推送,倘若你的智能设备在消息推送时未联网,推送过去没收到,再次联网也就收不到了。
“至少一次”,确保消息到达,但消息重复可能会发生。
“只有一次”,确保消息到达一次。在一些要求比较严格的计费系统中,可以使用此级别。在计费系统中,消息重复或丢失会导致不正确的结果。这种最高质量的消息发布服务还可以用于即时通讯类的APP的推送,确保用户收到且只会收到一次。
实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:
(1)topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);
(2)payload,可以理解为消息的内容,是指订阅者具体要使用的内容。
MQTT服务器可用免费共享的,也可自行搭建,本测试采用的在云服务器上自行搭建EMQX服务器(https://www.emqx.io/zh/downloads)
三、步骤:
1、复制wifi连接的组件代码到工程的自定义组件(components)中,进入vscode创建示例空项目,打开命令终端:
mkdir components
cp -r D:\Espressif\frameworks\esp-idf-v4.4.1\examples\common_components\protocol_examples_common .\components
2、配置wifi连接设置,打开终端命令界面输入idf.py menuconfig
,进入到Example Connection Configuration界面对热点信息的ssid和password等配置。
3、在main.c中编写实现以上功能的代码:
#include <esp_log.h>
#include <esp_system.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <nvs_flash.h>
#include <esp_netif.h>
#include <esp_event.h>
#include <protocol_examples_common.h>
#include <mqtt_client.h>
static char *TAG = "mqtt_client";
static esp_mqtt_client_handle_t mqttclient;
static bool connect_flag = false; // 定义一个连接上mqtt服务器的flag
// mqtt的状态机事件处理函数
static void mqtt_event_handler(void *args, esp_event_base_t base, int32_t id, void *event_data)
{
esp_mqtt_event_t *client_event = event_data;
esp_mqtt_event_id_t client_id = id;
switch (client_id)
{
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "connected to mqtt_server");
connect_flag = true;
esp_mqtt_client_subscribe(mqttclient, "control", 2); // 订阅一个/control的topic
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "disconnected");
connect_flag = false;
break;
case MQTT_EVENT_DATA:
{
char topic[128] = {};
char data[512] = {};
memcpy(topic, client_event->topic, client_event->topic_len);
memcpy(data, client_event->data, client_event->data_len);
printf("topic:%s\n", topic);
printf("data:%s\n", data);
}
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "error");
connect_flag = false;
break;
default:
break;
}
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
// mqtt服务器的配置信息(根据实际情况填写)
esp_mqtt_client_config_t cfg = {
.uri = "mqtt://XX.XX.XX.XX",
.port = XX,
.username = "XX",
.password = "XX",
};
mqttclient = esp_mqtt_client_init(&cfg);
//注册MQTT状态机事件处理回调函数
esp_mqtt_client_register_event(mqttclient, MQTT_EVENT_ANY, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqttclient);
while (1)
{
if (connect_flag == true)
{
char buff[512] = "client1 is online!";
// 向mqtt服务器发布主题为/heartbeat/1,payload为buff的数据
esp_mqtt_client_publish(mqttclient, "heartbeat", buff, strlen(buff), 2, 0);
}
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
四、测试:
编译、烧写、监控esp32,用另一个MQTT客户端(如通信猫)来进行订阅/发布测试,在通信猫上可每两秒接收到esp32发布的消息,在esp32上可打印出通信猫发布的消息。