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

共1条 1/1 1 跳转至

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

工程师
2026-06-17 23:16:40     打赏

在之前的帖子中我已经实现了FRDM-MCXW71的Zephyr环境搭建和DHT11获取环境温湿度,本文将介绍将数据通过蓝牙发送到手机app

在进行代码编写之前,需要先烧录ble的固件,其方法是断开电源 -> 按住SW3(ISP) -> 插上电源 -> 松开SW3,接着将mcuxw71_nbu_ble_hosted.sb3通过串口烧录进芯片固件

image.png

接着编写BLE相关代码,DHT11的读取方式在上一篇帖子中已经介绍了,这里不再赘述

定义蓝牙GATT服务相关参数

/* ================================================================
 * BLE Custom GATT Service - DHT11 Temperature & Humidity
 * ================================================================
 *
 * Service UUID:       12345678-1234-1234-1234-123456789abc
 * Temperature Char:   12345678-1234-1234-1234-123456789abd  int16 (0.1°C)
 * Humidity Char:      12345678-1234-1234-1234-123456789abe  int16 (0.1%RH)
 * Status String Char: 12345678-1234-1234-1234-123456789abf  UTF-8 text
 *
 * All three characteristics support Read.
 * Temperature and Humidity support Notify for real-time updates.
 * Status String supports Notify with short format (T:xx.xC H:xx.x%).
 */

/* UUID definitions for the custom DHT11 GATT service */
#define BT_UUID_DHT11_SERVICE_VAL \
    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x1234, 0x1234, 0x123456789abc)
#define BT_UUID_DHT11_TEMP_VAL \
    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x1234, 0x1234, 0x123456789abd)
#define BT_UUID_DHT11_HUM_VAL \
    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x1234, 0x1234, 0x123456789abe)
#define BT_UUID_DHT11_STATUS_VAL \
    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x1234, 0x1234, 0x123456789abf)

/* UUID structures */
static struct bt_uuid_128 dht11_svc_uuid    = BT_UUID_INIT_128(BT_UUID_DHT11_SERVICE_VAL);
static struct bt_uuid_128 dht11_temp_uuid   = BT_UUID_INIT_128(BT_UUID_DHT11_TEMP_VAL);
static struct bt_uuid_128 dht11_hum_uuid    = BT_UUID_INIT_128(BT_UUID_DHT11_HUM_VAL);
static struct bt_uuid_128 dht11_status_uuid = BT_UUID_INIT_128(BT_UUID_DHT11_STATUS_VAL);

/* Global sensor data shared between read callbacks and main loop */
static int16_t g_temperature_x10;  /**< Temperature in 0.1°C */
static int16_t g_humidity_x10;     /**< Humidity in 0.1%RH */
static bool g_ntf_enabled;         /**< CCC notification enable flag */
static char g_status_str[64];      /**< Status string buffer */

定义一些温湿度读取的回调函数

/**
 * @brief GATT read callback for temperature characteristic.
 *
 * Returns the current temperature value as int16 LE (0.1°C resolution).
 * Example: value 279 = 27.9°C.
 */
static ssize_t read_temperature(struct bt_conn *conn,
                const struct bt_gatt_attr *attr,
                void *buf, uint16_t len, uint16_t offset)
{
    int16_t val = sys_cpu_to_le16(g_temperature_x10);
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &val, sizeof(val));
}

/**
 * @brief GATT read callback for humidity characteristic.
 *
 * Returns the current humidity value as int16 LE (0.1%RH resolution).
 * Example: value 754 = 75.4%RH.
 */
static ssize_t read_humidity(struct bt_conn *conn,
                  const struct bt_gatt_attr *attr,
                  void *buf, uint16_t len, uint16_t offset)
{
    int16_t val = sys_cpu_to_le16(g_humidity_x10);
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &val, sizeof(val));
}

/**
 * @brief GATT read callback for status string characteristic.
 *
 * Returns the formatted status string as UTF-8 text.
 * Example: "T:27.9C H:75.4%"
 */
static ssize_t read_status(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,
                 g_status_str, strlen(g_status_str));
}

