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

news2025/1/13 15:49:08

阿里云平台的设置

参考文章:

http://t.csdn.cn/RzLGqicon-default.png?t=N7T8http://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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1016774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenGL超级宝典(第五版)疑难点汇总解析

《OpenGL超级宝典(第五版&#xff09;》如下&#xff1a; 1. 在该书的第1章的Block例子中用到了平面阴影投射矩阵&#xff0c;关于该矩阵的推导&#xff0c;参见&#xff1a; OpenGL: 平面阴影投射矩阵的推导 2. 在该书的第8章的pix_buffs例子中用到了正交投影矩阵的推导过程…

腾讯mini项目-【指标监控服务重构】2023-08-23

今日已办 进度和问题汇总 请求合并 feature/venus tracefeature/venus metricfeature/profile-otel-baserunner-stylebugfix/profile-logger-Syncfeature/profile_otelclient_enable_config 完成otel 开关 trace-采样metrice-reader 已经都在各自服务器运行&#xff0c;并接入…

Datagrip 下载、安装教程,详细图文,亲测有效

文章目录 前言1. 下载2. 安装3 DataGrip 常用操作4 推荐阅读 前言 DataGrip 是 JetBrains 发布的多引擎数据库环境&#xff0c;支持 MySQL 和 PostgreSQL&#xff0c;Microsoft SQL Server 和 Oracle&#xff0c;Sybase&#xff0c;DB2&#xff0c;SQLite&#xff0c;还有 Hyp…

RocketMQ快速实战以及集群架构详解

文章目录 1、MQ简介1.1 、定义1.2 、作用 2、RocketMQ产品特点2.1、RocketMQ介绍2.2 、RocketMQ特点 1、MQ简介 1.1 、定义 ​ MQ&#xff1a;MessageQueue&#xff0c;消息队列。是在互联网中使用非常广泛的一系列服务中间件。 这个词可以分两个部分来看 一是Message&#…

SmartApi使用说明

缘起&#xff1a; 搞移动开发十多年了&#xff0c;接口、数据、数据模型、以及数据边界值的处理是需要团队协作解决&#xff0c;而这方面恰恰是总容易导致bug难修复的地方。而一款好用的api调试工具对于后端、前端、测试都是必须必的掌握熟练使用&#xff0c;Api就像人体的血管…

开发、测试、生产环境

开发环境&#xff08;Development Environment&#xff09;&#xff1a; 开发环境是用于开发新功能、修改和调试代码的环境。 在开发环境中&#xff0c;开发人员可以针对特定需求编写和测试代码。 通常&#xff0c;开发环境会模拟完整的系统环境&#xff0c;并提供开发人员所需…

SQL 性能优化总结

文章目录 一、性能优化策略二、索引创建规则三、查询优化总结 一、性能优化策略 1. SQL 语句中 IN 包含的值不应过多 MySQL 将 IN中的常量全部存储在一个排好序的数组里面&#xff0c;但是如果数值较多&#xff0c;产生的消耗也是比较大的。所以对于连续的数值&#xff0c;能用…

手撕排序之堆排序

一、概念&#xff1a; 什么是逻辑结构、物理结构&#xff1f; 逻辑结构&#xff1a;是我们自己想象出来的&#xff0c;就像内存中不存在一个真正的树 物理结构(存储结构)&#xff1a;实际上在内存中存储的形式。 堆的逻辑结构是一颗完全二叉树 堆的物理结构是一个数组 之…

常见请求方法

请求方法的本质 请求方法是请求行中的第一个单词&#xff0c;它向服务器描述了客户端发出请求的动作类型。在 HTTP 协议中&#xff0c;不同的请求方法只是包含了不同的语义&#xff0c;但服务器和浏览器的一些约定俗成的行为造成了它们具体的区别 fetch(https://www.baidu.com…

【算法|双指针|链表】反转链表

Leetcode206 反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例…

opencv 处理扫描件移除灰色背景图

先看对比效果: 再上代码: import cv2 import numpy as npdef remove_gray_background(input_image_path, output_image_path, threshold180):# Load the input imageimage cv2.imread(input_image_path)# Convert the image to grayscalegray cv2.cvtColor(image, cv2.COLO…

【数据可视化】动态条形图Python代码实现

使用 Python 中的 bar_chart_race_cn 库创建动态条形图 前言 数据可视化在今天的数据分析和传达信息中起着至关重要的作用。动态条形图是一种强大的数据可视化工具&#xff0c;可以帮助我们展示随时间变化的数据趋势。本文将介绍如何使用 Python 编程语言中的 bar_chart_race…

阿里云yum源和tuna源

阿里云开源镜像站地址&#xff1a;阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区阿里巴巴开源镜像站&#xff0c;免费提供Linux镜像下载服务&#xff0c;拥有Ubuntu、CentOS、Deepin、MongoDB、Apache、Maven、Composer等多种开源软件镜像源&#xff0c;此外还提供域名解析D…

背包问题学习笔记-01背包

背景 背包问题是动态规划问题中的一个大类&#xff0c;学习背包问题对于掌握动态规划十分重要。背包问题也很容易成为程序员算法面试中的一个槛&#xff0c;但其实背包问题已经被研究&#xff0c;讲解的比较成熟了&#xff0c;在这些丰富的讲解资料的基础之上&#xff0c;大家…

图注意网络(GAT)的可视化实现详解

能够可视化的查看对于理解图神经网络(gnn)越来越重要&#xff0c;所以在这篇文章中&#xff0c;我将介绍传统GNN层的实现&#xff0c;然后展示ICLR论文“图注意力网络”中对传统GNN层的改进。 假设我们有一个表示为有向无环图(DAG)的文本文档图。文档0与文档1、2和3有一条边&am…

第72步 时间序列建模实战:单步滚动预测(以决策树回归为例)

基于WIN10的64位系统演示 一、写在前面 从这一期开始&#xff0c;我们开始基于python构建各种机器学习和深度学习的时间序列预测模型&#xff0c;本质上就是调用各种模型的回归分析的属性。所以很多模型其实之前都介绍过&#xff0c;比如说决策树、SVM等等。 同样&#xff0…

【踩坑篇】代码中使用 Long 作为 Map的Key存在的问题

本周的工作结束&#xff0c;详述一些在项目代码中实际遇到的一些坑。 代码中遇到这样一个场景&#xff1a; 有个业务接口&#xff0c;接口返回的值是一个JSON格式的字符串&#xff0c;通过JSON解析的方式&#xff0c;解析为格式为&#xff1a; Map<Long, Map<String, O…

数据结构——时间复杂度与空间复杂度

目录 一.什么是空间复杂度与时间复杂度 1.1算法效率 1.2时间复杂度的概念 1.3空间复杂度的概念 二.如何计算常见算法的时间复杂度 2.1大O的渐近表示法 使用规则 三.如何计算常见算法的空间复杂度 3.1 大O渐近表示法 3.2 面试题——消失的数字 3.3 面试题——旋转数组 一…

ChatGPT是留学生的论文神器还是学术不端的罪魁祸首?

当今时代&#xff0c;ChatGPT无疑是大数据和人工智能的完美结合&#xff0c;成为了搜索技术的革命性创新。几秒钟&#xff0c;一篇逻辑清晰、观点鲜明、有充分论据支持的文章即可生成。这种革命性的创新在学术界掀起了巨大的浪潮&#xff0c;甚至让全球的学校开始思考&#xff…

【GAN入门】生成 AI的概念

一、说明 GAN是生成对抗网络&#xff08;Generative Adversarial Network&#xff09;的缩写&#xff0c;是一种无监督学习算法&#xff0c;由Goodfellow等人于2014年提出。GAN由一个生成器网络和一个判别器网络组成&#xff0c;通过二者之间的对抗来训练生成器网络生成与真实样…