美文网首页ESP32
ESP32学习笔记(36)——BluFi(蓝牙配网)接口使用

ESP32学习笔记(36)——BluFi(蓝牙配网)接口使用

作者: Leung_ManWah | 来源:发表于2021-07-21 15:41 被阅读0次

    一、简介

    ESP32 的 BluFi 是通过蓝牙通道的 Wi-Fi 网络配置功能。它提供了一个安全协议来将 Wi-Fi 配置和凭据传递给 ESP32。使用这些信息,ESP32 可以连接到一个 AP 或建立一个 SoftAP。

    BluFi 层中的分片、数据加密、校验和验证是此过程的关键要素。

    您可以自定义对称加密、非对称加密和校验和支持自定义。这里我们使用DH算法进行密钥协商,128-AES算法进行数据加密,CRC16算法进行校验和验证。

    二、BluFi流程

    1. 将 ESP32 设置为 GATT Server 模式,然后它将发送带有特定广告数据的广播。您可以根据需要自定义此广播,这不是 BluFi 配置文件的一部分。
    2. 使用安装在手机上的应用程序搜索此特定广播。确认广播后,手机将作为 GATT Client 连接到 ESP32。这部分使用的应用程序由您决定。
    3. GATT连接建立成功后,手机会向ESP32发送密钥协商的数据帧(详见BluFi中定义的帧格式部分)。
    4. ESP32 收到密钥协商的数据帧后,会根据用户自定义的协商方式解析内容
    5. 手机配合ESP32使用DH、RSA或ECC等加密算法进行密钥协商
    6. 协商完成后,手机会向ESP32发送安全模式设置的控制帧
    7. ESP32 收到此控制帧后,将能够使用共享密钥和安全配置对通信数据进行加密和解密
    8. 手机将BluFi中定义的帧格式部分定义的数据帧连同Wi-Fi配置信息发送到ESP32,包括SSID、密码等。
    9. 手机向ESP32发送Wi-Fi连接请求的控制帧。ESP32 收到此控制帧后,会认为基本信息的通信已完成,并准备连接到 Wi-Fi
    10. 连接到 Wi-Fi 后,ESP32 会向手机发送 Wi-Fi 连接状态报告控制帧,以报告连接状态。至此,组网过程完成。

    注意:

    • ESP32 收到安全模式配置的控制帧后,会按照定义的安全模式执行操作。
    • 对称加密/解密前后的数据长度必须保持不变。它还支持就地加密和解密。

    三、API说明

    以下 BluFi 接口位于 bt/common/api/include/api/esp_blufi_api.h

    3.1 esp_blufi_register_callbacks

    3.2 esp_blufi_profile_init

    3.3 esp_blufi_send_wifi_conn_report

    3.4 esp_blufi_send_wifi_list

    3.5 esp_blufi_send_error_info

    3.6 esp_blufi_send_custom_data

    四、程序结构

    使用 esp-idf\examples\bluetooth\bluedroid\ble\blufi 中的例程

    4.1 四个事件处理

    4.1.1 WIFI部分事件处理

    主要负责WIFI的连接断开重连扫描

    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_mode_t mode;
    
        switch (event_id) {
        case WIFI_EVENT_STA_START:
            esp_wifi_connect();
            break;
        case WIFI_EVENT_STA_CONNECTED:
            gl_sta_connected = true;
            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;
            break; 
        case WIFI_EVENT_STA_DISCONNECTED:
            /* This is a workaround as ESP32 WiFi libs don't currently
               auto-reassociate. */
            gl_sta_connected = false;
            memset(gl_sta_ssid, 0, 32);
            memset(gl_sta_bssid, 0, 6);
            gl_sta_ssid_len = 0;
            esp_wifi_connect();
            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_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, NULL);
                } else {
                    esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL);
                }
            } else {
                BLUFI_INFO("BLUFI BLE is not connected yet\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("BLUFI BLE is not connected yet\n");
            }
    
            esp_wifi_scan_stop();
            free(ap_list);
            free(blufi_ap_list);
            break;
        }
        default:
            break;
        }
        return;
    }
    

    4.1.2 NETIF部分事件处理

    获取网络IP地址,完成IP接口搭建(默认IO口);
    更多netif功能介绍与使用参考链接:ESP-NETIF

    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;
            if (ble_is_connected == true) {
                esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info);
            } else {
                BLUFI_INFO("BLUFI BLE is not connected yet\n");
            }
            break;
        }
        default:
            break;
        }
        return;
    }
    

    4.1.3 BLUFI配网部分事件处理

    此过程事件的处理均按照收到的请求作相应的功能处理,可按照个人需求进行修改

    • ESP_BLUFI_EVENT_INIT_FINISH:完成blufi功能初始化,设置设备名称(Device Name) 并发送特定的 adv data 广播;
    • ESP_BLUFI_EVENT_DEINIT_FINISH:处理deinit配置事件;
    • ESP_BLUFI_EVENT_BLE_CONNECT:连接Blufi Ble,并设备进入安全模式;
    • ESP_BLUFI_EVENT_BLE_DISCONNECT:设置ble断开重连;
    • ESP_BLUFI_EVENT_SET_WIFI_OPMODE:设置WiFi进入运行模式——op_mode;
    • ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP:设置断开原有的WiFi连接,并连接指定WiFi;
    • ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP:断开当前WiIFi连接到的AP;
    • ESP_BLUFI_EVENT_REPORT_ERROR:上报错误信息;
    • ESP_BLUFI_EVENT_GET_WIFI_STATUS:获取WiFi状态信息,包括:WiFi当前模式、以及是否连接成功;
    • ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE:关闭blufi的gatt服务连接;
    • ESP_BLUFI_EVENT_RECV_STA_BSSID:设置进入STA模式,获取目标AP的bssid;
    • ESP_BLUFI_EVENT_RECV_STA_SSID:设置进入STA模式,获取目标AP的WiFi账号;
    • ESP_BLUFI_EVENT_RECV_STA_PASSWD:设置进入STA模式,获取目标AP的WiFi密码;
    • ESP_BLUFI_EVENT_RECV_SOFTAP_SSID:设置进入Soft AP模式,获取AP自定义账号;
    • ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD:设置进入Soft AP模式,获取AP自定义密码;
    • ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM:设置Soft AP模式下最大可连接设备数量;
    • ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE:设置Soft AP模式下进入认证模式;
    • ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL:设置Soft AP模式下的通讯通道;
    • ESP_BLUFI_EVENT_GET_WIFI_LIST:获取扫描到的空中WiFi账号、通信通道以及站点MAC地址;
    • ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:将接收到的数据打印出来;
    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_ble_gap_set_device_name(BLUFI_DEVICE_NAME);
            esp_ble_gap_config_adv_data(&example_adv_data);
            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;
            server_if = param->connect.server_if;
            conn_id = param->connect.conn_id;
            esp_ble_gap_stop_advertising();
            blufi_security_init();
            break;
        case ESP_BLUFI_EVENT_BLE_DISCONNECT:
            BLUFI_INFO("BLUFI ble disconnect\n");
            ble_is_connected = false;
            blufi_security_deinit();
            esp_ble_gap_start_advertising(&example_adv_params);
            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();
            esp_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, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info);
            } else {
                esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL);
            }
            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_close(server_if, conn_id);
            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_ERROR_CHECK(esp_wifi_scan_start(&scanConf, true));
            break;
        }
        case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
            BLUFI_INFO("Recv Custom Data %d\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;
        }
    }
    

    4.1.4 GAP广播部分事件处理

    用于当adv data数据报组装完成以后发送adv data广播

    static void example_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
    {
        switch (event) {
        case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
            esp_ble_gap_start_advertising(&example_adv_params);
            break;
        default:
            break;
        }
    }
    

    4.2 主程序

    1. 初始化WiFi;
    2. 初始化蓝牙控制器;
    3. 使能蓝牙控制器;
    4. 初始化bluedroid;
    5. 使能bludroid;
    6. 获取蓝牙地址;
    7. 获取blufi版本号;
    8. 创建蓝牙GAP处理事件;
    9. 创建blufi事件;
    void app_main(void)
    {
        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是蓝牙控制器配置结构体,这里使用了一个默认的参数
        esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
        //初始化蓝牙控制器,此函数只能被调用一次,且必须在其他蓝牙功能被调用之前调用
        ret = esp_bt_controller_init(&bt_cfg);
        if (ret) {
            ESP_LOGE(GATTC_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret));
            return;
        }
    
        //使能蓝牙控制器,mode是蓝牙模式,如果想要动态改变蓝牙模式不能直接调用该函数,
        //应该先用disable关闭蓝牙再使用该API来改变蓝牙模式
        ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
        if (ret) {
            ESP_LOGE(GATTC_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
            return;
        }
        //初始化蓝牙并分配系统资源,它应该被第一个调用
        /*
        蓝牙栈bluedroid stack包括了BT和BLE使用的基本的define和API
        初始化蓝牙栈以后并不能直接使用蓝牙功能,
        还需要用FSM管理蓝牙连接情况
        */
        ret = esp_bluedroid_init();
        if (ret) {
            ESP_LOGE(GATTC_TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
            return;
        }
        //使能蓝牙栈
        ret = esp_bluedroid_enable();
        if (ret) {
            ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
            return;
        }
    
        //获取蓝牙地址
        BLUFI_INFO("BD ADDR: "ESP_BD_ADDR_STR"\n", ESP_BD_ADDR_HEX(esp_bt_dev_get_address()));
        //获取blufi版本号
        BLUFI_INFO("BLUFI VERSION %04x\n", esp_blufi_get_version());
    
        //建立蓝牙的FSM(有限状态机)
        //这里使用回调函数来控制每个状态下的响应,需要将其在GAP层的回调函数注册
        /*esp_gap_cb处理蓝牙栈可能发生的所有情况,达到FSM的效果*/
        ret = esp_ble_gap_register_callback(example_gap_event_handler);
        if(ret){
            BLUFI_ERROR("%s gap register failed, error code = %x\n", __func__, ret);
            return;
        }
    
        ret = esp_blufi_register_callbacks(&example_callbacks);
        if(ret){
            BLUFI_ERROR("%s blufi register failed, error code = %x\n", __func__, ret);
            return;
        }
    
        esp_blufi_profile_init();
    }
    

    五、Station模式配置示例

    1. 给模组上电,可通过串口工具看到如下打印:


    2. 打开手机上的 EspBlufi app,在 app 界面下拉刷新,可以看到周围的蓝牙设备,如下图
      所示:


    3. 在刷新后界面显示的⼀系列蓝牙设备中,点击 ESP32 模组,跳转到设备界⾯,点击
      ,进行蓝牙连接。如果连接成功,则会出现如下页面,此时可点击配网按钮。
    4. 点击配网按钮,进⼊配网界面。如图所示:


    5. 点击确定按钮进行配网,如果配网成功,则会出现如下界面,且下方红字将显示
      配置完成后 Wi-Fi 模式 的 Station 连接信息,包括 AP 的 BSSID 和 SSID 信息,以及连
      接状态等。


    6. 同时,串口工具会打印如下信息:



    • 由 Leung 写于 2021 年 7 月 21 日

    • 参考:ESP32蓝牙配网用户指南
        安信可esp32c3模组 ESP-C3-12F 实现 Blufi 蓝牙配网。

    相关文章

      网友评论

        本文标题:ESP32学习笔记(36)——BluFi(蓝牙配网)接口使用

        本文链接:https://www.haomeiwen.com/subject/hgrmpltx.html