[ESP32 IDF+Vscode]蓝牙配网后采用上传温湿度数据至阿里云(MQTT协议)

阿里云平台的设置

参考文章:

http://t.csdn.cn/RzLGqhttp://t.csdn.cn/RzLGq

Blufi配网

1.简介

BluFi 是一款基于蓝牙通道的 Wi-Fi 网络配置功能,适用于 ESP32。它通过安全协议将 Wi-Fi 配置和证书传输到 ESP32,然后 ESP32 可基于这些信息连接到 AP 或建立 SoftAP。

BluFi 流程的关键部分包括数据的分片、加密、校验和验证。

用户可按需自定义用于对称加密、非对称加密和校验的算法。这里我们采用 DH 算法进行密钥协商、128-AES 算法用于数据加密、CRC16 算法用于校验和验证。

2.BluFi 流程

BluFi 配网流程包含配置 SoftAP 和配置 Station 两部分。

下面以配置 Station 为例,介绍了广播、连接、服务发现、协商共享密钥、传输数据、回传连接状态等关键步骤。

  1. ESP32 开启 GATT Server 模式,发送带有特定 advertising data 的广播。该广播不属于 BluFi Profile,可以按需对其进行自定义。

  2. 使用手机应用程序搜索到该广播后,手机将作为 GATT Client 连接 ESP32。该步骤对具体使用哪款手机应用程序并无特殊要求。

  3. 成功建立 GATT 连接后,手机会向 ESP32 发送数据帧进行密钥协商(详见 BluFi 中定义的帧格式 )。

  4. ESP32 收到密钥协商的数据帧后,会按照自定义的协商方法进行解析。

  5. 手机与 ESP32 进行密钥协商。协商过程可使用 DH/RSA/ECC 等加密算法。

  6. 协商结束后,手机端向 ESP32 发送控制帧,用于设置安全模式。

  7. ESP32 收到控制帧后,使用共享密钥以及安全配置对通信数据进行加密和解密。

  8. 手机向 ESP32 发送 BluFi 中定义的帧格式 中定义的数据帧,包括 SSID、密码等 Wi-Fi 配置信息。

  9. 手机向 ESP32 发送 Wi-Fi 连接请求的控制帧。ESP32 收到控制帧后,即默认手机已完成必要信息的传输,准备连接 Wi-Fi。

  10. 连接到 Wi-Fi 后,ESP32 发送 Wi-Fi 连接状态报告的控制帧到手机。至此,配网结束。

BluFi 流程图

蓝牙配网代码

