这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【e起DIY】低功耗蓝牙温湿度计成果贴-BLE温湿度透传实战

共1条 1/1 1 跳转至

【e起DIY】低功耗蓝牙温湿度计成果贴-BLE温湿度透传实战

高工
2026-05-26 23:05:34     打赏

        基于 FRDM-MCXW72 + Zephyr + Web Bluetooth 的温湿度监控实战经验。

一、系统架构

        这套系统的数据通路是这样的:SHT40 传感器测量温湿度 → Zephyr 应用读取数据 → 通过蓝牙 NUS 服务发送 → 手机或电脑接收 → 网页实时展示。

整个链路涉及三个核心组件:

        蓝牙 NUS(Nordic UART Service) 是这套方案的关键。它是 Nordic 半导体定义的一个 BLE 服务,本质上把蓝牙连接模拟成了一根串口线——应用层不需要了解 BLE 的 ATT/GATT 协议细节,只需要发送和接收字节流,对方就能收到。NUS 包含两个 Characteristic:TX(发送数据给手机)和 RX(接收手机发来的数据),配合 CCCD(Client Characteristic Configuration Descriptor)控制通知功能。

        Web Bluetooth API 让我们可以直接在网页里连接 BLE 设备,无需安装任何 App 或驱动。Chrome、Edge 等主流浏览器都支持。这意味着只要有一台有蓝牙的电脑或手机,打开网页就能连接设备。

        Zephyr BLE Host 层 负责处理蓝牙协议的 Host 端逻辑。在 MCXW72 这类双核芯片上,应用运行在 CM33 核心,蓝牙基带运行在另一个核心,两者通过 IPC 通信。bt_nus_send() 函数把数据放入 ATT 发送队列,由 Host 层自动处理分包、重传、流量控制。

二、硬件电路与设备树配置

        FRDM-MCXW72 开发板板载 SHT40 传感器,通过 I2C 总线与 MCU 通信。I2C 地址是 0x44,这是 SHT40 出厂设定的固定地址,无法修改。

        在 boards/frdm_mcxw72.overlay 中声明传感器:

&lpi2c1 {
    status = "okay";
    sht40: sht40@44 {
        compatible = "sensirion,sht4x";
        reg = <0x44>;
        label = "SHT40";
    };
};

        &lpi2c1 引用了开发板默认 I2C 总线配置,status = "okay" 使能总线。传感器节点的 compatible 字符串必须与 Zephyr 现有驱动匹配,否则系统找不到对应的驱动文件。

        设备树的 label 属性(这里设为 "SHT40")用于应用层获取设备句柄:

#define SHT40_NODE DT_NODELABEL(sht40)
sensor_dev = DEVICE_DT_GET(SHT40_NODE);
三、应用层逻辑

        应用的 main 函数执行顺序很清晰:注册 NUS 回调 → 初始化蓝牙 → 初始化传感器 → 启动广播 → 主循环定时读取发送。

int main(void) {
    bt_nus_cb_register(&nus_listener, NULL);  // 注册透传回调
    bt_enable(NULL);                           // 初始化蓝牙协议栈
    sensor_init();                              // 初始化 SHT40
    bt_le_adv_start(...);                       // 启动广播
    while (1) {
        k_sleep(K_SECONDS(5));
        if (current_conn && notifications_enabled) {
            k_work_schedule(&sensor_work, K_NO_WAIT);
        }
    }
}

        为什么要用 k_work_schedule 而不是直接调用? 因为 bt_nus_send() 是一个异步发送函数,内部会获取蓝牙锁,如果在中断上下文(如定时器回调)中直接调用,可能导致死锁。k_work 工作项会在系统调度器安全的上下文执行,避免锁冲突。

        notifications_enabled 的作用是什么? 这是个标志位,由 notif_enabled() 回调设置。当手机端成功订阅了 TX Characteristic 的 Notification,Zephyr 框架会调用这个回调,告诉我们"对方已经准备好接收数据了"。如果对方没订阅就发送数据,bt_nus_send() 会返回成功(数据进入队列),但数据实际上会被丢弃。

四、数据透传的实现细节

        bt_nus_send() 是透传的核心函数。它的行为需要理解清楚:

