ESP-C3入门16. 低功耗蓝牙广播

在这里插入图片描述

一、蓝牙协议栈

本文聚集于蓝牙广播,对蓝牙的整体协议栈并不做太多介绍。

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. 启动广播流程

  1. 初始化蓝牙适配器
  2. 启用蓝牙控制器
  3. 初始化蓝牙协议栈
  4. 启用蓝牙协议栈
  5. 注册GAP事件
  6. 设置扩展广播参数
  7. 启动广播

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 设备的连接和广播。它包括了设备名称、外观、首选连接参数和广播数据等信息。

猜你喜欢

转载自blog.csdn.net/xundh/article/details/129788456
今日推荐