这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【e起DIY】低功耗蓝牙温湿度计:基于Zephyr的蓝牙功能测试

共1条 1/1 1 跳转至

【e起DIY】低功耗蓝牙温湿度计:基于Zephyr的蓝牙功能测试

高工
2026-06-10 23:04:52     打赏

        我们根据Zephyr SDK中的例程进行工程创建,实际上蓝牙功能是我们这次任务实现的最基础功能,我们这一次要做一个功能能,就是通过蓝牙输出本次的开发板型号“FRDM-MCXW72”并带有发送的次数编号,间隔是2s。

        我们先创建基础工程:6a598d6c-7c2b-43a2-b4eb-61f467c6ca17.png

        Zephyr官方在samples/bluetooth/peripheral下放了一个非常完整的BLE外设demo,包含了BAS(电池服务)、HRS(心率服务)、IAS(报警服务)、CTS(时间服务)等一堆示例功能。

        基本目录如下:
peripheral/ ├── CMakeLists.txt ├── prj.conf           ← 蓝牙相关的 Kconfig 配置 ├── sample.yaml ├── boards/            ← 板级 overlay(默认不带,先用 SDK 自带) └── src/    └── main.c         ← 我们要改的主入口

prj.conf蓝牙配置

        demo自带的prj.conf把上面提到的服务全打开了,配置有20多行。我们这次任务很简单——只做"自定义GATT服务 + 定时透传编号"——所以配置反而要精简。

        精简后的 prj.conf:

CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_LOG=y CONFIG_BT_DEVICE_NAME="BLE test"
注意我们没有开CONFIG_BT_SETTINGS/CONFIG_BT_SMP/CONFIG_BT_BAS等,因为这次不需要绑定配对,也不需要演示服务,留着只会拖大固件体积。

自定义一个 GATT 服务

        我们做的事情可以概括成一句话:手机连上板子后,板子每 2 秒往手机推一个字符串 "FRDM-MCXW72 #N",N 是个递增的计数器。        拆成 Zephyr 的几个概念:
  • Service(服务):一组相关功能的集合,对应一个UUID

  • Characteristic(特征):服务里实际的数据项,可读可写可通知

  • Notify(通知):服务器主动推数据给手机(需要手机先订阅)

服务的UUID

我们用128-bit自定义UUID(避开蓝牙SIG标准UUID就行):

#define BT_UUID_COUNT_SERVICE_VAL \    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0) #define BT_UUID_COUNT_CHAR_VAL \    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1)

服务定义

        BT_GATT_SERVICE_DEFINE是Zephyr的一个很方便的宏,会自动展开成 GATT表项。用法像写表一样直观:

BT_GATT_SERVICE_DEFINE(count_svc,    BT_GATT_PRIMARY_SERVICE(&count_svc_uuid),    BT_GATT_CHARACTERISTIC(&count_char_uuid.uuid,                           BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,                           BT_GATT_PERM_READ,                           read_count, NULL, count_buf),    BT_GATT_CCC(count_ccc_cfg_changed,                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), );

周期任务

        每2秒触发一次"更新数据 + 发送通知",用k_work_delayable(这是Zephyr内核标准的工作队列机制):

static void count_work_handler(struct k_work *work) {    int err;    count_len = (uint16_t)snprintf((char *)count_buf, sizeof(count_buf),                                   "FRDM-MCXW72 #%u", (unsigned int)send_count);    send_count++;    printk("Notify: %s\n", count_buf);    if (notify_enabled) {        err = bt_gatt_notify(NULL, &count_svc.attrs[2],                             count_buf, count_len);        if (err) {            printk("bt_gatt_notify failed (err %d)\n", err);        }    }    if (streaming) {        k_work_schedule(&count_work, K_MSEC(NOTIFY_INTERVAL_MS));    } } K_WORK_DELAYABLE_DEFINE(count_work, count_work_handler);

连接状态

        BT_CONN_CB_DEFINE是Zephyr的连接回调注册宏,自动展开成一个struct bt_conn_cb,我们要关心两件事:连接成功、断开连接。

static void connected(struct bt_conn *conn, uint8_t err) {    if (err) {        printk("Connection failed (err 0x%02x %s)\n",               err, bt_hci_err_to_str(err));        return;    }    printk("Connected\n");    if (!streaming) {        streaming = true;        k_work_schedule(&count_work, K_MSEC(NOTIFY_INTERVAL_MS));    } } static void disconnected(struct bt_conn *conn, uint8_t reason) {    streaming = false;    (void)k_work_cancel_delayable(&count_work);    printk("Disconnected (reason 0x%02x %s)\n",           reason, bt_hci_err_to_str(reason)); } BT_CONN_CB_DEFINE(conn_callbacks) = {    .connected = connected,    .disconnected = disconnected, };

        关键的"在连接成功后才开始发送"这个时序就是在这里实现的——bt_ready里只启动广播,不开定时器;定时器在connected回调里启动。

main函数

        main反而是整个工程里最简短的:

int main(void) {    int err = bt_enable(bt_ready);    if (err) {        printk("Bluetooth enable failed (err %d)\n", err);    }    return 0; }

测试:

        这里我们找了一个微信小程序专门用于蓝牙测试的,可以看到手机蓝牙开启后可以看到我们的蓝牙名称“BLE test”,具体的服务可以收到对应的数据:

a4e82e47-d360-4e66-8a8c-15ea3dbf21f4.png

        这时板子串口会开始每 2 秒打印一次:

0ada9d0d-683b-48ef-90d2-88d90fbff9f4.png





关键词: Zephyr     蓝牙     功能测试    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]