1 系统简介
感谢电子产品世界提供的这次【e起DIY】低功耗蓝牙温湿度计活动参加的机会。本项目基于Zephyr实时操作系统,以NXP FRDM-MCXN947开发板为核心硬件,搭配DHT11温湿度传感器、串口蓝牙模块,搭建一套低功耗、实时性强的环境温湿度采集与无线传输系统。系统可实时采集环境温度、湿度数据,通过低功耗蓝牙(BLE)完成无线数据透传,最终在微信小程序端实时展示监测数值,实现无显示屏、轻量化、便携式的智能环境监测功能。2 系统硬件介绍
项目硬件整体架构由主控核心、感知模块、无线传输模块三部分组成,硬件选型兼顾低功耗、稳定性与适配性。
2.1 FRDM-MCXN947开发板
此次活动提了多个NXP提供的MCU开发板进行选择,我选择的开发板是FRDM-MCXN947。FRDM-MCXN947是NXP推出的高性能、低功耗通用开发板,搭载双ARM Cortex-M33内核,主频最高150MHz,内置2MB双Bank片上Flash、512KB SRAM,集成PowerQuad算力加速器、Smart DMA、低功耗外设单元,专为物联网低功耗终端设备设计,完美适配Zephyr实时操作系统运行需求。开发板自带MCU-Link调试器,支持一键烧录、在线调试,板载标准化排针接口,可快速外接各类传感器与通信模块,同时支持多种低功耗休眠模式,可有效降低设备待机与运行功耗。
2.2 串口蓝牙模块
该开发板无板载蓝牙外设,因此采用外接串口蓝牙模块的方式实现BLE通信,依托丰富的GPIO、UART外设接口,可稳定实现传感器数据采集与串口数据透传功能。模块采用UART串口与FRDM-MCXN947主控通信,支持数据透明传输,无需复杂协议适配,主控采集的温湿度数据可直接通过串口发送至蓝牙模块,再无线广播至手机端。模块支持蓝牙设备一对一绑定连接,连接成功后持续透传数据,适配微信小程序蓝牙通信协议,可快速实现移动端数据接收与展示。

2.3 DHT11温湿度传感器
同时板载只有一个温度传感器,没有湿度传感器,所以也使用了DHT11温湿度传感器模块。DHT11是一款低成本、高精度的数字温湿度复合传感器,内置测温、测湿感应元件及专用信号采集芯片,采用单总线通信协议,仅需一根GPIO口即可实现与主控的数据交互,接线简洁、开发难度低。其温度测量范围为0~50℃,精度±2℃;湿度测量范围为20%~90%RH,精度±5%RH,完全满足日常环境监测的精度需求。传感器工作电压3.3V-5V,功耗极低,适配本项目低功耗设计要求,可实现周期性定时采样,无需持续上电工作。
3、VSCode开发环境搭建
项目使用VSCode构建Zepyhr开发环境,参考官方文档进行配置Zephyr lab installation and preparation — MCUXpresso for VS Code 26.05 documentation。
安装MCUXpresso插件

然后使用MCUXpresso Installer安装如下组件
在VSCode的MCUXpresso中导入远程zephyr仓库,该过程会有点慢

仓库导入完成后,即可选择板卡FRDM-MCXN947,选择加载测试示例工程hello_world

在指定路径生成工程后,点击红色方框图标即可进行编译

但是这里我遇到了一个坑,编译指令CMake build提示如下错误
工作区为 f:\Workplace\zephyr\zephyr\hello_world\hello_world 已启动 生成... E:\CMake\bin\cmake.EXE --build F:/Workplace/zephyr/zephyr/hello_world/hello_world/build --target all -- Error: could not find CMAKE_PROJECT_NAME in Cache 已完成 生成,但出现错误。
根据提示信息,可以知道是CMake配置存在问题,输入以下行命令进行CMake重新配置
CMake configure
重新编译成功

烧录程序,打开串口监视器,程序执行情况如下所示,打印了hello_world及开发板型号信息

