提交 3bb84607 编写于 作者: Leung_ManWah's avatar Leung_ManWah

Update README.md

上级 0216f760
# 一、项目简介
> **蓝牙网关** 又叫蓝牙探针,是采集蓝牙设备的蓝牙数据,通过 WIFI 等方式传至服务器的一款中继设备。如果类比的话,就如同 WIFI 网络中的无线 AP 的作用。WIFI 网络中无线 AP 是将 WIFI 设备接入网络,而蓝牙网络中的蓝牙网关,是将蓝牙设备接入网络。
![](https://img-blog.csdnimg.cn/img_convert/54eee4a7fb51ae52349d6e420ebb6057.png)
**项目特性:**
* 采用 240MHz Xtensa 32-bit LX6 双核处理器
* 支持 STA 工作模式
......@@ -16,6 +18,7 @@
4. 将采集蓝牙数据通过 TCP 方式发送到指定服务器;
5. 上电或按下网关按键发送蓝牙广播数据,时长5秒(默认)。
![](https://img-blog.csdnimg.cn/img_convert/e7f18c1ecf1358445cde1b4a0d1442d8.png)
# 三、API接口
## 3.1 外设
......@@ -77,5 +80,438 @@ bool BLE_GetUuidFromAdv(uint8_array_t *pAdvData, uint8_array_t *pTypeData) | 从
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](https://gitcode.net/qq_36347513/esp32-wifi_ble_gateway)
将文件解压到 esp-idf/examples 目录下:
![](https://img-blog.csdnimg.cn/img_convert/ee4d1209ce9935f3ce437c1cdf7a3766.png)
## 4.1 工程结构
![](https://img-blog.csdnimg.cn/img_convert/8d0fde9fcb644d4686a03abb8ba899d2.png)
## 4.2 一键配网
首先在 `main.c``app_main()` 中初始化 WIFI 模块,然后创建一个 `network_task` 处理网络通信业务。
```cpp
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 地址和端口:
![](https://img-blog.csdnimg.cn/img_convert/a7a28110a743e4785e6b632e4d0c652e.png)
`network_task` 获取到 WIFI 状态已连接路由器成功后,调用 `Socket_Init()` 初始化socket通信,连接到指定 TCP 服务器。
成功连接 TCP 服务器后,发送一条内容为 `TEST` 的消息。
```cpp
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 地址重复的蓝牙广播包。
```cpp
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()`
```cpp
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** 等依次封装成消息,加入消息队列。
```cpp
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;
}
}
```
![](https://img-blog.csdnimg.cn/img_convert/870fb65e11510c6dd708680d32f504a6.png)
`monitor_task` 中,从消息队列中提取消息发送到 TCP 服务端。
![](https://img-blog.csdnimg.cn/img_convert/aad6dcceb3d3f7bbc2c4a244e30be946.png)
![](https://img-blog.csdnimg.cn/img_convert/c5bf2cd6c233e1f3dd51d086ecd29240.png)
## 4.5 按键发送BLE广播
* 首先在 `main.c``app_main()` 中初始化 BLE 模块,然后创建一个 `monitor_task` 处理事件业务。
* 创建蓝牙广播停止的定时器 `CreateKeepAdvertisingTimer()`,设备上电广播5秒。
```cpp
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` 发送消息。
```cpp
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
{
···
}
}
```
![](https://img-blog.csdnimg.cn/img_convert/8840975c52aafcc98ff803194aa4e9f6.png)
![](https://img-blog.csdnimg.cn/img_convert/7ddda1067494ddf910e06d8eab1c1caa.png)
当设备没有连接上 TCP 服务器时,按下按键通过 BLE 广播发送消息。
```cpp
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);
}
}
}
```
![](https://img-blog.csdnimg.cn/img_convert/753b5ba485d455c435bd9e2f313032d9.png)
---
• 由 [Leung](https://blog.csdn.net/qq_36347513) 写于 2022 年 5 月 17 日
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册