/*通过蓝牙进行配网操作*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_mac.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"

#include "esp_blufi_api.h"
#include "blufi_example.h"
#include "blufi_example_main.h"
#include "esp_blufi.h"
#include "smartconfig_led.h"
// WiFi 连接重试次数的常量
#define EXAMPLE_WIFI_CONNECTION_MAXIMUM_RETRY CONFIG_EXAMPLE_WIFI_CONNECTION_MAXIMUM_RETRY
//表示一个无效的原因代码
#define EXAMPLE_INVALID_REASON                255
//表示一个无效的信号强度值(RSSI) 通常用于表示未能成功获取到实际的 RSSI 值
#define EXAMPLE_INVALID_RSSI                  -128
int  is_configured=0;  //判断WiFi连接的标志,因为加入了按键长按清除配网信息
typedef enum {
    wifi_unconfiged = 0x00,
    wifi_configed   = 0xAA,
}wifi_info_storage_t;

//该函数是用作蓝牙低功耗(Bluetooth Low Energy, BLE)的回调函数,用于处理与蓝牙协议相关的事件。
//具体的事件类型和参数可以在 esp_blufi_cb_event_t 和 esp_blufi_cb_param_t 中定义和传递。
static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param);
//表示 Wi-Fi 扫描到的 AP 列表中最多包含的 AP 数量。
#define WIFI_LIST_NUM   10

static wifi_config_t sta_config;//用于存储 Wi-Fi 的配置信息
static wifi_config_t ap_config;

//是一个 FreeRTOS 事件组(Event Group)的句柄(handle)
//该事件组用于在连接成功并准备好发起请求时进行信号传递
static EventGroupHandle_t wifi_event_group;

//表示与接入点(AP)建立连接的状态
const int CONNECTED_BIT = BIT0;
//用于记录 Wi-Fi 连接失败时的重试次数
static uint8_t example_wifi_retry = 0;

/*
这段代码定义了一系列静态变量,用于存储与连接状态和连接相关的信息。下面是对每个变量的说明:

gl_sta_connected:一个布尔类型的变量,用于表示 Wi-Fi 站点(station)是否已连接。
gl_sta_got_ip:一个布尔类型的变量,用于表示 Wi-Fi 站点是否已获取到 IP 地址。
ble_is_connected:一个布尔类型的变量,用于表示 BLE 是否已连接。
gl_sta_bssid:一个长度为 6 的无符号 8 位整数数组,用于存储 Wi-Fi 站点的 BSSID(基本服务集标识符)。
gl_sta_ssid:一个长度为 32 的无符号 8 位整数数组,用于存储 Wi-Fi 站点的 SSID(服务集标识符)。
gl_sta_ssid_len:一个整数变量,用于存储 Wi-Fi 站点的 SSID 长度。
gl_sta_list:一个结构体变量,用于存储 Wi-Fi 站点列表信息。
gl_sta_is_connecting:一个布尔类型的变量,用于表示 Wi-Fi 站点当前是否正在连接中。
gl_sta_conn_info:一个结构体变量,用于存储与 Wi-Fi 站点连接相关的额外信息。
*/
static bool gl_sta_connected = false;
static bool gl_sta_got_ip = false;
static bool ble_is_connected = false;
static uint8_t gl_sta_bssid[6];
static uint8_t gl_sta_ssid[32];
static int gl_sta_ssid_len;
static wifi_sta_list_t gl_sta_list;
static bool gl_sta_is_connecting = false;
static esp_blufi_extra_info_t gl_sta_conn_info;
/*
该函数的作用是记录 Wi-Fi 连接的相关信息,并将其存储在 gl_sta_conn_info 变量中
gl_sta_is_connecting 为真(即 Wi-Fi 正在连接中)
gl_sta_is_connecting 为假,则表示 Wi-Fi 已连接或连接失败
*/
static void example_record_wifi_conn_info(int rssi, uint8_t reason)
{
    memset(&gl_sta_conn_info, 0, sizeof(esp_blufi_extra_info_t));
    if (gl_sta_is_connecting) {
        gl_sta_conn_info.sta_max_conn_retry_set = true;
        gl_sta_conn_info.sta_max_conn_retry = EXAMPLE_WIFI_CONNECTION_MAXIMUM_RETRY;
    } else {
        gl_sta_conn_info.sta_conn_rssi_set = true;
        gl_sta_conn_info.sta_conn_rssi = rssi;
        gl_sta_conn_info.sta_conn_end_reason_set = true;
        gl_sta_conn_info.sta_conn_end_reason = reason;
    }
}
/*启动 Wi-Fi 连接过程
esp_wifi_connect 函数来开始 Wi-Fi 连接。
如果连接成功,
gl_sta_is_connecting 变量会被设置为 true,否则为 false
*/
static void example_wifi_connect(void)
{
    example_wifi_retry = 0;
    gl_sta_is_connecting = (esp_wifi_connect() == ESP_OK);
    example_record_wifi_conn_info(EXAMPLE_INVALID_RSSI, EXAMPLE_INVALID_REASON);
}
/*
尝试重新连接 Wi-Fi
调用 example_record_wifi_conn_info 函数,
将连接信息记录到 gl_sta_conn_info 变量中。
这里传递的 rssi 参数为 EXAMPLE_INVALID_RSSI,表示初始信号强度未知;reason 参数为 EXAMPLE_INVALID_REASON,表示连接结束原因未知。
将 ret 设置为真,表示重新连接成功。
*/
static bool example_wifi_reconnect(void)
{
    bool ret;
    if (gl_sta_is_connecting && example_wifi_retry++ < EXAMPLE_WIFI_CONNECTION_MAXIMUM_RETRY) {
        BLUFI_INFO("BLUFI WiFi starts reconnection\n");
        gl_sta_is_connecting = (esp_wifi_connect() == ESP_OK);
        example_record_wifi_conn_info(EXAMPLE_INVALID_RSSI, EXAMPLE_INVALID_REASON);
        ret = true;
    } else {
        ret = false;
    }
    return ret;
}

