irpas技术客

ESP32学习笔记(48)——WiFi蓝牙网关_Leung_ManWah_esp32 蓝牙网关

网络 4457

一、项目简介

蓝牙网关 又叫蓝牙探针,是采集蓝牙设备的蓝牙数据,通过 WIFI 等方式传至服务器的一款中继设备。如果类比的话,就如同 WIFI 网络中的无线 AP 的作用。WIFI 网络中无线 AP 是将 WIFI 设备接入网络,而蓝牙网络中的蓝牙网关,是将蓝牙设备接入网络。

项目特性:

采用 240MHz Xtensa 32-bit LX6 双核处理器支持 STA 工作模式支持 Smart Config/AirKiss 一键配网内嵌 Lwip 和 FreeRTOS支持 BLE 广播/扫描支持 Beacons 设备蓝牙广播上报功能 二、基本工作流程 Smart Config/AirKiss 一键配网,WIFI 连接路由器;通过 TCP 方式连接到指定 IP 地址服务器;扫描周围蓝牙设备广播数据;将采集蓝牙数据通过 TCP 方式发送到指定服务器;上电或按下网关按键发送蓝牙广播数据,时长5秒(默认)。

三、API接口 3.1 外设 LED灯 函数功能void LED_Init(void)LED模块初始化void SetWarningLedStatus(uint8_t status)设置告警灯状态void SetNetworkLedStatus(uint8_t status)设置网络灯状态void LED_GPIO_Init(void)LED灯GPIO初始化void LED_GPIO_Write(uint8_t ledNum, uint8_t ledMode)设置LED灯GPIO状态uint8_t LED_GPIO_Read(uint8_t ledNum)获取LED灯GPIO状态
按键 函数功能void Key_Init(void)按键模块初始化void Key_GPIO_Init(void)按键GPIO初始化uint8_t Key_GPIO_Read(uint8_t keyNum)获取按键GPIO状态void Key_GPIO_IrqCallback(void *arg)按键中断触发回调函数
蜂鸣器 函数功能void Buzzer_Init(void)蜂鸣器模块初始化void Buzzer_Beep(uint8_t beepMode)设置蜂鸣器状态(短鸣/长鸣)void Buzzer_GPIO_Init(void)蜂鸣器GPIO初始化void Buzzer_GPIO_Write(uint8_t buzzerMode)设置蜂鸣器GPIO状态
3.2 Socket 函数功能uint8_t Socket_Init(void)socket初始化,并开启连接void Socket_Close(void)关闭socketvoid Socket_Send(char *pString)发送数据uint32_t Socket_Receive(char *pRecvDataBuf)接收数据
3.3 WIFI 函数功能void WIFI_Init(void)WIFI模块初始化,并等待配网uint8_t WIFI_Status(void)获取WIFI连接状态
3.4 BLE 函数功能void BLE_Init(void)蓝牙BLE模块初始化void BLE_Advertise(void)开启BLE广播void BLE_StopAdvertise(void)停止BLE广播void BLE_AdvertisingDataInit(void)BLE广播数据包内容初始化void BLE_SetManufacturerData(uint8_t *pData, uint32_t dataLen)设置用户自定义内容void BLE_Scan(void)开启BLE扫描void BLE_StopScan(void)停止BLE扫描bool BLE_GetAdTypeData(uint8_t adType, uint8_array_t *pAdvData, uint8_array_t *pTypeData)从广播包中提取AdType格式数据bool BLE_GetUuidFromAdv(uint8_array_t *pAdvData, uint8_array_t *pTypeData)从广播包中提取UUIDbool BLE_GetNameFromAdv(uint8_array_t *pAdvData, uint8_array_t *pTypeData)从广播包中提取设备名称bool BLE_GetUserDataFromAdv(uint8_array_t *pAdvData, uint8_array_t *pTypeData)从广播包中提取用户自定义内容
四、工程代码

GitCode:https://gitcode.net/qq_36347513/esp32-wifi_ble_gateway

将文件解压到 esp-idf/examples 目录下:

4.1 工程结构

4.2 一键配网

首先在 main.c 的 app_main() 中初始化 WIFI 模块,然后创建一个 network_task 处理网络通信业务。

void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); /*-------------------------- 外设驱初始化 ---------------------------*/ ··· WIFI_Init(); // WIFI模块初始化 ··· ··· /*-------------------------- 创建线程 ---------------------------*/ ··· xTaskCreate(network_task, "network_task", 4096, NULL, 5, NULL); ··· } /** @brief 通信业务 @param 无 @return 无 */ static void network_task(void *arg) { while(1) // 任务都是一个无限循环,不能返回 { HandleNetworkService(); Delay(1000); // 1s } }

扫描二维码进行 AirKiss 一键配网

4.3 连接TCP服务器

在 user_socket.h 中修改指定服务器的 IP 地址和端口:

当 network_task 获取到 WIFI 状态已连接路由器成功后,调用 Socket_Init() 初始化socket通信,连接到指定 TCP 服务器。

成功连接 TCP 服务器后,发送一条内容为 TEST 的消息。

void HandleNetworkService(void) { if(CONNECT_WIFI_FAIL == WIFI_Status()) { return; } if(INIT_FAIL == g_isSocketInit) { SetNetworkLedStatus(NO_NETWORK); s_isConnectServer = CONNECT_FAIL; g_isSocketInit = Socket_Init(); // 初始化socket通信 } else if(INIT_SUCCESS == g_isSocketInit) { if(CONNECT_FAIL == s_isConnectServer) { SetNetworkLedStatus(IDLE); s_isConnectServer = CONNECT_SUCCESS; BLE_Scan(); Socket_Send("TEST"); ESP_LOGI(TAG, "socket send TEST\r\n"); } // 接收服务器数据 char recvDataBuf[MAX_RECV_BUF_SIZE] = {0}; int recvDataLen = Socket_Receive(recvDataBuf); // 接收服务器数据 if(recvDataLen > 0) { handleSocketRecvData(recvDataBuf, recvDataLen); } } } 4.4 发送BLE扫描数据

首先在 main.c 的 app_main() 中初始化 BLE 模块,然后创建一个 monitor_task 处理事件业务。

创建一个蓝牙消息队列 MsgQueue_Init(g_pMsgQueue); 和一个蓝牙 MAC 地址队列 MacQueue_Init(g_pMacQueue);。

创建蓝牙 MAC 地址过滤定时器 CreateFilterMacTimer(),过滤短时间内同一 MAC 地址重复的蓝牙广播包。

void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); /*-------------------------- 外设驱初始化 ---------------------------*/ timers_init(); // 定时器驱动初始化(在此加入自定义定时器) ··· BLE_Init(); // 蓝牙模块初始化 BLE_AdvertisingDataInit(); BLE_Advertise(); /*---------------------------- 队列初始化 -----------------------------*/ g_pMsgQueue = (MsgQueue_t *)malloc(sizeof(MsgQueue_t)); g_pMacQueue = (MacQueue_t *)malloc(sizeof(MacQueue_t)); MsgQueue_Init(g_pMsgQueue); MacQueue_Init(g_pMacQueue); /*-------------------------- 创建线程 ---------------------------*/ ··· xTaskCreate(monitor_task, "monitor_task", 4096, NULL, 4, NULL); ··· application_timers_start(); } static void timers_init(void) { esp_timer_init(); // 使用定时器API函数,先调用接口初始化 ··· CreateFilterMacTimer(); } static void application_timers_start(void) { ··· StartFilterMacTimer(); } static void monitor_task(void *arg) { while(1) // 任务都是一个无限循环,不能返回 { HandleEventService(); Delay(100); // 100ms } }

当 network_task 获取到连接 TCP 服务器成功后,开启扫描 BLE_Scan()。

void HandleNetworkService(void) { if(CONNECT_WIFI_FAIL == WIFI_Status()) { return; } if(INIT_FAIL == g_isSocketInit) { SetNetworkLedStatus(NO_NETWORK); s_isConnectServer = CONNECT_FAIL; g_isSocketInit = Socket_Init(); // 初始化socket通信 } else if(INIT_SUCCESS == g_isSocketInit) { if(CONNECT_FAIL == s_isConnectServer) { ··· s_isConnectServer = CONNECT_SUCCESS; BLE_Scan(); ··· } ··· } }

当扫描到蓝牙广播时,进去蓝牙事件处理函数 bleEventHandler() 处理扫描结果,将蓝牙广播设备的 MAC 地址、设备名称、UUID、用户数据和RSSI 等依次封装成消息,加入消息队列。

static void bleEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { esp_err_t err; switch(event) { ··· /*--------------------------- 扫描 ---------------------------*/ case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: { //the unit of the duration is second, 0 means scan permanently // uint32_t duration = 0; // esp_ble_gap_start_scanning(duration); break; } case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: //scan start complete event to indicate scan start successfully or failed if((err = param->scan_start_cmpl.status) != ESP_BT_STATUS_SUCCESS) { ESP_LOGE(TAG, "Scan start failed: %s", esp_err_to_name(err)); } break; case ESP_GAP_BLE_SCAN_RESULT_EVT: scanResultHandler(param); break; case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: if((err = param->scan_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS) { ESP_LOGE(TAG, "Scan stop failed: %s", esp_err_to_name(err)); } else { ESP_LOGI(TAG, "Stop scan successfully"); } break; default: break; } } /** @brief 处理扫描结果 @param 无 @return 无 */ static void scanResultHandler(esp_ble_gap_cb_param_t *pScanResult) { bool result; uint8_array_t advData; uint8_array_t name; uint16_t uuid = 0; uint8_array_t uuidArray; uint8_array_t userData; uint8_t macAddr[ESP_BD_ADDR_LEN] = {0}; int8_t rssi = 0; switch(pScanResult->scan_rst.search_evt) { case ESP_GAP_SEARCH_INQ_RES_EVT: // ESP_LOGI(TAG, "----------Device Found----------"); advData.p_data = pScanResult->scan_rst.ble_adv; advData.size = pScanResult->scan_rst.adv_data_len + pScanResult->scan_rst.scan_rsp_len; // MAC地址 memcpy(macAddr, pScanResult->scan_rst.bda, ESP_BD_ADDR_LEN); // esp_log_buffer_hex("Device address:", pScanResult->scan_rst.bda, ESP_BD_ADDR_LEN); // 设备名称 result = BLE_GetNameFromAdv(&advData, &name); if(result == true) { // ESP_LOGI(TAG, "searched Device Name Len %d", name.size); // esp_log_buffer_char(TAG, (char *)name.p_data, name.size); } // UUID result = BLE_GetUuidFromAdv(&advData, &uuidArray); if(result == true) { uuid = U8ToU16LittleEndian(uuidArray.p_data); // ESP_LOGI(TAG, "searched uuid:%04x", uuid); } // 用户数据 result = BLE_GetUserDataFromAdv(&advData, &userData); if(result == true) { // esp_log_buffer_hex("searched user data:", userData.p_data, userData.size); } // RSSI rssi = pScanResult->scan_rst.rssi; // ESP_LOGI(TAG, "RSSI of packet:%d dbm", pScanResult->scan_rst.rssi); // 检查MAC地址不在待发送消息队列中 if(!CheckMac(g_pMacQueue, macAddr) && ESP_BT_DEVICE_TYPE_BLE == pScanResult->scan_rst.dev_type) { MacFilter_t *pMac; pMac =(MacFilter_t *)malloc(sizeof(MacFilter_t)); memcpy(pMac->addr, pScanResult->scan_rst.bda, ESP_BD_ADDR_LEN); // MAC地址 pMac->tick = esp_timer_get_time(); // 当前系统tick,用于过滤重复消息 if(MacQueue_GetLength(g_pMacQueue) < MAX_MAC_NUM) { MacQueue_PushElement(g_pMacQueue, *pMac); // 加入MAC队列 } free(pMac); pMac = NULL; DataType_t *pMsg; pMsg =(DataType_t *)malloc(sizeof(DataType_t)); // UUID pMsg->uuid = uuid; // 广播内容 pMsg->adv_data.size = advData.size; pMsg->adv_data.p_data = (uint8_t *)malloc(advData.size * sizeof(uint8_t)); memcpy(pMsg->adv_data.p_data, advData.p_data, advData.size); // 信号强度 pMsg->rssi = rssi; // MAC地址 memcpy(pMsg->addr, pScanResult->scan_rst.bda, ESP_BD_ADDR_LEN); if(MsgQueue_GetLength(g_pMsgQueue) < MAX_MESSAGE_NUM) { MsgQueue_PushElement(g_pMsgQueue, *pMsg); // 加入消息队列 } else { free(pMsg->adv_data.p_data); pMsg->adv_data.p_data = NULL; } free(pMsg); pMsg = NULL; } break; default: break; } }

在 monitor_task 中,从消息队列中提取消息发送到 TCP 服务端。

4.5 按键发送BLE广播 首先在 main.c 的 app_main() 中初始化 BLE 模块,然后创建一个 monitor_task 处理事件业务。创建蓝牙广播停止的定时器 CreateKeepAdvertisingTimer(),设备上电广播5秒。 void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); /*-------------------------- 外设驱初始化 ---------------------------*/ timers_init(); // 定时器驱动初始化(在此加入自定义定时器) ··· BLE_Init(); // 蓝牙模块初始化 BLE_AdvertisingDataInit(); BLE_Advertise(); ··· /*-------------------------- 创建线程 ---------------------------*/ ··· xTaskCreate(monitor_task, "monitor_task", 4096, NULL, 4, NULL); ··· application_timers_start(); } static void timers_init(void) { esp_timer_init(); // 使用定时器API函数,先调用接口初始化 CreateKeepAdvertisingTimer(); ··· } static void application_timers_start(void) { StartKeepAdvertisingTimer(); ··· } static void monitor_task(void *arg) { while(1) // 任务都是一个无限循环,不能返回 { HandleEventService(); Delay(100); // 100ms } }

当设备连接上 TCP 服务器时,按下按键通过 socket 发送消息。

void HandleEventService(void) { // 网络连接成功 if(CONNECT_SUCCESS == GetConnectServerStatus()) { // 发送呼叫 if(KEY_CALL_EVENT == GetKeyTriggerEvent()) { Buzzer_Beep(SHORT); Socket_Send("CALL"); ESP_LOGI(TAG, "socket send CALL\r\n"); SetKeyTriggerEvent(0); return; } ··· } // 网络故障 else { ··· } }

当设备没有连接上 TCP 服务器时,按下按键通过 BLE 广播发送消息。

void HandleEventService(void) { // 网络连接成功 if(CONNECT_SUCCESS == GetConnectServerStatus()) { ··· } // 网络故障 else { if(KEY_CALL_EVENT == GetKeyTriggerEvent()) { Buzzer_Beep(SHORT); SetWarningLedStatus(CALL_WARNING); uint8_t data[6] = {0}; data[0] = APP_COMPANY_IDENTIFIER_L; data[1] = APP_COMPANY_IDENTIFIER_H; data[2] = 0x11; data[3] = 0x22; data[4] = 0x33; data[5] = 0x44; BLE_SetManufacturerData(data, sizeof(data)); BLE_Advertise(); // 开启广播 StartKeepAdvertisingTimer(); SetKeyTriggerEvent(0); } } }


? 由 Leung 写于 2022 年 5 月 17 日

? 参考:【IoT】BLE 蓝牙网关与蓝牙定位技术解析     【安信可PB-01/02模组专题③】ESP32-G WIFI蓝牙网关与PB02模组开发进行组网通讯


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #ESP32 #蓝牙网关 #一项目简介蓝牙网关 #wifi #等方式传至服务器的一款中继设备 #如果类比的话就如同