ESP32蓝牙配网

news/2024/4/27 9:23:04/文章来源:https://blog.csdn.net/qq_45689245/article/details/129234902

注意********menuconfig 配置(必须打开蓝牙我这是C2所以使用NimBLE )可以直接从demo的配置文件拷贝

Component config ---> Bluetooth ---> NimBLE - BLE only

Component config ---> Bluetooth ---> NimBLE Options ---> Enable blufi functionality

相关头文件:

#include "esp_bt.h"          //蓝牙控制器和VHCI设置头文件
#include "esp_gap_ble_api.h" //GAP设置头文件,广播和连接相关参数配置
#include "esp_gatts_api.h"   //GATT配置头文件,创建Service和Characteristic
#include "esp_bt_main.h"     //蓝牙栈空间的初始化头文件

一、BluFi 流程 官方demo-----bluetooth/blufi API编程介绍

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

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

  1. ESP32-C2 开启 GATT Server 模式,发送带有特定 advertising data 的广播。该广播不属于 BluFi Profile,您可以按需对其进行自定义。
  2. 使用手机应用程序搜索到该广播后,手机将作为 GATT Client 连接 ESP32-C2。该步骤对具体使用哪款手机应用程序并无特殊要求。
  3. 成功建立 GATT 连接后,手机会向 ESP32-C2 发送数据帧进行密钥协商(详见 BluFi 中定义的帧格式 )。
  4. ESP32-C2 收到密钥协商的数据帧后,会按照您自定义的协商方法进行解析。
  5. 手机与 ESP32-C2 进行密钥协商。协商过程可使用 DH/RSA/ECC 等加密算法。
  6. 协商结束后,手机端向 ESP32-C2 发送控制帧,用于设置安全模式。
  7. ESP32-C2 收到控制帧后,使用共享密钥以及安全配置对通信数据进行加密和解密。
  8. 手机向 ESP32-C2 发送 BluFi 中定义的帧格式 中定义的数据帧,包括 SSID、密码等 Wi-Fi 配置信息。
  9. 手机向 ESP32-C2 发送 Wi-Fi 连接请求的控制帧。ESP32-C2 收到控制帧后,即默认手机已完成必要信息的传输,准备连接 Wi-Fi。
  10. 连接到 Wi-Fi 后,ESP32-C2 发送 Wi-Fi 连接状态报告的控制帧到手机。至此,配网结束。

一、 WIFI部分事件处理

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

wifi_event_handler

二、NETIF部分事件处理

获取网络IP地址,完成IP接口搭建(默认IO口);

更多netif功能介绍与使用参考链接:ESP-NETIF

ip_event_handler

ESP-NETIF : IP 网络层协议

  • ESP-NETIF 目的
    • 为 TCP/IP 栈之上的 APP 提供了一个抽象层。这将允许 APP 将来在 IP 栈之间进行选择。
    • 其提供的 API 是线程安全的,即使其底层的 TCP/IP 栈是不安全的。
    • 一些 API 旨在由 APP 代码调用,例如获取/设置接口 IP 地址、配置 DHCP。其它 API 旨在供网络驱动层在内部使用。
    • 在许多情况下,APP 是不需要直接调用 ESP-NETIF API 的,因为它们是从默认网络事件处理程序调用的。
  • WIFI 默认初始化
    • 初始化代码以及为默认接口(例如 station 和 soft-AP)注册事件处理程序在两个单独的 API 中提供,以方便大多数 APP 的简单启动方式。
esp_netif_create_default_wifi_ap()
esp_netif_create_default_wifi_sta()
    • 这些函数返回 esp_netif 句柄,即指向使用默认设置分配和配置的网络接口对象的指针。
      • 如果 APP 提供了网络取消初始化,则必须销毁创建的对象。
      • 这些默认接口不得多次创建,除非使用删除创建的句柄 esp_netif_destroy()
      • 当使用 AP + STA 模式时,这些接口都要创建。
  • 头文件
    • esp_netif/include/esp_netif.h

esp_err_t esp_netif_init(void) //初始化底层 TCP/IP 栈。 //当 APP 启动时,这个函数应该从 APP 代码中调用一次。

  • return
    • ESP_OK: 成功
    • ESP_FALL: 初始化失败

esp_netif_t* esp_netif_create_default_wifi_ap(void) //创建默认的 WIFI AP。如果出现任何初始化错误,此 API 将终止。

  • return
    • 指向 esp-netif 实例的指针。

三、 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:将接收到的数据打印出来;