static int softap_get_current_connection_number(void)
{
    esp_err_t ret;
    ret = esp_wifi_ap_get_sta_list(&gl_sta_list);
    if (ret == ESP_OK)
    {
        return gl_sta_list.num;
    }

    return 0;
}
/*获取当前连接到 SoftAP(软件接入点)的设备数量。*/
static void ip_event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    wifi_mode_t mode;

    switch (event_id) {
    case IP_EVENT_STA_GOT_IP: {
        esp_blufi_extra_info_t info;

        xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
        esp_wifi_get_mode(&mode);

        memset(&info, 0, sizeof(esp_blufi_extra_info_t));
        memcpy(info.sta_bssid, gl_sta_bssid, 6);
        info.sta_bssid_set = true;
        info.sta_ssid = gl_sta_ssid;
        info.sta_ssid_len = gl_sta_ssid_len;
        gl_sta_got_ip = true;
        if (ble_is_connected == true) {
            esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, softap_get_current_connection_number(), &info);
        } else {
            BLUFI_INFO("1BLUFI BLE is not connected yet\n");
            if(is_configured==1)
            {
                printf("Wi-fi Connected\n");
                break;
            }
        }
        break;
    }
    default:
        break;
    }
    return;
}
/*
WiFi事件处理函数
当收到WIFI_EVENT_STA_START事件时,调用delegate_wifi_connecting_status()函数,表示WiFi正在连接,然后调用example_wifi_connect()函数开始连接WiFi。

当收到WIFI_EVENT_STA_CONNECTED事件时,表示WiFi已成功连接。设置gl_sta_connected为true,表示已连接,gl_sta_is_connecting为false,表示连接过程已结束。将连接的BSSID和SSID保存在全局变量gl_sta_bssid和gl_sta_ssid中,并更新gl_sta_ssid_len。然后调用delegate_wifi_connected_status()函数,表示WiFi已连接。

当收到WIFI_EVENT_STA_DISCONNECTED事件时,表示WiFi连接断开。如果gl_sta_connected为false并且example_wifi_reconnect()返回false,表示无法重新连接WiFi,则将gl_sta_is_connecting设置为false,表示连接过程已结束。调用delegate_wifi_disconnect_status()函数,表示WiFi连接失败。同时,记录断开连接的原因和信号强度。然后重置连接相关的全局变量,并清除WiFi连接标志位。

当收到WIFI_EVENT_AP_START事件时,表示启动了SoftAP模式。获取当前的WiFi模式,并根据情况发送连接报告给BLE设备。

当收到WIFI_EVENT_SCAN_DONE事件时,表示WiFi扫描完成。如果扫描到了AP,则将扫描结果发送给BLE设备。然后停止WiFi扫描,并释放扫描结果的内存。

当收到WIFI_EVENT_AP_STACONNECTED事件时,表示有设备连接到SoftAP。记录连接的设备MAC地址和AID。

当收到WIFI_EVENT_AP_STADISCONNECTED事件时,表示有设备断开了与SoftAP的连接。记录断开连接的设备MAC地址和AID。
*/
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    wifi_event_sta_connected_t *event;
    wifi_event_sta_disconnected_t *disconnected_event;
    wifi_mode_t mode;

    switch (event_id) {
    case WIFI_EVENT_STA_START:
        delegate_wifi_connecting_status();wifi正在连接LED快闪
        example_wifi_connect();
        break;
    case WIFI_EVENT_STA_CONNECTED:
        is_configured = 1;
        gl_sta_connected = true;
        gl_sta_is_connecting = false;
        event = (wifi_event_sta_connected_t*) event_data;
        memcpy(gl_sta_bssid, event->bssid, 6);
        memcpy(gl_sta_ssid, event->ssid, event->ssid_len);
        gl_sta_ssid_len = event->ssid_len;
        delegate_wifi_connected_status(); wifi连接LED熄灭
        break;
    case WIFI_EVENT_STA_DISCONNECTED:
        /* Only handle reconnection during connecting */
        is_configured = 0;
        if (gl_sta_connected == false && example_wifi_reconnect() == false) {
            gl_sta_is_connecting = false;
            delegate_wifi_disconnect_status(); wifi连接失败LED慢闪
            disconnected_event = (wifi_event_sta_disconnected_t*) event_data;
            example_record_wifi_conn_info(disconnected_event->rssi, disconnected_event->reason);
        }
        /* This is a workaround as ESP32 WiFi libs don't currently
           auto-reassociate. */
        gl_sta_connected = false;
        gl_sta_got_ip = false;
        memset(gl_sta_ssid, 0, 32);
        memset(gl_sta_bssid, 0, 6);
        gl_sta_ssid_len = 0;
        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
        break;
    case WIFI_EVENT_AP_START:
        esp_wifi_get_mode(&mode);
        /* TODO: get config or information of softap, then set to report extra_info */
        if (ble_is_connected == true) {
            if (gl_sta_connected) {
                esp_blufi_extra_info_t info;
                memset(&info, 0, sizeof(esp_blufi_extra_info_t));
                memcpy(info.sta_bssid, gl_sta_bssid, 6);
                info.sta_bssid_set = true;
                info.sta_ssid = gl_sta_ssid;
                info.sta_ssid_len = gl_sta_ssid_len;
                esp_blufi_send_wifi_conn_report(mode, gl_sta_got_ip ? ESP_BLUFI_STA_CONN_SUCCESS : ESP_BLUFI_STA_NO_IP, softap_get_current_connection_number(), &info);
            } else if (gl_sta_is_connecting) {
                esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONNECTING, softap_get_current_connection_number(), &gl_sta_conn_info);
            } else {
                esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, softap_get_current_connection_number(), &gl_sta_conn_info);
            }
        } else {
            BLUFI_INFO("123BLUFI BLE is not connected yet\n");
            printf("123\n");
        }
        break;
    case WIFI_EVENT_SCAN_DONE: {
        uint16_t apCount = 0;
        esp_wifi_scan_get_ap_num(&apCount);
        if (apCount == 0) {
            BLUFI_INFO("Nothing AP found");
            break;
        }
        wifi_ap_record_t *ap_list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount);
        if (!ap_list) {
            BLUFI_ERROR("malloc error, ap_list is NULL");
            break;
        }
        ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, ap_list));
        esp_blufi_ap_record_t * blufi_ap_list = (esp_blufi_ap_record_t *)malloc(apCount * sizeof(esp_blufi_ap_record_t));
        if (!blufi_ap_list) {
            if (ap_list) {
                free(ap_list);
            }
            BLUFI_ERROR("malloc error, blufi_ap_list is NULL");
            break;
        }
        for (int i = 0; i < apCount; ++i)
        {
            blufi_ap_list[i].rssi = ap_list[i].rssi;
            memcpy(blufi_ap_list[i].ssid, ap_list[i].ssid, sizeof(ap_list[i].ssid));
        }

        if (ble_is_connected == true) {
            esp_blufi_send_wifi_list(apCount, blufi_ap_list);
        } else {
            BLUFI_INFO("12 BLUFI BLE is not connected yet\n");
            printf("12\n");
        }

        esp_wifi_scan_stop();
        free(ap_list);
        free(blufi_ap_list);
        break;
    }
    case WIFI_EVENT_AP_STACONNECTED: {
        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
        BLUFI_INFO("station "MACSTR" join, AID=%d", MAC2STR(event->mac), event->aid);
        break;
    }
    case WIFI_EVENT_AP_STADISCONNECTED: {
        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
        BLUFI_INFO("station "MACSTR" leave, AID=%d", MAC2STR(event->mac), event->aid);
        break;
    }

    default:
        break;
    }
    return;
}
/*
这是一个初始化WiFi的函数,以下是对其操作的简要说明:

调用esp_netif_init()函数进行ESP-NETIF网络接口的初始化。

使用xEventGroupCreate()函数创建一个事件组,用于处理WiFi事件。

调用esp_event_loop_create_default()函数创建默认的事件循环。

调用esp_netif_create_default_wifi_sta()函数创建默认的STA(Station)模式网络接口,并将返回的网络接口指针保存在sta_netif中。

调用esp_netif_create_default_wifi_ap()函数创建默认的AP(Access Point)模式网络接口,并将返回的网络接口指针保存在ap_netif中。

调用esp_event_handler_register()函数注册WiFi事件处理函数wifi_event_handler,以处理所有WiFi事件。

调用esp_event_handler_register()函数注册IP事件处理函数ip_event_handler,以处理STA(Station)获取IP地址的事件。

创建一个默认的WiFi初始化配置结构体cfg,并通过调用esp_wifi_init()函数进行WiFi初始化。

调用esp_wifi_set_mode()函数将WiFi模式设置为STA模式。

调用example_record_wifi_conn_info()函数记录无效的RSSI(信号强度)和无效的连接原因。

调用esp_wifi_start()函数启动WiFi。
*/
static void initialise_wifi(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    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);
    esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
    assert(ap_netif);
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL));

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    example_record_wifi_conn_info(EXAMPLE_INVALID_RSSI, EXAMPLE_INVALID_REASON);
    ESP_ERROR_CHECK( esp_wifi_start() );
}
/*
这是一个ESP-BLE-MESH蓝牙Mesh设备中的蓝牙低功耗(BLE)配置回调函数结构体esp_blufi_callbacks_t的初始化。以下是对其成员函数的简要说明:

.event_cb:指向用于处理蓝牙事件的回调函数example_event_callback的指针。

.negotiate_data_handler:指向用于处理数据协商的回调函数blufi_dh_negotiate_data_handler的指针。

.encrypt_func:指向用于加密数据的回调函数blufi_aes_encrypt的指针。

.decrypt_func:指向用于解密数据的回调函数blufi_aes_decrypt的指针。

.checksum_func:指向用于计算校验和的回调函数blufi_crc_checksum的指针。*/
static esp_blufi_callbacks_t example_callbacks = {
    .event_cb = example_event_callback,
    .negotiate_data_handler = blufi_dh_negotiate_data_handler,
    .encrypt_func = blufi_aes_encrypt,
    .decrypt_func = blufi_aes_decrypt,
    .checksum_func = blufi_crc_checksum,
};
/*
这是一个使用 ESP32 蓝牙功能的示例代码中的事件回调函数。该函数根据不同的事件类型执行相应的操作。

- `ESP_BLUFI_EVENT_INIT_FINISH`:蓝牙功能初始化完成时调用,启动广播。
- `ESP_BLUFI_EVENT_DEINIT_FINISH`:蓝牙功能关闭完成时调用。
- `ESP_BLUFI_EVENT_BLE_CONNECT`:蓝牙连接建立时调用,停止广播,并初始化安全设置。
- `ESP_BLUFI_EVENT_BLE_DISCONNECT`:蓝牙连接断开时调用,重新开始广播,并清除安全设置。
- `ESP_BLUFI_EVENT_SET_WIFI_OPMODE`:设置 Wi-Fi 工作模式时调用,根据参数设置 ESP8266 的 Wi-Fi 模式。
- `ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP`:请求连接到 AP 时调用,断开当前 Wi-Fi 连接并连接到指定 AP。
- `ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP`:请求断开与 AP 的连接时调用。
- `ESP_BLUFI_EVENT_REPORT_ERROR`:报告错误时调用,发送错误信息给手机端。
- `ESP_BLUFI_EVENT_GET_WIFI_STATUS`:获取 Wi-Fi 状态时调用,发送当前 Wi-Fi 连接状态给手机端。
- `ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE`:接收到从设备断开连接的事件时调用,断开与从设备的 GATT 连接。
- `ESP_BLUFI_EVENT_DEAUTHENTICATE_STA`:待实现的事件,用于取消认证的 STA。
- `ESP_BLUFI_EVENT_RECV_STA_BSSID`:接收到 STA 的 BSSID 时调用,设置 STA 配置的 BSSID。
- `ESP_BLUFI_EVENT_RECV_STA_SSID`:接收到 STA 的 SSID 时调用,设置 STA 配置的 SSID。
- `ESP_BLUFI_EVENT_RECV_STA_PASSWD`:接收到 STA 的密码时调用,设置 STA 配置的密码。
- `ESP_BLUFI_EVENT_RECV_SOFTAP_SSID`:接收到 SoftAP 的 SSID 时调用,设置 SoftAP 的 SSID。
- `ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD`:接收到 SoftAP 的密码时调用,设置 SoftAP 的密码。
- `ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM`:接收到 SoftAP 的最大连接数时调用,设置 SoftAP 的最大连接数。
- `ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE`:接收到 SoftAP 的认证模式时调用,设置 SoftAP 的认证模式。
- `ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL`:接收到 SoftAP 的信道时调用,设置 SoftAP 的信道。
- `ESP_BLUFI_EVENT_GET_WIFI_LIST`:获取 Wi-Fi 列表时调用,开启 Wi-Fi 扫描并发送扫描结果给手机端。
- `ESP_BLUFI_EVENT_RECV_CUSTOM_DATA`:接收到自定义数据时调用,打印数据内容。
- `ESP_BLUFI_EVENT_RECV_USERNAME`:待处理的事件,用于接收用户名。
- `ESP_BLUFI_EVENT_RECV_CA_CERT`:待处理的事件,用于接收 CA 证书。
- `ESP_BLUFI_EVENT_RECV_CLIENT_CERT`:待处理的事件,用于接收客户端证书。
- `ESP_BLUFI_EVENT_RECV_SERVER_CERT`:待处理的事件,用于接收服务器证书。
- `ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY`:待处理的事件,用于接收客户端私钥。
- `ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY`:待处理的事件,用于接收服务器私钥。
*/
static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param)
{
    /* actually, should post to blufi_task handle the procedure,
     * now, as a example, we do it more simply */
    switch (event) {
    case ESP_BLUFI_EVENT_INIT_FINISH:
        BLUFI_INFO("BLUFI init finish\n");
        esp_blufi_adv_start();
        break;
    case ESP_BLUFI_EVENT_DEINIT_FINISH:
        BLUFI_INFO("BLUFI deinit finish\n");
        break;
    case ESP_BLUFI_EVENT_BLE_CONNECT:
        BLUFI_INFO("BLUFI ble connect\n");
        ble_is_connected = true;
        esp_blufi_adv_stop();
        blufi_security_init();
        break;
    case ESP_BLUFI_EVENT_BLE_DISCONNECT:
        BLUFI_INFO("BLUFI ble disconnect\n");
        ble_is_connected = false;
        blufi_security_deinit();
        esp_blufi_adv_start();
        break;
    case ESP_BLUFI_EVENT_SET_WIFI_OPMODE:
        BLUFI_INFO("BLUFI Set WIFI opmode %d\n", param->wifi_mode.op_mode);
        ESP_ERROR_CHECK( esp_wifi_set_mode(param->wifi_mode.op_mode) );
        break;
    case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP:
        BLUFI_INFO("BLUFI requset wifi connect to AP\n");
        /* there is no wifi callback when the device has already connected to this wifi
        so disconnect wifi before connection.
        */
        esp_wifi_disconnect();
        example_wifi_connect();
        break;
    case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP:
        BLUFI_INFO("BLUFI requset wifi disconnect from AP\n");
        esp_wifi_disconnect();
        break;
    case ESP_BLUFI_EVENT_REPORT_ERROR:
        BLUFI_ERROR("BLUFI report error, error code %d\n", param->report_error.state);
        esp_blufi_send_error_info(param->report_error.state);
        break;
    case ESP_BLUFI_EVENT_GET_WIFI_STATUS: {
        wifi_mode_t mode;
        esp_blufi_extra_info_t info;

        esp_wifi_get_mode(&mode);

        if (gl_sta_connected) {
            memset(&info, 0, sizeof(esp_blufi_extra_info_t));
            memcpy(info.sta_bssid, gl_sta_bssid, 6);
            info.sta_bssid_set = true;
            info.sta_ssid = gl_sta_ssid;
            info.sta_ssid_len = gl_sta_ssid_len;
            esp_blufi_send_wifi_conn_report(mode, gl_sta_got_ip ? ESP_BLUFI_STA_CONN_SUCCESS : ESP_BLUFI_STA_NO_IP, softap_get_current_connection_number(), &info);
        } else if (gl_sta_is_connecting) {
            esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONNECTING, softap_get_current_connection_number(), &gl_sta_conn_info);
        } else {
            esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, softap_get_current_connection_number(), &gl_sta_conn_info);
        }
        BLUFI_INFO("BLUFI get wifi status from AP\n");

        break;
    }
    case ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE:
        BLUFI_INFO("blufi close a gatt connection");
        esp_blufi_disconnect();
        break;
    case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA:
        /* TODO */
        break;
	case ESP_BLUFI_EVENT_RECV_STA_BSSID:
        memcpy(sta_config.sta.bssid, param->sta_bssid.bssid, 6);
        sta_config.sta.bssid_set = 1;
        esp_wifi_set_config(WIFI_IF_STA, &sta_config);
        BLUFI_INFO("Recv STA BSSID %s\n", sta_config.sta.ssid);
        break;
	case ESP_BLUFI_EVENT_RECV_STA_SSID:
        strncpy((char *)sta_config.sta.ssid, (char *)param->sta_ssid.ssid, param->sta_ssid.ssid_len);
        sta_config.sta.ssid[param->sta_ssid.ssid_len] = '\0';
        esp_wifi_set_config(WIFI_IF_STA, &sta_config);
        BLUFI_INFO("Recv STA SSID %s\n", sta_config.sta.ssid);
        break;
	case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
        strncpy((char *)sta_config.sta.password, (char *)param->sta_passwd.passwd, param->sta_passwd.passwd_len);
        sta_config.sta.password[param->sta_passwd.passwd_len] = '\0';
        esp_wifi_set_config(WIFI_IF_STA, &sta_config);
        BLUFI_INFO("Recv STA PASSWORD %s\n", sta_config.sta.password);
        break;
	case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID:
        strncpy((char *)ap_config.ap.ssid, (char *)param->softap_ssid.ssid, param->softap_ssid.ssid_len);
        ap_config.ap.ssid[param->softap_ssid.ssid_len] = '\0';
        ap_config.ap.ssid_len = param->softap_ssid.ssid_len;
        esp_wifi_set_config(WIFI_IF_AP, &ap_config);
        BLUFI_INFO("Recv SOFTAP SSID %s, ssid len %d\n", ap_config.ap.ssid, ap_config.ap.ssid_len);
        break;
	case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD:
        strncpy((char *)ap_config.ap.password, (char *)param->softap_passwd.passwd, param->softap_passwd.passwd_len);
        ap_config.ap.password[param->softap_passwd.passwd_len] = '\0';
        esp_wifi_set_config(WIFI_IF_AP, &ap_config);
        BLUFI_INFO("Recv SOFTAP PASSWORD %s len = %d\n", ap_config.ap.password, param->softap_passwd.passwd_len);
        break;
	case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM:
        if (param->softap_max_conn_num.max_conn_num > 4) {
            return;
        }
        ap_config.ap.max_connection = param->softap_max_conn_num.max_conn_num;
        esp_wifi_set_config(WIFI_IF_AP, &ap_config);
        BLUFI_INFO("Recv SOFTAP MAX CONN NUM %d\n", ap_config.ap.max_connection);
        break;
	case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE:
        if (param->softap_auth_mode.auth_mode >= WIFI_AUTH_MAX) {
            return;
        }
        ap_config.ap.authmode = param->softap_auth_mode.auth_mode;
        esp_wifi_set_config(WIFI_IF_AP, &ap_config);
        BLUFI_INFO("Recv SOFTAP AUTH MODE %d\n", ap_config.ap.authmode);
        break;
	case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL:
        if (param->softap_channel.channel > 13) {
            return;
        }
        ap_config.ap.channel = param->softap_channel.channel;
        esp_wifi_set_config(WIFI_IF_AP, &ap_config);
        BLUFI_INFO("Recv SOFTAP CHANNEL %d\n", ap_config.ap.channel);
        break;
    case ESP_BLUFI_EVENT_GET_WIFI_LIST:{
        wifi_scan_config_t scanConf = {
            .ssid = NULL,
            .bssid = NULL,
            .channel = 0,
            .show_hidden = false
        };
        esp_err_t ret = esp_wifi_scan_start(&scanConf, true);
        if (ret != ESP_OK) {
            esp_blufi_send_error_info(ESP_BLUFI_WIFI_SCAN_FAIL);
        }
        break;
    }
    case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
        BLUFI_INFO("Recv Custom Data %" PRIu32 "\n", param->custom_data.data_len);
        esp_log_buffer_hex("Custom Data", param->custom_data.data, param->custom_data.data_len);
        break;
	case ESP_BLUFI_EVENT_RECV_USERNAME:
        /* Not handle currently */
        break;
	case ESP_BLUFI_EVENT_RECV_CA_CERT:
        /* Not handle currently */
        break;
	case ESP_BLUFI_EVENT_RECV_CLIENT_CERT:
        /* Not handle currently */
        break;
	case ESP_BLUFI_EVENT_RECV_SERVER_CERT:
        /* Not handle currently */
        break;
	case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY:
        /* Not handle currently */
        break;;
	case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY:
        /* Not handle currently */
        break;
    default:
        break;
    }
}
/**********************************************
 * *函数名:clearWifiConfigFlag
 * *功能描述:清除配网标记,如果运行这个函数,可以配合esp_restart(),复位系统。重新配网
 * *   --  主要是取代nvs_flash_erase()函数,这个函数把所有的数据都擦除,是不对的。
 * *******************************************/