/**
 * @brief CCC changed callback for DHT11 service characteristics.
 *
 * Called when the client enables or disables notifications (CCC descriptor
 * write). Updates the global notification flag.
 */
static void dht11_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
    g_ntf_enabled = (value == BT_GATT_CCC_NOTIFY);
    printk("DHT11 notifications %s\n", g_ntf_enabled ? "enabled" : "disabled");
}

定义消息输出的格式

/* GATT service definition macro - defines all attributes in order:
 *   attrs[0] = PRIMARY_SERVICE
 *   attrs[1] = Temperature characteristic declaration
 *   attrs[2] = Temperature characteristic value
 *   attrs[3] = Temperature CCC descriptor
 *   attrs[4] = Humidity characteristic declaration
 *   attrs[5] = Humidity characteristic value
 *   attrs[6] = Humidity CCC descriptor
 *   attrs[7] = Status String characteristic declaration
 *   attrs[8] = Status String characteristic value
 *   attrs[9] = Status String CCC descriptor
 */
BT_GATT_SERVICE_DEFINE(dht11_svc,
    BT_GATT_PRIMARY_SERVICE(&dht11_svc_uuid),

    /* Temperature (int16 LE, 0.1°C, Read + Notify) */
    BT_GATT_CHARACTERISTIC(&dht11_temp_uuid.uuid,
        BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
        BT_GATT_PERM_READ,
        read_temperature, NULL, NULL),
    BT_GATT_CCC(dht11_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),

    /* Humidity (int16 LE, 0.1%RH, Read + Notify) */
    BT_GATT_CHARACTERISTIC(&dht11_hum_uuid.uuid,
        BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
        BT_GATT_PERM_READ,
        read_humidity, NULL, NULL),
    BT_GATT_CCC(dht11_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),

    /* Status String (UTF-8 short text, Read + Notify) */
    BT_GATT_CHARACTERISTIC(&dht11_status_uuid.uuid,
        BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
        BT_GATT_PERM_READ,
        read_status, NULL, NULL),
    BT_GATT_CCC(dht11_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);

/* ================================================================
 * BLE Advertising Data
 * ================================================================ */

/* Advertising packet: flags + 128-bit service UUID */
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_DHT11_SERVICE_VAL),
};

/* Scan response packet: device name */
static const struct bt_data sd[] = {
    BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME,
        sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};

蓝牙连接功能

/* ================================================================
 * BLE Connection Management
 * ================================================================ */

/* Connection state bits */
enum { STATE_CONNECTED, STATE_DISCONNECTED, STATE_BITS };
static ATOMIC_DEFINE(state, STATE_BITS);  /**< Atomic connection state */
static struct bt_conn *g_ble_conn;        /**< Active BLE connection handle */

/**
 * @brief BLE connection established callback.
 *
 * Saves the connection handle with reference count and sets the
 * connected state flag for the main loop to detect.
 */
static void connected(struct bt_conn *conn, uint8_t err)
{
    if (err) {
        printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
    } else {
        printk("Connected\n");
        g_ble_conn = bt_conn_ref(conn);
        (void)atomic_set_bit(state, STATE_CONNECTED);
    }
}

/**
 * @brief BLE disconnection callback.
 *
 * Releases the connection handle and sets the disconnected state flag.
 */
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
    printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
    if (g_ble_conn) {
        bt_conn_unref(g_ble_conn);
        g_ble_conn = NULL;
    }
    (void)atomic_set_bit(state, STATE_DISCONNECTED);
}

/* Register connection callbacks */
BT_CONN_CB_DEFINE(conn_callbacks) = {
    .connected = connected,
    .disconnected = disconnected,
};

/**
 * @brief Send GATT notifications for all DHT11 characteristics.
 *
 * Sends notifications for temperature (attrs[2]), humidity (attrs[5]),
 * and status string (attrs[8]) to the connected client.
 * Only called when a BLE connection is active (g_ble_conn != NULL).
 */
