Zephyr上开发 BLE(低功耗蓝牙)入门应用

在 Zephyr RTOS 上开发 BLE(低功耗蓝牙)应用,内容涵盖从基础概念到实战案例的完整路径。Zephyr 集成了功能丰富、高度可配置的蓝牙协议栈,并提供了从客户端(Peripheral)服务端(Central) 的全套支持。

🏗️ 第一部分:核心概念与开发准备

在学习具体代码前,理解 BLE 的核心架构至关重要。

  • GAP (Generic Access Profile):决定设备的角色和工作模式。在 Zephyr 中,最常用的角色是:

    • 外设 (Peripheral):通常是传感器、手表等资源受限设备,发送广播并等待连接。

    • 中心 (Central):通常是手机、网关等资源丰富的设备,扫描并发起连接。

  • GATT (Generic Attribute Profile) :定义了连接建立后的数据通信规范 ,核心概念是 Service(服务)Characteristic(特征值)

    • Service:功能的集合。

    • Characteristic:实际的数据容器,可被读写或通知。

  • 开发环境

    • 硬件:推荐使用 Nordic nRF52840 DK 等官方支持良好的开发板。

    • 软件 :手机端可安装 nRF Connect App 进行调试;PC端可使用 west 命令行工具构建和烧录。

⚙️ 第二部分:Zephyr 项目配置

prj.conf 文件中启用必要的蓝牙 Kconfig 选项。

1. 作为服务端(Central)

kconfig

复制代码
# 启用蓝牙基础功能
CONFIG_BT=y

# 启用外设角色 (Peripheral)[reference:11]
CONFIG_BT_PERIPHERAL=y

# 根据需要启用 GATT 服务
CONFIG_BT_GATT_CLIENT=n  # 作为服务端,通常不启用客户端功能
CONFIG_BT_GATT_SERVER=y  # 作为服务端,启用服务器功能
2. 作为 客户端 (Peripheral)

kconfig

复制代码
# 启用蓝牙基础功能
CONFIG_BT=y

# 启用中心角色 (Central)[reference:12]
CONFIG_BT_CENTRAL=y

# 根据需要启用 GATT 客户端
CONFIG_BT_GATT_CLIENT=y  # 作为客户端,启用客户端功能
CONFIG_BT_GATT_SERVER=n  # 作为客户端,通常不启用服务器功能

注意CONFIG_BT_MAX_CONN 定义了最大连接数,在多连接场景下需要调整。


📡 第三部分:客户端开发实战

作为客户端,主要任务是构建并注册 GATT 服务 ,然后广播以等待中心设备连接。

1. 定义 GATT 服务

通常使用 BT_GATT_SERVICE_DEFINE 宏静态定义服务及其包含的特征值。

复制代码
// 1. 声明自定义服务 UUID (示例)
#define BT_UUID_MY_SERVICE_VAL \
    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)

// 2. 定义服务
static struct bt_gatt_attr my_attrs[] = {
    // 服务声明
    BT_GATT_PRIMARY_SERVICE(BT_UUID_DECLARE_128(BT_UUID_MY_SERVICE_VAL)),

    // 特征值声明:可读、可写、带通知
    BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(...), // 特征值 UUID
                           BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_NOTIFY,
                           BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
                           my_read_cb,   // 读回调
                           my_write_cb,  // 写回调
                           NULL),        // 特征值数据
};
BT_GATT_SERVICE_DEFINE(my_svc, my_attrs);

提示BT_GATT_SERVICE_DEFINE 是静态声明方式,Zephyr 也提供了 bt_gatt_service_register API 用于动态注册。

2. 启动广播
复制代码
#include <zephyr/bluetooth/bluetooth.h>

void start_advertising(void)
{
    // 配置广播数据
    struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(
        BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_IDENTITY,
        BT_GAP_ADV_FAST_INT_MIN_2,
        BT_GAP_ADV_FAST_INT_MAX_2,
        NULL
    );

    // 配置广播数据包内容
    struct bt_data ad[] = {
        BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
        BT_DATA_BYTES(BT_DATA_UUID16_ALL, ...), // 可在此添加服务的 16-bit UUID
    };

    // 开始广播[reference:18]
    int err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), NULL, 0);
    if (err) {
        printk("Advertising failed to start (err %d)\n", err);
    }
}
3. 实现回调函数

在读写回调函数中处理来自客户端的请求。例如,在写回调中更新状态,并在需要时通过 bt_gatt_notify 主动推送数据。

复制代码
static ssize_t my_write_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                           const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    // 处理写入的数据...
    // 如果需要,触发通知
    // bt_gatt_notify(conn, attr, data, len);
    return len;
}

🔍 第四部分:服务端开发实战

作为服务端,主要任务是扫描连接发现服务读写数据

1. 启动扫描
复制代码
#include <zephyr/bluetooth/bluetooth.h>

static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
                         struct net_buf_simple *ad)
{
    // 1. 过滤设备 (例如,只关心特定服务或 RSSI 阈值)[reference:21][reference:22]
    // 2. 停止扫描[reference:23]
    bt_le_scan_stop();
    // 3. 发起连接[reference:24]
    bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &default_conn);
}