//BLUFI配网部分事件处理
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功能初始化,设置设备名称(Device Name) 并发送特定的 adv data 广播   BLUFI_INFO("BLUFI init finish\n");//esp_blufi_adv_start();//开始广播ble_svc_gap_device_name_set("nimble-default");//目前使用此函数设置广播名称esp_blufi_adv_start();//再开始广播 暂时无法自定义广播参数break;case ESP_BLUFI_EVENT_DEINIT_FINISH: //处理deinit配置事件BLUFI_INFO("BLUFI deinit finish\n");break;case ESP_BLUFI_EVENT_BLE_CONNECT: //连接Blufi Ble,并设备进入安全模式BLUFI_INFO("BLUFI ble connect\n");ble_is_connected = true;esp_blufi_adv_stop(); //停止广播blufi_security_init(); //开启安全模式esp_err_t err = esp_blufi_send_custom_data(char1_str,sizeof(char1_str));   //发送自定义数据BLUFI_INFO("BLUFI send custom data:%s,status:%s\n",char1_str,esp_err_to_name(err));break;case ESP_BLUFI_EVENT_BLE_DISCONNECT: //设置ble断开重连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: //设置WiFi进入运行模式——op_modeBLUFI_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: // 设置断开原有的WiFi连接,并连接指定WiFi BLUFI_INFO("BLUFI requset wifi connect to AP\n");/* there is no wifi callback when the device has already connected to this wifiso disconnect wifi before connection.*/esp_wifi_disconnect();example_wifi_connect();break;case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP: //断开当前WiIFi连接到的APBLUFI_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状态信息,包括:WiFi当前模式、以及是否连接成功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的gatt服务连接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: //设置进入STA模式,获取目标AP的bssidmemcpy(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: //设置进入STA模式,获取目标AP的WiFi账号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: //设置进入STA模式,获取目标AP的WiFi密码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 %d\n", param->custom_data.data_len);esp_log_buffer_hex("Custom Data", param->custom_data.data, param->custom_data.data_len);break;default:break;}
}

四、GAP广播部分事件处理(自测发现C2在使用nimble后不支持)

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

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,
};

功能

esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t*回调)

调用此函数以接收 blufi 回调事件。参数回调–[在]回调函数返回ESP_OK - 成功,其他 - 失败

esp_err_t esp_blufi_profile_init(无效)

调用此函数以初始化blufi_profile。返回ESP_OK - 成功,其他 - 失败

esp_err_t esp_blufi_profile_deinit(无效)

调用此函数以取消初始化blufi_profile。返回ESP_OK - 成功,其他 - 失败

esp_err_t esp_blufi_send_wifi_conn_report(wifi_mode_topmode,esp_blufi_sta_conn_state_tsta_conn_state,uint8_tsoftap_conn_num,esp_blufi_extra_info_t*extra_info)

调用此函数以发送 wifi 连接报告。参数操作模式–: 无线操作模式sta_conn_state–:工作站是否已连接softap_conn_num–: 软件连接号extra_info– :额外信息,如sta_ssid、softap_ssid等。返回ESP_OK - 成功,其他 - 失败

esp_err_t esp_blufi_send_wifi_list(uint16_tapCount,esp_blufi_ap_record_t*list)

调用此函数以发送 wifi 列表。参数apCount– : wifi list count列表– : 无线列表返回ESP_OK - 成功,其他 - 失败

主函数:

void app_main(void)
{esp_err_t ret;// Initialize NVSret = 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) {BLUFI_ERROR("%s initialize bt controller failed: %s\n", __func__, esp_err_to_name(ret));}//使能蓝牙控制器,mode是蓝牙模式,如果想要动态改变蓝牙模式不能直接调用该函数,//应该先用disable关闭蓝牙再使用该API来改变蓝牙模式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;}//建立蓝牙的FSM(有限状态机)//这里使用回调函数来控制每个状态下的响应,需要将其在GAP层的回调函数注册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版本号BLUFI_INFO("BLUFI VERSION %04x\n", esp_blufi_get_version());
}

信息存储,上电自动重连。参考

 //存放当前的配网信息