void clearWifiConfigFlag(void)
{
    nvs_handle my_handle;
    //  0.打开
    nvs_open("WIFI_CONFIG", NVS_READWRITE, &my_handle); 
    //  1.写入标记 0x00,清除配网标记
    nvs_set_u8(my_handle, "WifiConfigFlag", wifi_unconfiged);
    //  2.提交 并保存表的内容
    ESP_ERROR_CHECK(nvs_commit(my_handle)); 
    //  3.退出
    nvs_close(my_handle);  
}

void Init_blufi(void)
{
    //printf("is_configured = %d\n", is_configured);
    if(is_configured==0)
    {
        esp_err_t ret;

        // Initialize NVS
        ret = nvs_flash_init();
        if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
            ESP_ERROR_CHECK(nvs_flash_erase());
            ret = nvs_flash_init();
        }
        ESP_ERROR_CHECK( ret );

        initialise_wifi();
        ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));

        esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
        ret = esp_bt_controller_init(&bt_cfg);
        if (ret) {
            BLUFI_ERROR("%s initialize bt controller failed: %s\n", __func__, esp_err_to_name(ret));
        }

        ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
        if (ret) {
            BLUFI_ERROR("%s enable bt controller failed: %s\n", __func__, esp_err_to_name(ret));
            return;
        }

        ret = esp_blufi_host_and_cb_init(&example_callbacks);
        if (ret) {
            BLUFI_ERROR("%s initialise failed: %s\n", __func__, esp_err_to_name(ret));
            return;
        }

        BLUFI_INFO("BLUFI VERSION %04x\n", esp_blufi_get_version());
        printf("is_configured = %d\n", is_configured);
    }
    else if (is_configured==1) 
    {
        //printf("is_configured = %d\n", is_configured);
        printf("wifi connected\n");
    }
    

    
}