static void dht11_notify(void)
{
    if (!g_ble_conn) {
        return;
    }

    int16_t temp_le = sys_cpu_to_le16(g_temperature_x10);
    int16_t hum_le  = sys_cpu_to_le16(g_humidity_x10);

    bt_gatt_notify(g_ble_conn, &dht11_svc.attrs[2], &temp_le, sizeof(temp_le));
    bt_gatt_notify(g_ble_conn, &dht11_svc.attrs[5], &hum_le,  sizeof(hum_le));
    bt_gatt_notify(g_ble_conn, &dht11_svc.attrs[8], g_status_str, strlen(g_status_str));
}

在主函数中,读取DHT11获取到的温湿度,组装数据,发送到蓝牙接收器

/* ================================================================
 * Application Entry Point
 * ================================================================ */

/**
 * @brief Application main function.
 *
 * Initialization sequence:
 *   1. Configure DHT11 sensor on PTB4
 *   2. Read first sample for immediate output
 *   3. Initialize BLE stack
 *   4. Start BLE advertising
 *   5. Enter main loop: read DHT11 every 2 seconds, notify BLE client
 *
 * If BLE initialization fails, falls back to serial-only mode.
 */
int main(void)
{
    int err;

    printk("DHT11 BLE Sensor starting...\n");

    /* ---- Initialize DHT11 sensor ---- */
    printk("Initializing DHT11 on PTB4...\n");
    err = dht11_init();
    if (err) {
        printk("DHT11: Init failed (err %d)\n", err);
    } else {
        printk("DHT11: Init OK\n");
        int16_t t, h;
        if (dht11_read(&t, &h) == 0) {
            g_temperature_x10 = t;
            g_humidity_x10 = h;
            snprintf(g_status_str, sizeof(g_status_str),
                 "T:%d.%dC H:%d.%d%%",
                 t / 10, t % 10, h / 10, h % 10);
            printk("%s\n", g_status_str);
        }
    }

    /* ---- Initialize BLE ---- */
    printk("Initializing Bluetooth...\n");
    err = bt_enable(NULL);
    if (err) {
        printk("Bluetooth init failed (err %d), serial-only mode\n", err);
        goto serial_only;
    }

    printk("Bluetooth initialized\n");

    /* ---- Start BLE advertising ---- */
    printk("Starting advertising...\n");
    err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad),
                  sd, ARRAY_SIZE(sd));
    if (err) {
        printk("Advertising failed (err %d)\n", err);
        goto serial_only;
    }

    printk("Advertising started as '%s'\n", CONFIG_BT_DEVICE_NAME);

    /* ---- Main loop: read sensor + notify BLE ---- */
    while (1) {
        k_sleep(K_SECONDS(2));
        if (!dht11_dev) continue;

        int16_t t, h;
        if (dht11_read(&t, &h) != 0) continue;

        g_temperature_x10 = t;
        g_humidity_x10 = h;
        snprintf(g_status_str, sizeof(g_status_str),
             "T:%d.%dC H:%d.%d%%",
             t / 10, t % 10, h / 10, h % 10);
        printk("%s\n", g_status_str);

        if (atomic_test_bit(state, STATE_CONNECTED)) {
            dht11_notify();
        }
    }

    return 0;

serial_only:
    /* ---- Fallback: serial-only mode (no BLE) ---- */
    printk("DHT11 serial-only mode (no BLE)\n");
    while (1) {
        k_sleep(K_SECONDS(2));
        if (!dht11_dev) continue;
        int16_t t, h;
        if (dht11_read(&t, &h) == 0) {
            printk("Temperature: %d.%d C, Humidity: %d.%d %%RH\n",
                   t / 10, t % 10, h / 10, h % 10);
        }
    }
    return 0;
}

编译烧录代码,可以进行了蓝牙初始化,并且以Zephyr Heartrate Sensor的蓝牙设备名出现,当手机端点击连接后,输出Connected字样作为提示信息image.png

我是用nRF Connect,可以看到可以成功读取温湿度信息,并且可以实时变化

Screenshot_20260617_225924_no.nordicsemi.android.mcp.jpg

完整工程代码如下

peripheral_hr_all.zip




关键词: FRDM-MCXW71     温湿度     蓝牙    

共1条 1/1 1 跳转至

回复

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