4、读取DHT11数据实现
4.1、DHT11介绍
DHT11 是一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个 NTC测温元件,并与一个高性能8位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。
DHT11 与单片机之间能采用简单的单总线进行通信,仅仅需要1个 IO 口。单个数据引脚端口完成输入输出双向传输。其数据包由5Byte(40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。DHT11的数据格式为:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit 温度小数数据+8bit 校验和。其中校验和数据为前四个字节相加。
4.2 zephyr项目配置
prj.conf配置项设置
# 基础内核配置 CONFIG_SYS_CLOCK_EXISTS=y CONFIG_SYS_CLOCK_TICKS_PER_SEC=100 # GPIO驱动开启(DHT11使用) CONFIG_GPIO=y # UART串口驱动开启(蓝牙透传使用) CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y # 传感器子系统开启 CONFIG_SENSOR=y # 低功耗休眠配置 CONFIG_PM=y CONFIG_PM_DEVICE=y # 线程与调度配置 CONFIG_MULTITHREADING=y CONFIG_NUM_PREEMPT_PRIORITIES=8 # 日志调试配置 CONFIG_CONSOLE=y # 启用浮点 printf 支持 CONFIG_REQUIRES_FLOAT_PRINTF=y
app.overlay配置如下,蓝牙串口使用LPUART5,占用P1.16和P1.17两个端口
&flexcomm5 {
status = "okay";
};
&flexcomm5_lpuart5 { // 蓝牙模块所用串口LPUART5
status = "okay";
current-speed = <115200>;
pinctrl-0 = <&pinmux_flexcomm5_lpuart>;
pinctrl-names = "default";
};
&pinctrl {
pinmux_flexcomm5_lpuart: pinmux_flexcomm5_lpuart {
group0 {
pinmux = <FC5_P0_PIO1_16>,
<FC5_P1_PIO1_17>;
slew-rate = "fast";
drive-strength = "low";
input-enable;
bias-pull-up;
};
};
};4.2、DHT11数据读写代码
DHT11读写使用P2.0端口移植裸机的程序,逻辑IO操作通过宏定义替换为zephyr的IO操作
//IO方向设置 #define DHT11_IO_IN() gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_INPUT) //PB12输入模式 #define DHT11_IO_OUT() gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_OUTPUT_LOW) //PB12输出模式 ////IO操作函数 #define DHT11_DQ_OUT(x) gpio_pin_set(dht11_dev, DHT11_PIN, x) //数据端口输出 #define DHT11_DQ_IN gpio_pin_get(dht11_dev, DHT11_PIN) //数据端口输入 #define delay_us(x) k_busy_wait(x) #define delay_ms(x) k_sleep(K_MSEC(x));
通过以下程序,单个IO按照单总线通信的方式,进行一次读写一个位
uint8_t DHT11_Read_Bit(void)
{
uint8_t retry=0;
while(DHT11_DQ_IN&&retry<100)//等待变为低电平
{
retry++;
delay_us(1);
}
retry=0;
while(!DHT11_DQ_IN&&retry<100)//等待变高电平
{
retry++;
delay_us(1);
}
delay_us(40);//等待40us
if(DHT11_DQ_IN)return 1;
else return 0;
}以为操作读写一个字
uint8_t DHT11_Read_Byte(void)
{
uint8_t i,dat;
dat=0;
for (i=0;i<8;i++)
{
dat<<=1;
dat|=DHT11_Read_Bit();
}
return dat;
}进行啥数据校验完整读写40bit数据
uint8_t DHT11_Read_Data(uint8_t *temp,uint8_t *humi)
{
uint8_t buf[5];
uint8_t i;
DHT11_Rst();
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=buf[0];
*temp=buf[2];
}
}else return 1;
return 0;
}4.2、DHT11串口读写测试
DHT11采样线程如下,循环读取数据,然后通过串口进行打印
static void sampling_thread(void *p1, void *p2, void *p3)
{
while (1)
{
int ret = DHT11_Read_Data(&temperature,&humidity); //读取温湿度值
if (ret == 0) {
k_mutex_lock(&data_mutex, K_FOREVER);
printk("DHT11 OK: T=%d H=%d\n", temperature, humidity);
k_mutex_unlock(&data_mutex);
} else {
printk("DHT11 fail ret=%d\n", ret);
}
k_sleep(K_MSEC(200));
}
}然后在main函数中创建该线程
int main(void)
{
hardware_init();
k_thread_create(&sampling_tid, sampling_stack, SAMPLING_STACK_SIZE,
sampling_thread, NULL, NULL, NULL,
SAMPLING_PRIORITY, 0, K_NO_WAIT);
while (1)
{
k_sleep(K_FOREVER);
}
return 0;
}串口打印的温湿度数据如下
4.3、串口蓝牙-小程序读取
我要赚赏金
