一、项目简介

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

项目特性:

  • 采用 240MHz Xtensa 32-bit LX6 双核处理器
  • 支持 STA 工作模式
  • 支持 Smart Config/AirKiss 一键配网
  • 内嵌 Lwip 和 FreeRTOS
  • 支持 BLE 广播/扫描
  • 支持 Beacons 设备蓝牙广播上报功能

二、基本工作流程

  1. Smart Config/AirKiss 一键配网,WIFI 连接路由器;
  2. 通过 TCP 方式连接到指定 IP 地址服务器;
  3. 扫描周围蓝牙设备广播数据;
  4. 将采集蓝牙数据通过 TCP 方式发送到指定服务器;
  5. 上电或按下网关按键发送蓝牙广播数据,时长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) 关闭socket
void 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) 从广播包中提取UUID
bool 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.capp_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.capp_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 failedif((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);}// UUIDresult = 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);}// RSSIrssi = 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));// UUIDpMsg->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.capp_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模组开发进行组网通讯

ESP32学习笔记(48)——WiFi蓝牙网关相关推荐

  1. ESPIDF开发ESP32学习笔记【WiFi实现】

    WiFi外设配置 ESP32/8266的Wi-Fi库支持配置及监控Wi-Fi连网功能 相关内容参考乐鑫的ESP32/8266文档https://docs.espressif.com/projects/ ...

  2. ESP32学习笔记(27)——BLE GAP主机端扫描

    一.背景 1.1 低功耗蓝牙(BLE)协议栈 链路层(LL) 控制设备的射频状态,有五个设备状态:待机.广播.扫描.初始化和连接. 广播 为广播数据包,而 扫描 则是监听广播. GAP通信中角色,中心 ...

  3. 关于esp32蓝牙模块的使用——esp32学习笔记

    关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 零.前言 一.经典蓝牙BT 二.低功耗蓝牙B ...

  4. 立创开源|手把手教你做个WiFi/蓝牙网关

    我知道你也想要一个属于自己的WiFi/蓝牙网关 开源地址:ESP32 WiFi/蓝牙网关 ESP32 GetWay 工程说明 官方说明:安信可 Wi-Fi 家庭智能网关(ESP32-G)由安信可科技设 ...

  5. ESP32学习笔记(七) 复位和时钟

    ESP32学习笔记(七) 复位和时钟 目录: ESP32学习笔记(一) 芯片型号介绍 ESP32学习笔记(二) 开发环境搭建 VSCode+platformio ESP32学习笔记(三) 硬件资源介绍 ...

  6. ESP32学习笔记(一) 芯片型号介绍

    ESP32学习笔记(一) 芯片型号介绍 目录: ESP32学习笔记(一) 芯片型号介绍 ESP32学习笔记(二) 开发环境搭建 VSCode+platformio ESP32学习笔记(三) 硬件资源介 ...

  7. ESP32学习笔记(30)——BLE GATT服务端自定义服务和特征

    一.简介 1.1 低功耗蓝牙(BLE)协议栈 链路层(LL) 控制设备的射频状态,有五个设备状态:待机.广播.扫描.初始化和连接. 广播 为广播数据包,而 扫描 则是监听广播. GAP通信中角色,中心 ...

  8. ESP32学习笔记(14)——HTTP服务器

    一.HTTP简介 HTTP(Hyper Text Transfer Protocol) 超文本传输协议,是一种建立在 TCP 上的无状态连接,整个基本的工作流程是客户端发送一个 HTTP 请求,说明客 ...

  9. ESP32学习笔记(7)——SmartConfig接口使用(ESP-Touch和AirKiss)

    一.概述 SmartConfig是TI开发的一种配置技术,用于将新的Wi-Fi设备连接到Wi-Fi网络.它使用移动应用程序将网络凭据从智能手机或平板电脑广播到未配置的Wi-Fi设备. 该技术的优点是设 ...

最新文章

  1. 递归用函数、存储过程实现的效果
  2. vue中如何设置和清除定时器setInterval
  3. feign post 传递空值_听我讲完GET、POST原理,面试官给我倒了杯卡布奇诺
  4. 简记模态对话框和非模态对话框
  5. oracle sqlplus ed,Uedit32与SQLPlus结合使用技巧-数据库专栏,ORACLE
  6. php怎么加一个透明的菜单栏,window_PHP制作下拉透明菜单,下拉透明菜单 script language= - phpStudy...
  7. 2018最新hadoop服务器环境配置教程(附详细步骤)
  8. 2021-03-23梦笔记
  9. C#Excel上传批量导入sqlserver
  10. VB的阶乘和伽马函数
  11. lisp坐标一键生成_如何利用lisp程序一次性提取CAD中点的坐标(不要点击每个点,太多了麻烦)...
  12. Kubernetes查看日志命令
  13. 医药箱APP静态小项目
  14. Vulkan 多线程渲染
  15. 分享几款UI设计师快速提升工作效率的辅助设计软件
  16. 剪映专业版v1.4.1正式版!一款全能好用的视频编辑工具
  17. 新用户报到,以及门电路相关手游推荐
  18. 详细讲解二极管的钳位电路和限幅电路
  19. Deblur-NeRF CVPR 2022
  20. linux下float的寄存器,检测x86上Linux的非正常浮动操作(Detecting denormal float operations on Linux for x86)...

热门文章

  1. linux无法打开或写入文件格式,Centos系统下“无法打开并写入文件”问题的解决...
  2. 【Linux】fork()
  3. ShareSDK QQ分享遇到的问题记录
  4. nrf51822 --- 外部中断 (按键)
  5. CISP-PTE2022最新考试经验分享
  6. Orange Business Services 携手俄罗斯劢龙时装集团打造虚拟试衣间 助其改善全球化生产制造模式...
  7. 北邮信通复试题c语言,『转贴』北邮信通院跑调剂的经历
  8. 如何取淘宝登录的完整cookies
  9. [Hacking]对于删除上网痕迹的深入探讨
  10. 同程SRC巡风-内网漏洞应急巡航扫描系统