MQTT连接参数

然后使用MQTT签名工具获取以下信息

将上面的信息进行对应替换

如何计算MQTT签名参数_阿里云物联网平台-阿里云帮助中心 (aliyun.com)

MQTT连接代码

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.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"
#include "os.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "mqtt.h"
esp_mqtt_client_handle_t client;
static const char *TAG = "MQTT_EXAMPLE";

#define   Aliyun_host       "h54wTCsZDZU.iot-as-mqtt.cn-shanghai.aliyuncs.com"
#define   Aliyun_port       1883

#define   Aliyun_client_id  "LCH|securemode=2,signmethod=hmacsha1,timestamp=1694742596956|"
#define   Aliyun_username   "Device_1&h54wTCsZDZU"
#define   Aliyun_password   "75B16F77A2FA77FF3CA5ACADF984928E7F2AF018"



#define   AliyunSubscribeTopic_user_get     "/h54wTCsZDZU/Device_1/user/get"
#define   AliyunPublishTopic_user_update    "/h54wTCsZDZU/Device_1/user/update"
//#define   AliyunSubscribeTopic_post   "/sys/h54wTCsZDZU/Device_1/thing/event/property/post"
//#define   AliyunSubscribeTopic_post_reply   "/sys/h54wTCsZDZU/Device_1/thing/event/property/post_reply"



