之前的帖子,成功读取了DHT11的温湿度信息,并且通过串口显示。接下来就是去将温湿度信息通过蓝牙广播出来。
参考着老师的视频和论坛各位老师的帖子,对项目进行改造。

首先修改prj.conf文件,添加蓝牙支持。然后修改main.c文件。这里是修改后的文件内容。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/sensor_data_types.h>
#include <zephyr/rtio/rtio.h>
#include <zephyr/dsp/print_format.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gap.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#define DEVICE_NAME "MCXW72_DHT11"
#define BT_UUID_TH_SERVICE BT_UUID_DECLARE_16(0xFFF0)
#define BT_UUID_TH_CHAR BT_UUID_DECLARE_16(0xFFF1)
static struct bt_conn *g_conn = NULL;
static bool notify_subscribed = false;
#define DHT_ALIAS(i) DT_ALIAS(_CONCAT(dht, i))
#define DHT_DEVICE(i, _) IF_ENABLED(DT_NODE_EXISTS(DHT_ALIAS(i)), (DEVICE_DT_GET(DHT_ALIAS(i)),))
/* Support up to 10 temperature/humidity sensors */
static const struct device *const sensors[] = {LISTIFY(10, DHT_DEVICE, ())};
#define DHT_IODEV(i, _) \
IF_ENABLED(DT_NODE_EXISTS(DHT_ALIAS(i)), \
(SENSOR_DT_READ_IODEV(_CONCAT(dht_iodev, i), DHT_ALIAS(i), \
{SENSOR_CHAN_AMBIENT_TEMP, 0}, \
{SENSOR_CHAN_HUMIDITY, 0})))
LISTIFY(10, DHT_IODEV, (;));
#define DHT_IODEV_REF(i, _) COND_CODE_1(DT_NODE_EXISTS(DHT_ALIAS(i)), (CONCAT(&dht_iodev, i)), (NULL))
static struct rtio_iodev *dht_iodev[] = {LISTIFY(10, DHT_IODEV_REF, (,)) };
RTIO_DEFINE(dht_ctx, 1, 1);
static void ccc_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
notify_subscribed = (value == BT_GATT_CCC_NOTIFY);
printk("手机订阅状态: %s\n", notify_subscribed ? "已订阅" : "未订阅");
}
static ssize_t th_read_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
uint8_t dummy[4] = {0};
return bt_gatt_attr_read(conn, attr, buf, len, offset, dummy, sizeof(dummy));
}
BT_GATT_SERVICE_DEFINE(th_service,
BT_GATT_PRIMARY_SERVICE(BT_UUID_TH_SERVICE),
BT_GATT_CHARACTERISTIC(BT_UUID_TH_CHAR,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
th_read_cb, NULL, NULL),
BT_GATT_CCC(ccc_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(BT_DATA_NAME_COMPLETE, DEVICE_NAME, sizeof(DEVICE_NAME)-1),
};
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("连接失败\n");
return;
}
g_conn = conn;
printk("蓝牙连接成功!\n");
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
g_conn = NULL;
notify_subscribed = false;
printk("蓝牙断开连接\n");
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
static void bt_ready(int err)
{
if (err) {
printk("蓝牙初始化失败\n");
return;
}
printk("蓝牙初始化完成\n");
printk("可连接广播运行中\n");
const struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(
BT_LE_ADV_OPT_CONN,
BT_GAP_ADV_FAST_INT_MIN_2,
BT_GAP_ADV_FAST_INT_MAX_2,
NULL
);
bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), NULL, 0);
}
static void bt_delayed_init(struct k_work *work)
{
printk("初始化蓝牙中...\n");
bt_enable(bt_ready);
}
K_WORK_DELAYABLE_DEFINE(bt_work, bt_delayed_init);
// --- 新增:发送蓝牙通知的辅助函数 ---
static void send_th_notification(int16_t temp_centi, int16_t hum_centi)
{
// 如果没有初始化、没有连接、或者手机未订阅,则不发送
if (!bt_is_ready || !g_conn || !notify_subscribed) {
return;
}
// 构造数据包:小端序 (Little Endian)
// Byte 0-1: Temperature (centi-degrees, e.g., 2450 for 24.50°C)
// Byte 2-3: Humidity (centi-percent, e.g., 5100 for 51.00%)
uint8_t data[4];
data[0] = temp_centi & 0xFF;
data[1] = (temp_centi >> 8) & 0xFF;
data[2] = hum_centi & 0xFF;
data[3] = (hum_centi >> 8) & 0xFF;
// 发送通知
// &th_service.attrs[2] 对应的是 Characteristic Value Handle (CCC 是 [3])
int err = bt_gatt_notify(g_conn, &th_service.attrs[2], data, sizeof(data));
if (err) {
printk("Notify error (err %d)\n", err);
}
}
int main(void)
{
int rc, ret;
for (size_t i = 0; i < ARRAY_SIZE(sensors); i++) {
if (!device_is_ready(sensors[i])) {
printk("sensor: device %s not ready.\n", sensors[i]->name);
return 0;
}
}
k_work_schedule(&bt_work, K_SECONDS(1));
// 在 main 循环中替换为:
while (1) {
for (size_t i = 0; i < ARRAY_SIZE(sensors); i++) {
if (!sensors[i]) {
continue;
}
struct device *dev = sensors[i];
// 1. 获取样本
if (sensor_sample_fetch(dev) != 0) {
printk("%s: fetch failed\n", dev->name);
continue;
}
// 2. 获取温度
struct sensor_value temp;
if (sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp) != 0) {
printk("%s: get temp failed\n", dev->name);
continue;
}
// 3. 获取湿度
struct sensor_value hum;
if (sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &hum) != 0) {
printk("%s: get hum failed\n", dev->name);
continue;
}
// 4. 打印 (sensor_value 是 val1 + val2/1000000)
printk("%-16s: TEMP: %d.%06d °C | Hum: %d.%06d %%RH\n", dev->name,
temp.val1, temp.val2, hum.val1, hum.val2);
// 5. 数据转换与发送
// 将 sensor_value 转换为 centi-units (百分位)
// 例如: 24.500000 -> 24 * 100 + 500000/10000 = 2450
int16_t temp_centi = (int16_t)(temp.val1 * 100 + temp.val2 / 10000);
int16_t hum_centi = (int16_t)(hum.val1 * 100 + hum.val2 / 10000);
// 发送蓝牙通知
send_th_notification(temp_centi, hum_centi);
}
k_msleep(1000);
}
return 0;
}这里遇到了一个大问题,那就是添加了蓝牙的初始化后,编译、烧录,但是手机上始终是找不到这个蓝牙名称“MCXW72_DHT11”。打开串口终端,可以看见这里有个错误,那就是蓝牙在初始化时报错。