void start_scanning(void)
{
    struct bt_le_scan_param scan_param = {
        .type = BT_LE_SCAN_TYPE_PASSIVE, // 或 ACTIVE[reference:25]
        .options = BT_LE_SCAN_OPT_NONE,
        .interval = BT_GAP_SCAN_FAST_INTERVAL,
        .window = BT_GAP_SCAN_FAST_WINDOW,
    };

    int err = bt_le_scan_start(&scan_param, device_found); /* 注册扫描回调[reference:26] */
    if (err) {
        printk("Scanning failed to start (err %d)\n", err);
    }
}

注意samples/bluetooth/central 是一个非常好的入门示例。

2. 处理连接与发现服务

连接建立后,在 connected 回调中开始服务发现。

复制代码
static void connected(struct bt_conn *conn, uint8_t err)
{
    if (err) {
        // 处理连接失败
        return;
    }
    // 开始服务发现
    struct bt_gatt_discover_params *params = ...;
    params->uuid = BT_UUID_DECLARE_16(YOUR_SERVICE_UUID);
    params->func = discover_cb;
    params->start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
    params->end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
    params->type = BT_GATT_DISCOVER_PRIMARY;

    bt_gatt_discover(conn, params);
}
3. 处理发现结果与数据交互

在发现回调中,根据结果找到目标特征值句柄,然后进行读写或订阅通知。

复制代码
static uint8_t discover_cb(struct bt_conn *conn,
                           const struct bt_gatt_attr *attr,
                           struct bt_gatt_discover_params *params)
{
    if (!attr) {
        // 发现完成
        return BT_GATT_ITER_STOP;
    }

    // 根据 attr->uuid 判断是否为目标特征值
    // 保存特征值的句柄 (bt_gatt_attr_value_handle(attr))

    // 发起读、写或订阅操作
    // bt_gatt_read(conn, &read_params);
    // bt_gatt_write(conn, &write_params);
    // bt_gatt_subscribe(conn, &subscribe_params);

    return BT_GATT_ITER_CONTINUE;
}

🧪 第五部分:官方示例与学习路径

Zephyr 官方提供了大量示例,是学习和开发的最佳参考。建议按以下路径学习:

  • 入门 (基础角色)

    • 客户端samples/bluetooth/peripheralsamples/bluetooth/peripheral_hr

    • 服务端samples/bluetooth/central

  • 进阶 (特定功能)

    • 自定义服务samples/bluetooth/peripheral_esp

    • 多连接samples/bluetooth/central_multilink

    • 数据传输samples/bluetooth/peripheral_gatt_write

    • 广播者/观察者samples/bluetooth/broadcastersamples/bluetooth/observer

    • 高级应用samples/bluetooth/ots_clientsamples/bluetooth/central_hr

提示 :所有示例的构建和烧录方式相同,即使用 west build -b <your_board> <path_to_sample>


💎 总结与建议

  1. 从基础开始 :先运行 peripheralcentral 示例,理解 BLE 连接建立的基本流程。

  2. 善用工具 :使用 nRF Connect 手机 App 观察广播包、服务和数据交互,对理解 BLE 行为非常有帮助。

  3. 循序渐进 :从修改现有示例(如 peripheral_hr)开始,逐步添加自定义服务和特征值。

  4. 理解回调 :Zephyr BLE 是事件驱动的,connecteddiscoverednotified 等回调函数是应用逻辑的核心。

  5. 查阅文档:官方文档是最权威的资料,遇到问题时应首先查阅。

Zephyr 的 BLE 开发体验非常出色,其开放、完整的协议栈是你构建可靠蓝牙应用的坚实基础。希望这份指南能帮助你顺利起步。

相关推荐
JNX_SEMI1 小时前
ATR2652 GNSS双频LNA:0.75dB噪声系数与22dB高增益设计
前端·单片机·嵌入式硬件·物联网·硬件工程
三十岁老牛再出发2 小时前
07.08.&07.09.每日总结
c语言·python
Luminous.2 小时前
51 单片机--day1
单片机·嵌入式硬件
三品吉他手会点灯2 小时前
嵌入式机器学习 - 学习笔记1.0.1 - 从 Alexa 到 TinyML:为什么机器学习开始跑进微控制器?
人工智能·笔记·嵌入式硬件·学习·机器学习
nuoxin1142 小时前
HR4988替代A4988-富利威
网络·人工智能·嵌入式硬件·fpga开发·dsp开发
老梁agent2 小时前
为什么你的 Agent 答错问题你查不出来?
物联网·agent
KaifuZeng3 小时前
通信与接口协议面试十七、SFP光模块
单片机·嵌入式硬件·面试·通信与接口协议
深圳尚鼎3 小时前
尚鼎防潮柜分析:三核EtherCAT实时控制MCU/DSP发展趋势
单片机·嵌入式硬件
lzqrzpt3 小时前
LED驱动电源行业知名厂家技术特点与市场格局梳理
python·物联网