nvs_handle_t wificonfig_set_handle;
ESP_ERROR_CHECK( nvs_open("wificonfig",NVS_READWRITE,&wificonfig_set_handle) );
ESP_ERROR_CHECK( nvs_set_u8(wificonfig_set_handle,"WifiConfigFlag", wifi_configed) );
ESP_ERROR_CHECK( nvs_set_str(wificonfig_set_handle,"SSID",(const char *)ssid) );
ESP_ERROR_CHECK( nvs_set_str(wificonfig_set_handle,"PASSWORD", (const char *)password) );
ESP_ERROR_CHECK( nvs_commit(wificonfig_set_handle) );
nvs_close(wificonfig_set_handle);
//上电检测重连
static void check_wifi_config_in_nvs(void)
{nvs_handle_t wificonfig_get_handle;wifi_config_t wifi_config;esp_err_t err;uint8_t u8WifiConfigVal = 0;uint8_t u8Ssid[33] = { 0 };uint8_t u8Password[65] = { 0 };size_t Len = 0;uint8_t u8GetWifiFlag = 0;bzero(&wifi_config, sizeof(wifi_config_t));nvs_open("wificonfig", NVS_READWRITE, &wificonfig_get_handle);nvs_get_u8(wificonfig_get_handle, "WifiConfigFlag", &u8WifiConfigVal);printf("wificonfigval:%X \r\n",u8WifiConfigVal);if (u8WifiConfigVal == wifi_configed){Len = sizeof(u8Ssid);err = nvs_get_str(wificonfig_get_handle, "SSID", (char *)u8Ssid, &Len);if(err == ESP_OK){memcpy(wifi_config.sta.ssid, u8Ssid, sizeof(wifi_config.sta.ssid));ESP_LOGI(TAG, "ssid:%s,len:%d",u8Ssid,Len);u8GetWifiFlag ++;}Len = sizeof(u8Password);err = nvs_get_str(wificonfig_get_handle, "PASSWORD",(char *)u8Password,&Len);if(err == ESP_OK){memcpy(wifi_config.sta.password, u8Password, sizeof(wifi_config.sta.password));ESP_LOGI(TAG, "password:%s,len:%d",u8Password,Len);u8GetWifiFlag ++;}nvs_close(wificonfig_get_handle);initialise_wifi();if(u8GetWifiFlag == 2){//使用获取的配网信息链接无线网络ESP_ERROR_CHECK( esp_wifi_disconnect() );ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );ESP_ERROR_CHECK( esp_wifi_connect() );}else{xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);}}else{nvs_close(wificonfig_get_handle);initialise_wifi();xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);ESP_LOGI(TAG, "Get WifiConfig Fail,Start SmartConfig......");}}

五、使用nimble后再如何使用一些接口

可以查看使用IDF这个目录下的一些接口,来实现。通过调用一些nimble组件下的一些API来达到现有目的。

//使用blufi配网 修改设备名
esp-idf/components/bt/host/nimble/nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h
//nimble广播设置例程
examples\bluetooth\nimble\blehr ---暂时不支持C2
examples\bluetooth\nimble\ble_l2cap_coc\coc_bleprph
//部分接口参考
esp-idf/components/bt/host/nimble/nimble/nimble/host/include
int ble_gap_adv_set_fields(const struct ble_hs_adv_fields *rsp_fields);
//配置要包含在后续广告中的字段。这是ble_gap_adv_set_data()的方便包装器。
//@param adv_fields指定广告数据。
//@return哦,成功了,BLE_HS_EBUSY表示广告正在进行中,BLE_HS_EMSGSIZE表示指定的数据太大无法放入广告中,失败时的其他错误代码。
int ble_gap_adv_stop(void);
//停止当前活动的广告过程。成功回归代码表示广告已经完全中止,可以立即启动新的广告过程。

nimble广播不是自定义的服务UUID和属性设置如下

超时关闭蓝牙

esp_err_t ble_close(void)
{esp_err_t ret = ESP_FAIL;if (G_UpdateInfo.BleBroadcastState == 1){// esp_blufi_adv_stop(); //停止广播// ret = esp_bt_controller_disable();// if (ret != ESP_OK)// {//     BLUFI_ERROR("%s disable bt controller failed: %s\n", __func__, esp_err_to_name(ret));//     return ret;// }G_UpdateInfo.BleBroadcastState = 0;// esp_bt_controller_deinit();BLUFI_INFO("BLUFI Stop running\n");}save_flash_restart(); //配网结束存储重启return ret;
}
/*
*是否关闭蓝牙广播 
*@param:无
*@return:0:未连接WiFi不关闭,1:连接WiFi后APP关闭,-1:连接WiFi后超时设备自动关闭
*/
int is_close_ble()
{int ret = 0;if (gl_sta_connected && G_Blufi_State.BleBroadcastState == 1){int cur_time = 0;cur_time = get_sys_time_sec();while (get_sys_time_sec() < cur_time + CLOSE_BLE_TIME) {if (!ble_is_connected){ble_close(); // 配网成功后10秒内蓝牙没有连接关闭蓝牙ret = 1;BLUFI_INFO("app close bluetooth succeed\n");break;}vTaskDelay(100 / portTICK_PERIOD_MS);}ble_close(); //连接WiFi后APP超时未断开蓝牙 自动关闭BLUFI_INFO("close ble time out disable bt\n");ret = -1;}return ret;
}
//等待配网完成
void reply_app_connect_wait()
{int cur_time = 0;cur_time = get_sys_time_sec();while (get_sys_time_sec() < cur_time + SEND_APP_OUT_TIME) // 没有配网并且超时时间没到 超时时间为1min{if (gl_sta_connected){had_connect_wifi = 1; //成功配网 无超时break;}had_connect_wifi = 0; //配网超时vTaskDelay(100 / portTICK_PERIOD_MS);}  
}

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算结构体大小

计算结构体大小 目录计算结构体大小一. 结构体内存对齐1. 简介2. 嵌套结构体二. offsetof三. 内存对齐的意义四. 修改默认对齐数一. 结构体内存对齐 以字节&#xff08;bety&#xff09;为单位 1. 简介 对于结构体成员在内存里的存储&#xff0c;存在结构体的对齐规则&#…

Vue下载安装步骤的详细教程(亲测有效) 1

目录 一、【准备工作】nodejs下载安装(npm环境) 1 下载安装nodejs 2 查看环境变量是否添加成功 3、验证是否安装成功 4、修改模块下载位置 &#xff08;1&#xff09;查看npm默认存放位置 &#xff08;2&#xff09;在 nodejs 安装目录下&#xff0c;创建 “node_global…

Java查漏补缺(14)数据结构剖析、一维数组、链表、栈、队列、树与二叉树、List接口分析、Map接口分析、Set接口分析、HashMap的相关问题

Java查漏补缺&#xff08;14&#xff09;数据结构剖析、一维数组、链表、栈、队列、树与二叉树、List接口分析、Map接口分析、Set接口分析、HashMap的相关问题本章专题与脉络1. 数据结构剖析1.1 研究对象一&#xff1a;数据间逻辑关系1.2 研究对象二&#xff1a;数据的存储结构…

Laravel框架04:视图与CSRF攻击

Laravel框架04&#xff1a;视图与CSRF攻击一、视图概述二、变量分配与展示三、模板中直接使用函数四、循环与分支语法标签五、模板继承、包含1. 继承2. 包含六、外部静态文件引入七、CSRF攻击概述八、从CSRF验证中排除例外路由一、视图概述 视图存放在 resources/views 目录下…

MyBatis学习笔记(七) —— 特殊SQL的执行

7、特殊SQL的执行 7.1、模糊查询 模糊查询的三种方式&#xff1a; 方式1&#xff1a;select * from t_user where username like ‘%${mohu}%’ 方式2&#xff1a;select * from t_user where username like concat(‘%’,#{mohu},‘%’) 方式3&#xff1a;select * from t_u…

收集分享一些AI工具第三期(网站篇)

感谢大家对于内容的喜欢&#xff0c;目前已经来到了AI工具分享的最后一期了&#xff0c;目前为止大部分好用的AI工具都已经介绍给大家了&#xff0c;希望大家可以喜欢。 image-to-sound-fx (https://huggingface.co/spaces/fffiloni/image-to-sound-fx) 图片转换为相对应的声音…

2.27 junit5常用语法

一.了解junitjunit是一个开源的java单元测试框架,java方向使用最广泛的单元测试框架.所需要的依赖<dependencies><!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java --><dependency><groupId>org.seleniumhq.selenium&l…

笔记本触摸板没反应怎么办?处理方法看这些

触摸板在笔记本电脑中是非常重要的一部分&#xff0c;很多用户都会选择使用触摸板代替鼠标。然而&#xff0c;有时你可能会发现&#xff0c;你的笔记本电脑触摸板没反应&#xff0c;无法正常使用。这对于日常使用来说是非常困扰的&#xff0c;但不用担心&#xff0c;我们将在这…

react源码解析10.commit阶段

在render阶段的末尾会调用commitRoot(root);进入commit阶段&#xff0c;这里的root指的就是fiberRoot&#xff0c;然后会遍历render阶段生成的effectList&#xff0c;effectList上的Fiber节点保存着对应的props变化。之后会遍历effectList进行对应的dom操作和生命周期、hooks回…

【数据结构】知识点总结(C语言)

线性表、栈和队列、串、数组和广义表、树和二叉树、图、查找、排序线性表线性表&#xff08;顺序表示&#xff09;线性表是具有相同特性元素的一个有限序列&#xff0c;数据元素之间是线性关系&#xff0c;起始元素称为线性起点&#xff0c;终端元素称为线性终点。线性表的顺序…

sed 功能详解

介绍sedsed是一种流编辑器&#xff0c;它一次处理一行内容&#xff0c;把当前处理的行存储在临时缓冲区中&#xff08;buffer&#xff09;,称为"模式空间"&#xff0c;接着sed命令处理缓冲区中的内容&#xff0c;处理完成后&#xff0c;把缓冲区的内容送往屏幕&#…

RCEE: Event Extraction as Machine Reading Comprehension 论文解读

RCEE: Event Extraction as Machine Reading Comprehension 论文&#xff1a;Event Extraction as Machine Reading Comprehension (aclanthology.org) 代码&#xff1a;jianliu-ml/EEasMRC (github.com) 期刊/会议&#xff1a;EMNLP 2020 摘要 事件提取(Event extraction,…

哪个品牌蓝牙耳机性价比高?性价比高的平价蓝牙耳机推荐

现如今&#xff0c;随着蓝牙技术的进步&#xff0c;蓝牙耳机在人们日常生活中的便捷性更胜从前。越来越多的蓝牙耳机品牌被大众看见、认可。那么&#xff0c;哪个品牌的蓝牙耳机性价比高&#xff1f;接下来&#xff0c;我给大家推荐几款性价比高的平价蓝牙耳机&#xff0c;一起…

软件测试面试问答

笔试 笔试的话我们需要揣测具体会考什么内容&#xff0c;我们可以通过招聘信息去了解该公司需要什么样的技能&#xff0c;以此来准备笔试。一般必考的内容会有编程&#xff0c;测试用例设计&#xff0c;工作流程&#xff0c;逻辑思维等内容&#xff0c;除此之外每个公司可能还会…

移动端监听物理返回

业务场景&#xff1a;用户没有填完数据却不小心点到了回退按钮&#xff0c;此时需要展示确认弹框项目场景&#xff1a;vue2 uni-app Chrome Dev调试工具代码片段&#xff1a;onLoad(options){// 将当前url地址添加到浏览器的历史记录中window.history.pushState(null, null, …

OSI和TCP/IP网络模型细讲

文章目录一、OSI七层参考模型二、TCP/IP体系结构三、TCP/IP参考模型四、沙漏计时器形状的TCP/IP协议族五、两种国际标准对比相似之处不同之处一、OSI七层参考模型 OSI参考模型共分为7层&#xff0c;低三层面向通信&#xff0c;可用软硬件实现&#xff1b;高三层面向信息处理&am…

一个基于 LKM 的 Linux 内核级 rootkit 的实现

博客已迁移至&#xff1a;https://gls.show/ GitHub链接 演示Slides overview rootkit是一种恶意软件&#xff0c;攻击者可以在获得 root 或管理员权限后安装它&#xff0c;从而隐藏入侵并保持root权限访问。rootkit可以是用户级的&#xff0c;也可以是内核级的。关于rootk…

Android 实现菜单拖拽排序

效果图简介本文主角是ItemTouchHelper。它是RecyclerView对于item交互处理的一个「辅助类」&#xff0c;主要用于拖拽以及滑动处理。以接口实现的方式&#xff0c;达到配置简单、逻辑解耦、职责分明的效果&#xff0c;并且支持所有的布局方式。功能拆解功能实现4.1、实现接口自…

ARM的工作模式和37个寄存器

一、ARM的工作模式 ARM一共有7种工作模式 模式含义User非特权模式&#xff0c;大部分任务执行在这种模式FIQ当一个高优先级&#xff08;fast) 中断产生时将会进入这种模式IRQ当一个低优先级&#xff08;normal) 中断产生时将会进入这种模式Supervisor当复位或软中断指令执行时…

CISP注册信息安全专业人员证书

一、什么是“CISP”&#xff1f; 注册信息安全专业人员(Certified Information Security Professional&#xff0c;简称“CISP”)&#xff0c;是安全行业最为权威的安全资格认证&#xff0c;由中国信息安全测评中心统一授权组织&#xff0c;中国信息安全测评中心授权培训机构进…