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

共1条 1/1 1 跳转至

【e起DIY】低功耗蓝牙温湿度计—成果贴

菜鸟
2026-06-17 09:36:17     打赏

【e起DIY】低功耗蓝牙温湿度计 — 成果贴

基于 NXP FRDM-MCXW71 + Zephyr RTOS,使用 DHT11 传感器采集温湿度数据,通过 BLE 自定义 GATT 服务实时透传至手机 App。双核架构(CM33 主核 + NBU 无线核),全程无需配对加密,开箱即连。


一、硬件介绍

1.1 主控 — NXP FRDM-MCXW71

项目参数



主控芯片NXP MCXW716C
主核ARM Cortex-M33F @ 96MHz
无线子核 NBU独立 CM3 核,专用于 BLE / 802.15.4 Controller
Flash1MB(主核)+ NBU 独立闪存
SRAM128KB(主核)+ NBU 独立内存
蓝牙BLE 5.x,主核与 NBU 通过共享内存 RPMSG 通信(非 UART HCI)

1.2 板载外设与本项目用途

外设引脚本项目中用途




蓝色 LEDGPIOC Pin 1BLE 连接状态指示(快闪 100ms = 已连接 / 慢闪 500ms = 未连接)
MCU-LINK 调试器板载 CMSIS-DAP一键烧录 + 串口日志
Arduino R3 接口GPIO/I2C/SPI引出 PA19 接 DHT11 数据线
TPM0 PWMPA19~21本项目中禁用(Device Tree overlay),释放 PA19 给 DHT11

1.3 传感器 — DHT11

参数规格



温度范围 / 精度0~50°C / ±2°C
湿度范围 / 精度20~90%RH / ±5%RH
通信协议单总线(Single-Wire),仅需 1 根 GPIO
采样间隔要求≥1 秒

1.4 应用方向与场景

MCXW716C 双核架构 + BLE/15.4 双模射频,典型场景:

  • 智能家居传感器 — BLE + 低功耗传感器,手机直连查看(本项目方向)

  • 工业无线网络 — 802.15.4 Thread 组网 + CAN 工业总线

  • 可穿戴设备 — 小封装 BLE 心率/温湿度监测

  • 楼宇自动化 — 电池供电 + BLE Mesh 组网


二、设计思路

2.1 模块功能映射

传感器采集:   GPIOA PA19  →  DHT11(bit-bang 单总线)
无线透传:     NBU 子系统  →  BLE GATT Notify 推送至手机
状态指示:     GPIOC PC1   →  蓝色 LED 连接状态
调试日志:     UART 串口   →  printk 温湿度数值

2.2 分层架构

image.png

2.3 数据流

DHT11 ─(单总线)─→ GPIOA PA19 ─→ DHT 驱动 ─→ sensor_value
                                             ├─→ printk 串口输出
                                             └─→ 转换为 BLE 标准单位
                                                  ├─ Temperature: sint16 × 0.01°C
                                                  └─ Humidity:    uint16 × 0.01%
                                                       └─→ GATT Notify ─→ 手机 App

2.4 关键设计决策

决策理由



禁用 TPM0 PWMPA19 默认被 PWM 占用,通过 Device Tree overlay 禁掉释放 GPIO
不启用 BLE 配对 (SMP=n)纯透传场景无需加密,降低复杂度与 Flash 占用
温湿度用标准 UUID (0x2A6E / 0x2A6F)兼容 nRF Connect 等通用 BLE 工具
16-bit 通知值 (sint16 / uint16)BLE 标准数据格式,分辨率 0.01°C / 0.01%RH
采样间隔 2 秒满足 DHT11 ≥1s 约束,兼顾实时性与功耗

三、代码流程

deepseek_mermaid_20260617_6dd36b.png

事件回调(BT 子系统中断上下文)

手机连接 ──→ connected()      ──→ 保存 conn 句柄 + 打印日志
手机断开 ──→ disconnected()   ──→ 释放 conn 句柄
手机订阅 ──→ ccc_cfg_changed() ──→ 置位 notify 标志

三、核心代码 (main.c)

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/byteorder.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>

#define SLEEP_TIME_MS   2000
#define LED0_NODE DT_ALIAS(led0)
#if DT_NODE_EXISTS(LED0_NODE)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
#endif

