ESP-C3入门16. 低功耗蓝牙广播
标签: ESP-C3入门16. 低功耗蓝牙广播 jQuery博客 51CTO博客
2023-04-01 18:23:42 138浏览
ESP-C3入门16. 低功耗蓝牙广播
- 一、蓝牙协议栈
- 1. 协议栈图示
- 2. 广播
- 3. GAP
- 4. PHY
- 5. Coded PHY
- 6. ESP32 内部结构示意图
- 二、蓝牙广播
- 1. BLE 信道
- 2. 广播数据包格式
- 3. AD Structure格式
- 4. AD Stucture类型定义
- 5. 广播数据解析示例
- 6. 广播器和扫描器
- 三、ESP IDF的广播demo
- 1. 1M phy extend adv
- 2. 2M phy extend adv
- 3. 1M phy legacy adv, ADV_IND
- 4. coded phy extend adv
- 四、源码分析
- 1. 参数设置
- 定义6个字节的地址:
- 定义广播数据AD Structure
- 定义启用广播的扩展参数
- 定义扩展广播参数
- 定义GAP事件
- 2. 启动广播流程
- 3. 源代码:
- 4. 运行
- 广播包信息
- 设备Services
一、蓝牙协议栈
本文聚集于蓝牙广播,对蓝牙的整体协议栈并不做太多介绍。
1. 协议栈图示
这里重点介绍一下 Link Layer。
Link Layer 是蓝牙协议栈的第二层,它负责处理蓝牙设备之间的连接和通信。
Link Layer包括两个子层:LL层和LM层,其中:
- LL层负责处理链路管理和数据包传输
- LM层负责处理链路控制和链路维护。
在LL层中,蓝牙广播数据被封装在广播数据包中,并通过广播信道发送到周围的蓝牙设备。
2. 广播
在蓝牙低功耗应用中,广播是一个重要的技术,可以用于实现设备发现、连接维护、位置服务等应用场景。
3. GAP
GAP(Generic Access Profile)是蓝牙协议栈中的一个重要协议,它定义了蓝牙设备的访问模式和行为。GAP 协议为蓝牙设备提供了一个标准化的接口,使得不同的蓝牙设备可以互相识别和交互。其中,广播是 GAP 协议的一个功能,用于在蓝牙设备之间传输短数据。
在蓝牙低功耗(BLE)应用中,GAP 协议和广播密切相关。GAP 协议定义了广播的格式、参数和过程,使得不同设备可以互相识别和交互。在广播过程中,主机设备可以通过 GAP 接口发送广播包,从机设备可以通过 GAP 接口接收广播包,实现设备之间的互动。广播还可以用于设备发现、连接维护、位置服务等应用场景。
4. PHY
PHY代表物理层(Physical Layer),它是蓝牙技术中的一层协议,主要功能包括传输媒介的选取、传输信道的调制和解调、数据的编码和解码、差错检测和纠正等。
在蓝牙技术中,有多种不同的PHY,包括1Mbps的经典蓝牙PHY,2Mbps和Coded S8的LE PHY,用于不同的应用场景和需求。
5. Coded PHY
Coded PHY 是 BLE(蓝牙低功耗)协议定义的一种物理层技术。与传统的 1Mbps 和 2Mbps PHY 相比,Coded PHY 的传输速率较慢,通常在 125kbps 或 500kbps 左右,但可以提供更远的传输距离和更好的抗干扰性能。因此,Coded PHY 常用于物联网等需要远距离传输和低功耗的应用场景。
在ESP32系列芯片中,只有支持BLE5.0的芯片才能支持Coded PHY,IDF版本要4.1及以上。
6. ESP32 内部结构示意图
二、蓝牙广播
1. BLE 信道
低功耗蓝牙(BLE)使用2.4 GHz频段,频段范围为2402 MHz至2480 MHz。BLE的频段范围与Wi-Fi和蓝牙2.0相同,但BLE使用的频道不同,所以它们不会发生冲突。
BLE使用40个频道,每个频道之间的带宽为2 MHz。
各信道划分如下:
其中: 37、38、39是广播信道,其它是数据信道。
2. 广播数据包格式
一个广播数据包最长为37个字节 , 前6个字节为设备地址,后面是数据区,数据区最大31个字节,又分为若干个AD Sturcture。
数据区没用完的话,系统会在后面补0。
3. AD Structure格式
每个AD Stucture由 长度、类型、内容三个部分组成 , 长度指的是类型+内容字节数。
长度 |
类型 |
内容 |
1字节 |
1字节 |
若干字节 |
4. AD Stucture类型定义
一些BLE广播类型定义值如下:
- 0x00:广播标志
- 0x01:不完整的16位服务UUID列表
- 0x02:完整的16位服务UUID列表
- 0x03:不完整的32位服务UUID列表
- 0x04:完整的32位服务UUID列表
- 0x05:不完整的128位服务UUID列表
- 0x06:完整的128位服务UUID列表
- 0x07:本地名称
- 0x08:TX功率级别
- 0x09:蓝牙名称
- 0x0A:简单配对的Hash C
- 0x0B:简单配对的随机数R
- 0x0C:设备ID
- 0x0D:服务数据
- 0x0E:制造商特定数据
- 0x0F:广播间隔
5. 广播数据解析示例
使用一般的安卓端蓝牙调试工具就可以看到针对广播包的解析,下面是一个实例:
部分内容解析如下:
- 0x01:设备标识
- 0x09:完整蓝牙名称(UTF-8编码)
- 0x02:16 Bit UUID
- 0x0a:蓝牙信号发射功率(上面示例无此数据)
- 0xFF:厂商自定义数据
6. 广播器和扫描器
BLE 通过 GAP协议来实现设备之间的互通,其中定义了两种基本的设备类型:
- 广播器(Broadcaster)
- 扫描器(Scanner)
广播器发送广播包,扫描器接收广播包。基于 GAP,BLE还定义了三种设备角色:中央(Central)、外围(Peripheral)和观察者(Observer),分别用于实现不同的通信模式。
在 GAP 中,广播包包含设备的基本信息,如设备名称、设备类型、设备服务等,同时还包含了一些标志位,用于标识设备是否可以被扫描或连接。具体来说,广播包中的 Flags 字段用于标识设备是否支持连接:
- 如果 Flags 字段中包含 LE Limited Discoverable Mode 标志,则表示设备可以被扫描,但不可以被连接;
- 如果 Flags 字段中包含 LE General Discoverable Mode 标志,则表示设备可以被扫描和连接。
三、ESP IDF的广播demo
在ESP32 IDF中BLE广播示例程序 multi_adv_demo
里,演示了同时广播多种不同类型的广播,其中:
1. 1M phy extend adv
使用1Mbps的PHY速率,可连接广播。
2. 2M phy extend adv
使用2MBps的PHY高速广播,可扫描。
3. 1M phy legacy adv, ADV_IND
1M 高速广播,
4. coded phy extend adv
coded phy广播,可连接。
四、源码分析
原来的demo提供了四种类型广播 , 本文主要介绍其中的1MBps PHY部分的代码。
1. 参数设置
首先需要定义发送广播需要的几个参数:
定义6个字节的地址:
// 定义一个 1M PHY 的地址
uint8_t addr_1m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x01};
定义广播数据AD Structure
// 定义 1M PHY 的扩展广播数据
static uint8_t raw_adv_data_1m[] = {
0x02, 0x01, 0x06, // 广播标志
0x02, 0x0a, 0xeb, // 16 位厂商 ID
0x11, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A', // 设备名称
'D', 'V', '_', '1', 'M'
};
定义启用广播的扩展参数
// 定义 1M PHY 的扩展广播参数
esp_ble_gap_ext_adv_t ext_adv[4] = {
// 实例,持续时间,间隔
[0] = {0, 0, 0}
};
定义扩展广播参数
esp_ble_gap_ext_adv_params_t ext_adv_params_1M = {
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE, // 广播类型为可连接
.interval_min = 0x30, // 广播间隔最小值
.interval_max = 0x30, // 广播间隔最大值
.channel_map = ADV_CHNL_ALL, // 广播信道
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, // 过滤策略
.primary_phy = ESP_BLE_GAP_PHY_1M, // 主要物理层
.max_skip = 0, // 最大跳过次数
.secondary_phy = ESP_BLE_GAP_PHY_1M, // 次要物理层
.sid = 0, // 广播 ID
.scan_req_notif = false, // 扫描请求通知
.own_addr_type = BLE_ADDR_TYPE_RANDOM, // 自身地址类型
.tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE, // 发射功率
};
定义GAP事件
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event) {
case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, status %d", param->ext_adv_set_rand_addr.status);
break;
case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT, status %d", param->ext_adv_set_params.status);
break;
case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT, status %d", param->ext_adv_data_set.status);
break;
case ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT, status %d", param->scan_rsp_set.status);
break;
case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT, status %d", param->ext_adv_start.status);
break;
case ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT, status %d", param->ext_adv_stop.status);
break;
default:
break;
}
}
2. 启动广播流程
- 初始化蓝牙适配器
- 启用蓝牙控制器
- 初始化蓝牙协议栈
- 启用蓝牙协议栈
- 注册GAP事件
- 设置扩展广播参数
- 启动广播
3. 源代码:
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/****************************************************************************
*
* 本演示展示了 BLE GATT 服务器。它可以发送广播数据,被客户端连接。
* 运行 gatt_client 演示,客户端演示将自动连接到 gatt_server 演示。
* 客户端演示将在连接后启用 gatt_server 的通知。两个设备将交换数据。
*
****************************************************************************/
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"
#include "esp_gap_ble_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "sdkconfig.h"
#include "freertos/semphr.h"
#define LOG_TAG "MULTI_ADV_DEMO"
// 定义一个宏,用于发送消息并等待信号量
#define FUNC_SEND_WAIT_SEM(func, sem) do {\
esp_err_t __err_rc = (func);\
if (__err_rc != ESP_OK) { \
ESP_LOGE(LOG_TAG, "%s, message send fail, error = %d", __func__, __err_rc); \
} \
xSemaphoreTake(sem, portMAX_DELAY); \
} while(0);
// 定义一个信号量,用于等待消息发送完成
static SemaphoreHandle_t test_sem = NULL;
// 定义一个 1M PHY 的地址
uint8_t addr_1m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x01};
// 定义一个 1M PHY 的扩展广播参数
esp_ble_gap_ext_adv_params_t ext_adv_params_1M = {
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE, // 广播类型为可连接
.interval_min = 0x30, // 广播间隔最小值
.interval_max = 0x30, // 广播间隔最大值
.channel_map = ADV_CHNL_ALL, // 广播信道
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, // 过滤策略
.primary_phy = ESP_BLE_GAP_PHY_1M, // 主要物理层
.max_skip = 0, // 最大跳过次数
.secondary_phy = ESP_BLE_GAP_PHY_1M, // 次要物理层
.sid = 0, // 广播 ID
.scan_req_notif = false, // 扫描请求通知
.own_addr_type = BLE_ADDR_TYPE_RANDOM, // 自身地址类型
.tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE, // 发射功率
};
// 定义 1M PHY 的扩展广播数据
static uint8_t raw_adv_data_1m[] = {
0x02, 0x01, 0x06, // 广播标志
0x02, 0x0a, 0xeb, // 16 位厂商 ID
0x11, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A', // 设备名称
'D', 'V', '_', '1', 'M'
};
// 定义 1M PHY 的扩展广播参数
esp_ble_gap_ext_adv_t ext_adv[4] = {
// 实例,持续时间,间隔
[0] = {0, 0, 0}
};
// 定义 GAP 事件处理函数
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event) {
case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT:
// 设置随机地址完成事件
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, status %d", param->ext_adv_set_rand_addr.status);
break;
case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT:
// 设置参数完成事件
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT, status %d", param->ext_adv_set_params.status);
break;
case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT:
// 设置广播数据完成事件
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT, status %d", param->ext_adv_data_set.status);
break;
case ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT:
// 设置扫描响应数据完成事件
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT, status %d", param->scan_rsp_set.status);
break;
case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT:
// 启动广播完成事件
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT, status %d", param->ext_adv_start.status);
break;
case ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT:
// 停止广播完成事件
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT, status %d", param->ext_adv_stop.status);
break;
default:
break;
}
}
void app_main(void)
{
esp_err_t ret;
// 初始化 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 );
// 释放经典蓝牙控制器
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) {
ESP_LOGE(LOG_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
// 启用蓝牙控制器
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(LOG_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
// 初始化蓝牙协议栈
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(LOG_TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
// 启用蓝牙协议栈
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(LOG_TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
// 注册 GAP 事件处理函数
ret = esp_ble_gap_register_callback(gap_event_handler);
if (ret){
ESP_LOGE(LOG_TAG, "gap register error, error code = %x", ret);
return;
}
vTaskDelay(200 / portTICK_PERIOD_MS);
// 创建信号量
test_sem = xSemaphoreCreateBinary();
// 设置 1M PHY 的扩展广播参数
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_params(0, &ext_adv_params_1M), test_sem);
// 设置 1M PHY 的扩展广播随机地址
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_rand_addr(0, addr_1m), test_sem);
// 配置 1M PHY 的扩展广播数据
FUNC_SEND_WAIT_SEM(esp_ble_gap_config_ext_adv_data_raw(0, sizeof(raw_adv_data_1m), &raw_adv_data_1m[0]), test_sem);
// 启动所有广播
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_start(4, &ext_adv[0]), test_sem);
return;
}
4. 运行
广播包信息
程序烧写运行后,可以通过手机蓝牙调试APP查看到广播信息:
其中广播包的内容与 raw_adv_data_1m 值相同。也可以看到采用的是1M PHY广播。
设备Services
连接设备后,可以看到两个默认的Servicecs,它们通用属性配置文件(GATT)服务,该服务用于在BLE设备上发现服务、特征和描述符。
- 00001801-0000-1000-8000-00805f9b34fb:通用属性配置文件(GATT)服务,用于在 BLE 设备上发现服务、特征和描述符;
- 00001800-0000-1000-8000-00805f9b34fb:通用访问配置文件(GAP)服务,用于管理 BLE 设备的连接和广播。它包括了设备名称、外观、首选连接参数和广播数据等信息。
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
展开评论
您可能感兴趣的博客