本例程展示如何用Blufi配置WiFi连接到AP。将esp32设备连接到AP的配网方法还有WiFi部分的SmartConfig。这里就不赘述后者了。
基于ESP32的Blufi是一个通过蓝牙信道的WiFi配网函数。它提供了一个安全的协议,用于将 Wi-Fi 配置和凭据传递给 ESP32。
本例程包含3个C文件和1个头文件。这里只分析其中包含main函数的c文件,其他两个暂不分析。
Esp32蓝牙框架简介
标准蓝牙从上到下大体可分为:
- 应用层(Profiles)
- 主机层(Host)
- 控制层(Controller)
ESP-IDF蓝牙框架支持两个主机堆栈:
- Bluedroid:默认的主机堆栈,支持经典蓝牙和BLE
- Apache NimBLE:只支持BLE
blufi_example_main.c
头文件、变量和函数声明
#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_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 "esp_blufi.h"
//blufi事件回调函数
static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param);
//wifi列表的长度
#define WIFI_LIST_NUM 10
//创建wifi连接时的配置信息,包含名称、密码等
static wifi_config_t sta_config;
static wifi_config_t ap_config;
//wifi事件组的句柄
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
//连接标志位
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
//返还手机的STA信息
/* store the station info for send back to phone */
static bool gl_sta_connected = false;//是否连接到目标AP的标志位
static bool ble_is_connected = false;//蓝牙是否连接的标志位
static uint8_t gl_sta_bssid[6];//目标AP的MAC地址
static uint8_t gl_sta_ssid[32];//目标AP的名字
static int gl_sta_ssid_len;//目标AP名字的长度
IP事件处理函数
//ip事件处理函数
static void ip_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
//wifi模式
wifi_mode_t mode;
switch (event_id) {
//如果拿到IP地址
case IP_EVENT_STA_GOT_IP: {
//blufi额外信息结构体
esp_blufi_extra_info_t info;
//置位标志位,表示已连接到AP
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
//获取wifi的工作模式
esp_wifi_get_mode(&mode);
//为info分配内存
memset(&info, 0, sizeof(esp_blufi_extra_info_t));
//将目标AP各项信息复制到info
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;
//如果蓝牙处于连接状态,就把wifi工作模式、成功连接、没有STA接入本设备以及额外信息发送出去
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;
}
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工作模式
wifi_mode_t mode;
switch (event_id) {
case WIFI_EVENT_STA_START://如果wifi以STA模式启动,则开始连接AP
esp_wifi_connect();
break;
case WIFI_EVENT_STA_CONNECTED://若连接AP成功
//置为标志位,表示连接AP成功
gl_sta_connected = true;
//将事件数据强制转换为wifi_event_sta_connected_t
//后者是事件WIFI_EVENT_STA_CONNECTED的参数结构体
event = (wifi_event_sta_connected_t*) event_data;
//将连接到的AP的信息存入gl相关变量
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://若wifi断开连接
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
/*
这是一种解决办法,因为esp32的wifi库目前没有自动重连的函数
*/
//置位,表示与目标AP断开连接
gl_sta_connected = false;
//将与目标AP相关的变量全部置零
memset(gl_sta_ssid, 0, 32);
memset(gl_sta_bssid, 0, 6);
gl_sta_ssid_len = 0;
//重连wifi
esp_wifi_connect();
//置位标志位,表示已连接到AP
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
case WIFI_EVENT_AP_START://若esp32以AP模式启动
//获取工作模式
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) {//且连接到目标AP
//报告工作模式、连接成功、0个设备连接到esp32、无额外信息
esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, NULL);
} else {//没连接到AP
//报告工作模式、连接失败、0个设备连接到esp32、无额外信息
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: {//若wifi扫描完成
//ap数量
uint16_t apCount = 0;
//获取ap数量
esp_wifi_scan_get_ap_num(&apCount);
//没扫描到AP,打印信息并返回
if (apCount == 0) {
BLUFI_INFO("Nothing AP found");
break;
}
//按照扫描到的AP数给ap列表分配内存
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;
}
//将扫描到的AP信息存入列表
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, ap_list));
//给配网AP列表分配内存
esp_blufi_ap_record_t * blufi_ap_list = (esp_blufi_ap_record_t *)malloc(apCount * sizeof(esp_blufi_ap_record_t));
//分配失败,释放ap_list并打印信息
if (!blufi_ap_list) {
if (ap_list) {//如果ap_list分配成功就释放掉
free(ap_list);
}
BLUFI_ERROR("malloc error, blufi_ap_list is NULL");
break;
}
//将ap_list中的信号强度和AP名称复制给blufi_ap_list
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));
}
//如果有蓝牙连接,则发送扫描到的ap,否则打印信息
if (ble_is_connected == true) {
esp_blufi_send_wifi_list(apCount, blufi_ap_list);
} else {
BLUFI_INFO("BLUFI BLE is not connected yet\n");
}
//停止wifi扫描
esp_wifi_scan_stop();
//释放内存
free(ap_list);
free(blufi_ap_list);
break;
}
default:
break;
}
return;
}
WiFi初始化函数
//初始化wifi,以STA模式启动
//这部分我之前在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_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) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
事件回调函数
//BLUFI回调结构体
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,
};
//事件回调函数
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事件初始化完成
BLUFI_INFO("BLUFI init finish\n");
esp_blufi_adv_start();//开始blufi广播
break;
case ESP_BLUFI_EVENT_DEINIT_FINISH://blufi事件初始化失败
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.c中
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://设置wifi模式
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://需要连接到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.
*/
//断开wifi,重连
esp_wifi_disconnect();
esp_wifi_connect();
break;
case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP://需要与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_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_disconnect();
break;
case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA://未进行身份验证的STA
/* TODO */
break;
case ESP_BLUFI_EVENT_RECV_STA_BSSID://设置STA的MAC地址
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的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://设置STA的密码
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://设置AP模式下的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://设置AP模式下的密码
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://设置AP模式下的最大接入数
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://指定AP模式的信道
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:{//扫描附近的AP
wifi_scan_config_t scanConf = {
.ssid = NULL,
.bssid = NULL,
.channel = 0,
.show_hidden = false
};
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;
}
}
main函数
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();
//启动BLE之前先释放经典蓝牙占用的内存
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));
}
//设置蓝牙为BLE模式
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;
}
//初始化blufi,具体代码在blufi_init.c
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());
}