MQTT文档介绍
一、在PC上可以使用 MQTT X 工具:(参考地址)
1、客户端下载:MQTT X 工具下载地址
2、EMQX服务器下载地址
3.打开命令行工具,进入目录运行EMQX服务。
电脑左下角,右键开始->运行->输入cmd,确定->打开命令行工具,操作如下:
1)切换到E盘:输入e:
2)进入到EMQX的bin目录:输入cd D:\EMQX\bin
3)启动EMQX服务器,输入emqx start,如需停止服务器,输入emqx stop
4)查看EMQX服务器状态,先进入bin目录:输入cd D:\EMQX\bin
5)接着输入:emqx_ctl status,可以看到EMQX服务器已启动
4.登陆EMQX服务器,验证是否可用。
1)打开浏览器,输入地址:http://127.0.0.1:18083/#/login
2)输入用户名:admin,输入密码:public,点击Login
3)更改界面为中文。
二、wif配网:
配网app下载地址
智能配网
static void smartconfig_example_task(void * parm)
{
wifi_config_t myconfig = {0};
EventBits_t uxBits;
esp_wifi_get_config(WIFI_IF_STA,&myconfig); //获取WiFi配网信息
if( strlen((char*)myconfig.sta.ssid) > 0 ) //之前已经完成配网 直接连接
{
ESP_LOGI(TAG,"wifi already set,ssid %s start connect.", myconfig.sta.ssid);
esp_wifi_connect();
}
else//以前未进行连接
{
ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) ); //开始进行配网
}
while (1) {
uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
if(uxBits & CONNECTED_BIT) {
ESP_LOGI(TAG, "WiFi Connected to ap");
}
if(uxBits & ESPTOUCH_DONE_BIT) {
ESP_LOGI(TAG, "smartconfig over");
esp_smartconfig_stop();
vTaskDelete(NULL); //清除配网信息
}
}
}
蓝牙配网:通过蓝牙将wifi配置信息传给esp32。
手机通过蓝牙配网app,检索到esp蓝牙信号,连接。
然后通过app传输wifi名称和密码,交给esp32。
esp32接收到wifi配置,进行wifi连接。
API esp_wifi_set_config() //可用于配置 AP。配置的参数信息会保存到 NVS 中。
三、MQTT连接
1、esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *config)
函数描述:根据配置创建MQTT客户端句柄
typedef struct {
mqtt_event_callback_t event_handle; / *!<处理MQTT事件作为传统模式的回调* /
esp_event_loop_handle_t event_loop_handle; / *!<MQTT事件循环库的句柄* /
const char * host; / *!<MQTT服务器域(ipv4 as string)* /
const char * uri; / *!<完整的MQTT代理URI * /
uint32_t port; / *!<MQTT服务器端口* /
const char * client_id; / *!<默认客户端ID是``ESP32_%CHIPID%``其中%CHIPID%是十六进制格式的MAC地址的最后3个字节* /
const char * username; / *!<MQTT用户名* /
const char *密码; / *!<MQTT密码* /
const char * lwt_topic; / *!<LWT(遗嘱和遗嘱)消息主题(默认为NULL)* /
const char * lwt_msg; / *!<LWT消息(默认为NULL)* /
int lwt_qos; / *!<LWT消息qos * /
int lwt_retain; / *!<LWT保留消息标志* /
int lwt_msg_len; / *!<LWT消息长度* /
int disable_clean_session; / *!<mqtt clean session,默认clean_session为true * /
int keepalive; / *!<mqtt keepalive,默认为120秒* /
bool disable_auto_reconnect; / *!<此mqtt客户端将重新连接到服务器(当出错/断开连接时)。设置disable_auto_reconnect = true以禁用* /
void * user_context; / *!<将用户上下文传递给此选项,然后可以在``event-> user_context`` * /中接收该上下文
int task_prio; / *!<MQTT任务优先级,默认为5,可以在``make menuconfig``中更改* /
int task_stack; / *!<MQTT任务堆栈大小,默认为6144字节,可以在``make menuconfig``中更改* /
int buffer_size; / *!<MQTT发送/接收缓冲区的大小,默认为1024 * /
const char * cert_pem; / *!<用于服务器验证的PEM格式的证书数据指针(使用SSL),默认为NULL,不需要验证服务器* /
const char * client_cert_pem; / *!<指向用于SSL相互身份验证的PEM格式的证书数据的指针,默认为NULL,如果不需要相互身份验证,则不需要。如果它不是NULL,则还必须提供`client_key_pem`。 * /
const char * client_key_pem; / *!<指向用于SSL相互身份验证的PEM格式的私钥数据的指针,默认为NULL,如果不需要相互身份验证,则不需要。如果它不是NULL,则还必须提供`client_cert_pem`。 * /
esp_mqtt_transport_t transport; / *!<覆盖URI传输* /
int refresh_connection_after_ms; / *!<刷新此值后的连接(以毫秒为单位)* /
esp_mqtt_client_config_t;
2、sp_err_t esp_mqtt_client_start(esp_mqtt_client_handle_t client)
函数描述:启动MQTT客户端
参数:创建的MQTT句柄
返回值:ESP_OK表示成功ESP_ERR_无效_ARG错误初始化ESP_FERT其他错误
3、esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client)
函数描述:停止MQTT客户端
参数:创建的MQTT句柄
4、int esp_mqtt_client_subscribe(esp_mqtt_client_handle_t client, const char *topic, int qos)
函数描述:订阅主题
参数:A、esp_mqtt_client_handle_t client 创建的MQTT句柄
B、char *topic 订阅的主题
C、Qos级别 0 、1、 2
返回值:成功时订阅消息返回Message_id- 失败返回-1
5、int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client, const char *topic)
函数描述:取消订阅的主题
参数:A、esp_mqtt_client_handle_t client 创建的MQTT句柄
B、B、char *topic 订阅的主题
返回值:成功时订阅消息返回Message_id- 失败返回-1
6、int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain)
函数描述:往代理发布消息
参数:A、esp_mqtt_client_handle_t client 创建的MQTT句柄
B、char topic 发布的主题C、 const char data 发布的数据D、int len 数据长度,如果设置为0,则从发布的数据长度E、Qos级别F、retain 当我们使用MQTT客户端发布消息(PUBLISH)时,如果将RETAIN标志位设置为true,那么MQTT服务器会将最近收到的一条RETAIN标志位为true的消息保存在服务器端7、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)函数描述:注册MQTT事件参数:A、esp_mqtt_client_handle_t client 创建的MQTT句柄B、esp_mqtt_event_id_t event 注册任何事件id的处理程序C、esp_event_handler_t event_handler 回调函数D、void event_handler_arg 处理程序上下文
连接发送测试:
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wpa2.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_smartconfig.h"
#include "my_button.h"
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_event.h"
#include "protocol_examples_common.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "mqtt_client.h"
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t s_wifi_event_group;
static esp_mqtt_client_handle_t mqttclient;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static const char *TAG = "smartconfig_example";
static void smartconfig_example_task(void * parm);
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
esp_wifi_connect();
xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
ESP_LOGI(TAG, "Scan done");
} else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
ESP_LOGI(TAG, "Found channel");
} else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
ESP_LOGI(TAG, "Got SSID and password");
smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
wifi_config_t wifi_config;
uint8_t ssid[33] = { 0 };
uint8_t password[65] = { 0 };
uint8_t rvd_data[33] = { 0 };
bzero(&wifi_config, sizeof(wifi_config_t));
memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
wifi_config.sta.bssid_set = evt->bssid_set;
if (wifi_config.sta.bssid_set == true) {
memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
}
memcpy(ssid, evt->ssid, sizeof(evt->ssid));
memcpy(password, evt->password, sizeof(evt->password));
ESP_LOGI(TAG, "SSID:%s", ssid);
ESP_LOGI(TAG, "PASSWORD:%s", password);
if (evt->type == SC_TYPE_ESPTOUCH_V2) {
ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
ESP_LOGI(TAG, "RVD_DATA:");
for (int i=0; i<33; i++) {
printf("%02x ", rvd_data[i]);
}
printf("\n");
}
ESP_ERROR_CHECK( esp_wifi_disconnect() );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
esp_wifi_connect();
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
}
}
static void initialise_wifi(void)
{
ESP_ERROR_CHECK(esp_netif_init());
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); //wifi接口初始化
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
static void smartconfig_example_task(void * parm)
{
wifi_config_t myconfig = {0};
EventBits_t uxBits;
esp_wifi_get_config(WIFI_IF_STA,&myconfig); //获取WiFi配网信息
if( strlen((char*)myconfig.sta.ssid) > 0 ) //之前已经完成配网 直接连接
{
ESP_LOGI(TAG,"wifi already set,ssid %s start connect.", myconfig.sta.ssid);
esp_wifi_connect();
}
else//以前未进行连接
{
ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) ); //开始进行配网
}
while (1) {
uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
if(uxBits & CONNECTED_BIT) {
ESP_LOGI(TAG, "WiFi Connected to ap");
}
if(uxBits & ESPTOUCH_DONE_BIT) {
ESP_LOGI(TAG, "smartconfig over");
esp_smartconfig_stop();
vTaskDelete(NULL); //清除配网信息
}
}
}
//事件处理函数
static esp_err_t mqtt_event_handler_cb(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");
//发送订阅
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_unsubscribe(client, "/topic/qos0");
// ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED: //MQTT断开连接事件
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
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", "data", 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");
// 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;
}
return ESP_OK;
}
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
/* The argument passed to esp_mqtt_client_register_event can de accessed as handler_args*/
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%ld", base, event_id);
mqtt_event_handler_cb(event_data);
}
//mqtt初始化
static void mqtt_app_start(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
// .event_handle = mqtt_event_handler, //MQTT事件
.broker.address.uri = "mqtt://broker.emqx.io",
// .broker.address.hostname = "192.168.12.34" , //MQTT服务器IP
// .broker.address.port = 1883, //端口
.credentials.username = "admin" , //用户名
// .credentials.client_id = "emqx_OTM5OD", //客户端id
.credentials.authentication.password = "public" , //密码
};
#if CONFIG_BROKER_URL_FROM_STDIN
char line[128];
if (strcmp(mqtt_cfg.uri, "FROM_STDIN") == 0) {
int count = 0;
printf("Please enter url of mqtt broker\n");
while (count < 128) {
int c = fgetc(stdin);
if (c == '\n') {
line[count] = '\0';
break;
} else if (c > 0 && c < 127) {
line[count] = c;
++count;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
mqtt_cfg.uri = line;
printf("Broker url: %s\n", line);
} else {
ESP_LOGE(TAG, "Configuration mismatch: wrong broker url");
abort();
}
#endif /* CONFIG_BROKER_URL_FROM_STDIN */
printf("esp_mqtt_client_start\r\n");
//通过esp_mqtt_client_init获取一个MQTT客户端结构体指针,参数是MQTT客户端配置结构体
// esp_mqtt_client_handle_t
mqttclient = esp_mqtt_client_init(&mqtt_cfg);
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
//注册MQTT事件
esp_mqtt_client_register_event(mqttclient, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
//开启MQTT功能
esp_mqtt_client_start(mqttclient);
//等mqtt连上
// xEventGroupWaitBits(mqtt_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
}
//获取纳秒级时间
int64_t getTime(void)
{
struct timeval tv_now;
gettimeofday(&tv_now, NULL);
int64_t time_us = (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec;
ESP_LOGI(TAG, "[APP] IDF time: %lld", time_us);
return time_us;
}
void app_main(void)
{
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %lu bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT_BASE", ESP_LOG_VERBOSE);
esp_log_level_set("esp-tls", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE);
ESP_ERROR_CHECK( nvs_flash_init() );
initialise_wifi();
button_start(); //按键清除配网信息
mqtt_app_start();
getTime();
while (1) {
printf("system is running!\r\n");
//发布主题
esp_mqtt_client_publish(mqttclient, "/topic/qos0", "Hello MQTT,I am huqin ", 0, 0, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
测试采用公共服务器 mqtt://broker.emqx.io 进行通讯测试
客户端发送消息和接受的消息
也可以在不同服务器之间通讯