这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 磕磕碰碰,终于将读取到的DHT11的温湿度信息上传给蓝牙啦!

共1条 1/1 1 跳转至

磕磕碰碰,终于将读取到的DHT11的温湿度信息上传给蓝牙啦!

助工
2026-06-12 16:47:43     打赏

之前的帖子,成功读取了DHT11的温湿度信息,并且通过串口显示。接下来就是去将温湿度信息通过蓝牙广播出来。

参考着老师的视频和论坛各位老师的帖子,对项目进行改造。

image.png

首先修改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”。打开串口终端,可以看见这里有个错误,那就是蓝牙在初始化时报错。

72414785812551f59662b6a51ac6e289.png

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

image.png

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

image.png

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

image.png

31b469c29186e6d32499e002aed300ed.png

固件文件位置:“zephyr\modules\hal\nxp\zephyr\blobs\mcxw72\mcxw72_nbu_ble_all_hosted.bin”,偏移量“0x48800000”。点击run,就能够将蓝牙固件烧写到板子里。

重新编译代码、烧录、运行。这时串口监视窗口中就没有再报错了。

image.png

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

5d411ea73953a2fffa15e331e8a2287b.jpg

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

image.png

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

44400b95aa8ef8c4eee5d597e7ffd3db.jpg

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

d9fbd1dbcfe846abc68f0b29dc78f511.jpg

同样的解析出来,温度:27.00℃;湿度:64%。

总结一下:虽然过程曲折,但是磕磕碰碰地总算是搞定了。首次接触Zephyr,不得不说,在Zephyr编程还是很简单,很高效。就是安装过程太艰难了。




共1条 1/1 1 跳转至

回复

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