ESP32中MQTT通讯

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 进行通讯测试

客户端发送消息和接受的消息

也可以在不同服务器之间通讯

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值