/* ── BLE GATT 服务:温湿度 ──────────────────── */

#define BT_UUID_TH_SERVICE BT_UUID_DECLARE_128(                        \
   BT_UUID_128_ENCODE(0x12340001, 0x5678, 0x9abc, 0xdef0, 0x123456789abc))
#define BT_UUID_TH_TEMP    BT_UUID_DECLARE_16(0x2A6E)  /* 0.01°C */
#define BT_UUID_TH_HUMID   BT_UUID_DECLARE_16(0x2A6F)  /* 0.01%   */

static int16_t  th_temp  = 0;
static uint16_t th_humid = 0;
static struct bt_conn *current_conn;
static bool notify_temp_enabled, notify_humid_enabled;

static void temp_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
   notify_temp_enabled = (value == BT_GATT_CCC_NOTIFY);
}

static void humid_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
   notify_humid_enabled = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t read_temp(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                        void *buf, uint16_t len, uint16_t offset)
{
   return bt_gatt_attr_read(conn, attr, buf, len, offset,
                            &th_temp, sizeof(th_temp));
}

static ssize_t read_humid(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                         void *buf, uint16_t len, uint16_t offset)
{
   return bt_gatt_attr_read(conn, attr, buf, len, offset,
                            &th_humid, sizeof(th_humid));
}

BT_GATT_SERVICE_DEFINE(th_svc,
   BT_GATT_PRIMARY_SERVICE(BT_UUID_TH_SERVICE),
   BT_GATT_CHARACTERISTIC(BT_UUID_TH_TEMP,
       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
       BT_GATT_PERM_READ, read_temp, NULL, NULL),
   BT_GATT_CCC(temp_ccc_cfg_changed,
       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
   BT_GATT_CHARACTERISTIC(BT_UUID_TH_HUMID,
       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
       BT_GATT_PERM_READ, read_humid, NULL, NULL),
   BT_GATT_CCC(humid_ccc_cfg_changed,
       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);

/* ── 广播 & 连接 ────────────────────────────── */

static const struct bt_data ad[] = {
   BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
   BT_DATA_BYTES(BT_DATA_UUID128_ALL,
       BT_UUID_128_ENCODE(0x12340001, 0x5678, 0x9abc, 0xdef0, 0x123456789abc)),
};

static const struct bt_data sd[] = {
   BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME,
           sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};

static void connected(struct bt_conn *conn, uint8_t err)
{
   if (!err) { current_conn = bt_conn_ref(conn); printk("Connected\n"); }
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
   printk("Disconnected (reason %u)\n", reason);
   if (current_conn) { bt_conn_unref(current_conn); current_conn = NULL; }
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
   .connected = connected, .disconnected = disconnected,
};

/* ── BLE 初始化 & 通知 ──────────────────────── */

static void ble_init(void)
{
   bt_enable(NULL);
   bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad),
                   sd, ARRAY_SIZE(sd));
   printk("BLE advertising as \"%s\"\n", CONFIG_BT_DEVICE_NAME);
}

static void ble_notify(void)
{
   if (!current_conn) return;
   if (notify_temp_enabled)
       bt_gatt_notify(current_conn, &th_svc.attrs[2], &th_temp, sizeof(th_temp));
   if (notify_humid_enabled)
       bt_gatt_notify(current_conn, &th_svc.attrs[5], &th_humid, sizeof(th_humid));
}

/* ── 主函数 ────────────────────────────────── */

int main(void)
{
#if DT_NODE_EXISTS(LED0_NODE)
   gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
#endif

   const struct device *const dht = DEVICE_DT_GET_OR_NULL(DT_ALIAS(dht0));
   if (!device_is_ready(dht)) { printk("DHT11 not ready\n"); return 0; }

   ble_init();

   while (1) {
       if (sensor_sample_fetch(dht) == 0) {
           struct sensor_value temp, humidity;
           sensor_channel_get(dht, SENSOR_CHAN_AMBIENT_TEMP, &temp);
           sensor_channel_get(dht, SENSOR_CHAN_HUMIDITY,  &humidity);

           th_temp  = (int16_t)(temp.val1 * 100 + temp.val2 / 10000);
           th_humid = (uint16_t)(humidity.val1 * 100 + humidity.val2 / 10000);

           printk("DHT11: %d.%d C  %d.%d %%RH\n",
                  temp.val1, temp.val2 / 100000,
                  humidity.val1, humidity.val2 / 100000);
           ble_notify();
       }

#if DT_NODE_EXISTS(LED0_NODE)
       int ms = current_conn ? 100 : 500;   /* 已连接快闪 / 未连接慢闪 */
       gpio_pin_toggle_dt(&led); k_msleep(ms);
       gpio_pin_toggle_dt(&led); k_msleep(SLEEP_TIME_MS - ms);
#else
       k_msleep(SLEEP_TIME_MS);
#endif
   }
   return 0;
}

