注意********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 配网流程包含配置 SoftAP 和配置 Station 两部分。
下面以配置 Station 为例,介绍了广播、连接、服务发现、协商共享密钥、传输数据、回传连接状态等关键步骤。
主要负责WIFI的连接、断开重连、扫描
wifi_event_handler
获取网络IP地址,完成IP接口搭建(默认IO口);
更多netif功能介绍与使用参考链接:ESP-NETIF
ip_event_handler
ESP-NETIF : IP 网络层协议
esp_netif_create_default_wifi_ap()
esp_netif_create_default_wifi_sta()
esp_err_t esp_netif_init(void) //初始化底层 TCP/IP 栈。 //当 APP 启动时,这个函数应该从 APP 代码中调用一次。
esp_netif_t* esp_netif_create_default_wifi_ap(void) //创建默认的 WIFI AP。如果出现任何初始化错误,此 API 将终止。
此过程事件的处理均按照收到的请求作相应的功能处理,可按照个人需求进行修改
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_mode
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: // 设置断开原有的WiFi连接,并连接指定WiFi
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: //断开当前WiIFi连接到的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状态信息,包括: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的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: //设置进入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;
}
}
用于当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 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) {
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......");
}
}
可以查看使用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);
}
}