在群里咨询了各位老师,说是需要烧录一个蓝牙固件。打开老师提供的文档,找到这段刷写蓝牙固件的描述,来刷一下固件。

但是并不顺利,在电脑上,使用linkflash始终无法连上开发板。按住开发板上的SW3按键,然后再插上type-c数据线,但是依然无法连上开发板。

终于在群里老师的指点下,打开MCUXpress IDE,打开这里边的jlink probes工具。


固件文件位置:“zephyr\modules\hal\nxp\zephyr\blobs\mcxw72\mcxw72_nbu_ble_all_hosted.bin”,偏移量“0x48800000”。点击run,就能够将蓝牙固件烧写到板子里。
重新编译代码、烧录、运行。这时串口监视窗口中就没有再报错了。

打开手机上的蓝牙软件,我这使用的是nRF Connect。

成功在蓝牙列表中看见了“MCXW72_DHT11”。点击CONNECT连接上蓝牙。

点击开这个UUID为0XFFF0的服务,点击该特征值的箭头图标(Enable Notifications),就可以看见上送过来的温湿度信息啦!

这里收到的是FC-08-B4-14,解析一下温度就是(0xFC | (0x08 << 8)=0x08FC)23.00℃,湿度(0xB4 | (0x14 << 8) = 0x14B4)53.00%,对着DHT11哈气,就可以看见这个值开始变化。

同样的解析出来,温度:27.00℃;湿度:64%。
总结一下:虽然过程曲折,但是磕磕碰碰地总算是搞定了。首次接触Zephyr,不得不说,在Zephyr编程还是很简单,很高效。就是安装过程太艰难了。
我要赚赏金