四、关键配置

4.1 Device Tree Overlay

blinky/boards/frdm_mcxw71.overlay

/ {
   aliases { dht0 = &dht11; };

   dht11: dht11 {
       compatible = "aosong,dht";
       dio-gpios = <&gpioa 19 GPIO_ACTIVE_LOW>;
       status = "okay";
   };
};

&gpioa { status = "okay"; };
&tpm0  { status = "disabled"; };  /* 释放 PA19 */

4.2 Kconfig

blinky/prj.conf

CONFIG_GPIO=y
CONFIG_SENSOR=y
CONFIG_DHT=y
CONFIG_PRINTK=y
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="DHT11-TH-Sensor"
CONFIG_BT_DEVICE_APPEARANCE=0
CONFIG_MBEDTLS=n
CONFIG_BT_SMP=n

SoC 级 defconfig 自动处理 BLE 栈大小、HCI 缓冲区、NBU 核间通信等底层参数。


五、BLE GATT 服务

Service: 12340001-5678-9abc-def0-123456789abc  (自定义 128-bit)
 ├─ Temperature (0x2A6E)  READ | NOTIFY   → sint16, 单位 0.01°C
 │   └─ CCC Descriptor(手机订阅后自动推送)
 └─ Humidity   (0x2A6F)  READ | NOTIFY   → uint16, 单位 0.01%
     └─ CCC Descriptor(手机订阅后自动推送)

手机端使用 nRF Connect 连接 "DHT11-TH-Sensor",展开 Unknown Service,点击 ↓ 订阅温湿度特征即可收到实时数据。弹配对时直接取消,不影响数据传输。


六、烧录步骤

6.1 拉取 NBU 固件

west blobs fetch hal_nxp

blobs fetchimage.png

6.2 烧录 NBU 无线核固件

使用 blhost 工具

  1. 进入 ISP 模式:断开电源 → 按住 SW3 → 接 USB → 松开 SW3

  2. 执行烧录:

blhost.exe -p COM18 -- receive-sb-file ^ E:\Zephyr\external\modules\hal\nxp\zephyr\blobs\mcxw71\mcxw71_nbu_ble.sb3

blhostimage.png

6.3 编译烧录应用

cd blinky
west build -b frdm_mcxw71
west flash

七、效果展示

视频链接:<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=116762796361032&bvid=BV1ZBjL6JEKU&cid=39174406327&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>


低功耗蓝牙温湿度计_哔哩哔哩_bilibili

源代码:blinky.zip

输出方式内容



串口日志DHT11: 25.0°C  60.0%RH(每 2 秒)
LED未连接慢闪 500ms / 已连接快闪 100ms
手机 AppnRF Connect 展开 GATT 服务,订阅后温湿度数值自动刷新

654cb98c625b9109c738141f9891f19aimage.pngimage.png

abade8be64647970a98fda5e68b3c39c

八、总结

本项目充分利用了 FRDM-MCXW71 的双核无线架构

  • CM33 主核运行 Zephyr Sensor 框架 + BLE Host 协议栈,驱动 DHT11 单总线传感器

  • NBU 无线核独立运行 BLE Controller 固件,通过共享内存 RPMSG 与 Host 高效通信

  • Device Tree Overlay 解决了 PA19 引脚复用冲突(TPM0 PWM)

  • 自定义 BLE GATT 服务 + 标准温湿度 UUID,兼容主流 BLE 调试 App

全程无需配对加密,开箱即连即用,适合作为智能家居无线传感器节点的原型参考。




共1条 1/1 1 跳转至

回复

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