ret = bt_nus_send(current_conn, mqtt_buf, strlen(mqtt_buf));
if (ret < 0) {
    printk("Failed to send NUS data (err %d)\n", ret);
} else {
    printk("NUS send OK: %s\n", mqtt_buf);
}

        bt_nus_send() 返回成功(ret = 0)只表示数据成功放入发送队列,并不代表对方已经收到。 这是蓝牙 ATT 协议的设计:发送方把数据放进队列就立即返回,实际传输由 Host 层后台处理。如果对方没开启 Notification 订阅、或者连接已经断开,数据会在队列中等待或被丢弃,但函数本身已经返回成功了。

        这与 TCP 的 send() 类似——返回成功只表示内核缓冲区接受了这个数据,不代表对方收到了。蓝牙透传是一个"尽力而为"的通道。

发送的数据格式早期是 JSON:{"t":26.83,"h":73.79},但这个格式在短 payload 高频率发送场景下遇到问题。最终改为简洁的纯文本格式 T:26.7 H:72.7,更容易解析且占用带宽更少。

五、网页端连接与数据接收

        网页通过 Web Bluetooth API 连接设备,核心流程是:请求设备 → 连接 GATT Server → 获取 NUS Service → 获取 TX Characteristic → 开启通知。

characteristic = await service.getCharacteristic(NUS_TX_CHAR_UUID);
await characteristic.startNotifications();

// 手动使能 CCCD(部分浏览器需要)
const cccd = await characteristic.getDescriptorByUUID('2902');
await cccd.writeValue(new Uint8Array([0x01, 0x00]));

        startNotifications() 的作用是什么? 这个函数向蓝牙设备发送一个写请求,在 CCCD 中写入 0x0001(表示启用 Notification)。设备收到这个请求后,才会通过 Notification 方式发送数据。nRF Connect 默认会自动做这件事,但某些浏览器实现不完整,需要手动写 CCCD。

        为什么数据格式带有双引号? 固件发送的是字节流 "T:26.7 H:72.7\n",但 TextDecoder.decode() 返回的是 JavaScript 字符串。经过 JSON.stringify() 打印后,引号被转义显示。实际数据处理时要去除首尾引号:text.trim().replace(/^"|"$/g, '')。

        网页端的解析函数会依次尝试 JSON 格式和纯文本格式:

// JSON 格式:{"t":26.7,"h":72.7}
try {
    const data = JSON.parse(text);
    updateData('temperature', data.t.toFixed(2));
    return;
} catch (e) {}

// 纯文本格式:T:26.7 H:72.7
let clean = text.trim().replace(/^"|"$/g, '');
const t = clean.match(/T:(-?\d+\.?\d*)/);
const h = clean.match(/H:(-?\d+\.?\d*)/);
六、调试过程中的坑与解决

        网页端连接成功但不显示数据: 控制台能看到 characteristicvaluechanged 事件触发,数据也收到了,但页面温度湿度始终是 --。排查发现是数据解析逻辑的问题——固件发送的 T:26.7 H:72.7 带双引号,JSON.parse 失败后,正则表达式没匹配到预期的格式。修复方法是在解析前去除引号。

        串口调试效果:

5c171444-68b4-4984-bb80-654aa33fa783.png

        页面数据显示效果:

e6361864-9a1c-42e5-90d0-cbe1d0d6c7be.png

七、总结

使用这套系统的完整流程:

第一步:烧录固件。 用 ninja flash 将编译好的 zephyr.bin 烧录到开发板。

第二步:打开网页。 在 Chrome/Edge 中打开 ble-monitor.html,点击"搜索蓝牙设备",在弹出的设备列表中选择 MCXW72-BLE。

第三步:等待连接。 网页显示"已连接"后,等待约 5 秒,首次数据就会出现在页面上。之后每 5 秒更新一次。

第四步:查看原始数据。 页面底部的黑色区域会显示每次接收到的原始数据,方便排查问题。

        如果连接成功但页面不更新,打开浏览器开发者工具(F12)→ Console,查看是否有 updateData called 日付。如果没有,说明 parseData 解析失败,页面上会显示 [PARSE FAILED]。

八、关键配置参数
参数说明
广播名称MCXW72-BLE手机蓝牙扫描时看到的名字
NUS Service UUID6e400001-b5a3-f393-e0a9-e50e24dcca9e蓝牙服务唯一标识
NUS TX UUID6e400003-b5a3-f393-e0a9-e50e24dcca9e设备发送数据给手机
NUS RX UUID6e400002-b5a3-f393-e0a9-e50e24dcca9e手机发送数据给设备
CCCD UUID2902控制通知功能的 Descriptor
发送间隔5 秒k_sleep(K_SECONDS(5))控制
I2C 地址0x44SHT40 固定地址



关键词: 成果     温湿度     透传     蓝牙    

共1条 1/1 1 跳转至

回复

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