char mqtt_message[256]={0};
char mqtt_publish_data1[] = "mqtt connect ok ";
char mqtt_publish_data2[] = "mqtt subscribe successful";
char mqtt_publish_data3[] = "mqtt i am esp32";

/*
这是一个用于检查错误代码并打印错误信息的辅助函数。下面是函数的具体解析:

函数接受两个参数:message 是用于描述错误的字符串,error_code 是要检查的错误代码。
函数会检查 error_code 是否为非零值。如果是非零值,表示发生了错误。
如果发生了错误,函数会使用 ESP-IDF 的日志函数打印错误信息。错误信息包括 message 字符串和十六进制表示的错误代码。
如果 error_code 为零,表示没有发生错误,函数不会执行任何操作。
*/
static void log_error_if_nonzero(const char *message, int error_code)
{
    if (error_code != 0) {
        ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
    }
}

/*
 这是一个用于处理 MQTT 事件的回调函数。下面是函数的具体解析:

函数接受四个参数:handler_args 是回调函数的参数,base 是事件基础类型,event_id 是事件 ID,event_data 是事件数据。
函数会使用 ESP-IDF 的日志函数打印事件基础类型和事件 ID。
根据事件 ID 的不同,函数会执行相应的操作:
如果事件 ID 是 MQTT_EVENT_CONNECTED,表示已连接到 MQTT 服务器。
函数会发布一条主题为 AliyunPublishTopic_user_update,负载为 "mqtt_publish_data1" 的消息,并记录消息的 ID。
如果事件 ID 是 MQTT_EVENT_DISCONNECTED,表示与 MQTT 服务器的连接断开。
如果事件 ID 是 MQTT_EVENT_SUBSCRIBED,表示成功订阅了一个主题。
函数会发布一条主题为 AliyunPublishTopic_user_update,负载为 "mqtt_publish_data2" 的消息,并记录消息的 ID。
如果事件 ID 是 MQTT_EVENT_UNSUBSCRIBED,表示成功取消订阅了一个主题。
如果事件 ID 是 MQTT_EVENT_PUBLISHED,表示成功发布了一条消息。
如果事件 ID 是 MQTT_EVENT_DATA,表示接收到了一条消息。函数会打印主题和消息的内容。
如果事件 ID 是 MQTT_EVENT_ERROR,表示发生了 MQTT 错误。函数会根据错误类型打印相应的错误信息。
如果事件 ID 是其他值,表示其他类型的事件,函数会打印事件 ID。
该回调函数通过检测不同的 MQTT 事件来执行相应的操作。在使用 ESP-IDF 的 MQTT 客户端时,你可以将该回调函数注册为事件处理程序,并在接收到 MQTT 事件时进行相应的处理。例如,在连接到 MQTT 服务器后,你可以在 MQTT_EVENT_CONNECTED 事件中发布消息或者订阅主题,在收到消息时处理 MQTT_EVENT_DATA 事件。这样可以方便地与 MQTT 服务器进行通信和处理消息。*/
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id);
    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    switch ((esp_mqtt_event_id_t)event_id) {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
        msg_id = esp_mqtt_client_publish(client, AliyunPublishTopic_user_update, mqtt_publish_data1, strlen(mqtt_publish_data1), 1, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);

        msg_id = esp_mqtt_client_subscribe(client, AliyunSubscribeTopic_user_get, 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
        break;

    case MQTT_EVENT_SUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
        msg_id = esp_mqtt_client_publish(client, AliyunPublishTopic_user_update, mqtt_publish_data2, strlen(mqtt_publish_data2), 0, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        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:
        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;
    }
}


void user_mqtt_app_start(void)
{

    esp_mqtt_client_config_t mqtt_cfg = {
        .broker.address.hostname = Aliyun_host,
        .broker.address.port = Aliyun_port,
        .broker.address.transport = MQTT_TRANSPORT_OVER_TCP,
        .credentials.client_id = Aliyun_client_id,
        .credentials.username = Aliyun_username,
        .credentials.authentication.password = Aliyun_password,

    };

    client = esp_mqtt_client_init(&mqtt_cfg);
     esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
     esp_mqtt_client_start(client);
}

效果

完整代码:ESP32 IDF Vscode: ESP32开发 (gitee.com)

相关推荐
老刘莱国瑞2 小时前
STM32 与 AS608 指纹模块的调试与应用
python·物联网·阿里云
小林coding4 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
Anna_Tong4 小时前
云原生大数据计算服务 MaxCompute 是什么?
大数据·阿里云·云原生·maxcompute·odps
☆凡尘清心☆6 小时前
CentOS上安装NTP并配置时间同步
linux·阿里云·centos
web150854159359 小时前
vue 集成 webrtc-streamer 播放视频流 - 解决阿里云内外网访问视频流问题
vue.js·阿里云·webrtc
向宇it10 小时前
【从零开始入门unity游戏开发之——unity篇02】unity6基础入门——软件下载安装、Unity Hub配置、安装unity编辑器、许可证管理
开发语言·unity·c#·编辑器·游戏引擎
徐小夕@趣谈前端10 小时前
可视化大屏编辑器, 开源!
编辑器
天天进步201510 小时前
TipTap编辑器:现代化的富文本编辑解决方案
编辑器
o(╥﹏╥)13 小时前
在 Ubuntu 上安装 VS Code
linux·运维·vscode·ubuntu·vs
18号房客14 小时前
macOS开发环境配置与应用开发教程(一)
vscode·macos·visualstudio·eclipse·intellij-idea·phpstorm·visual studio