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

共1条 1/1 1 跳转至

【e起DIY】低功耗蓝牙温湿度计:获取DHT11的温湿度

工程师
2026-06-17 00:27:15     打赏

在DHT11的手册中,DHT11仅有3根线,连接示意图如下,包括一根电源,一根地线,一根信号线

image.png

由于这里不存在什么协议,因此信号线可以任意选择一个GPIO接口即可实现信号传输,因此我选用了Mikro Bus Connector右下角的三个排母接口,其中这里的SDA对应了PTB4

image.png

我基于开发的工程是peripheral_hr

image.png

定义基本参数

/* DHT11 sensor on PTB4 */
#define DHT11_NODE DT_NODELABEL(gpiob)
#define DHT11_PIN  4

static const struct device *dht11_dev;
static struct k_mutex dht11_mutex;

设置us级的延时用于DHT11温湿度获取的延时

/* Simple delay using nop loops - avoids k_busy_wait overhead */
static inline void delay_us(uint32_t us)
{
    for (volatile uint32_t i = 0; i < us * 8; i++) {
        __asm__ volatile("nop");
    }
}

DHT11初始化和读取函数

static int dht11_init(void)
{
    dht11_dev = DEVICE_DT_GET(DHT11_NODE);
    if (!device_is_ready(dht11_dev)) {
        printk("DHT11: GPIO device not ready\n");
        return -ENODEV;
    }
    k_mutex_init(&dht11_mutex);

    int ret = gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_OUTPUT);
    if (ret < 0) {
        printk("DHT11: config failed (err %d)\n", ret);
        return ret;
    }
    gpio_pin_set(dht11_dev, DHT11_PIN, 1);
    k_sleep(K_MSEC(1000));
    return 0;
}

static int dht11_read(int16_t *temp_x10, int16_t *hum_x10)
{
    uint8_t data[5] = {0};
    int ret = 0;
    unsigned int irq_key = 0;
    uint32_t w;

    k_mutex_lock(&dht11_mutex, K_FOREVER);

    /*
     * Start: pull low 20ms, then release high 30us
     */
    gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_OUTPUT);
    gpio_pin_set(dht11_dev, DHT11_PIN, 0);
    k_sleep(K_MSEC(20));
    gpio_pin_set(dht11_dev, DHT11_PIN, 1);
    delay_us(30);

    /*
     * Switch to input. DHT11 will respond within ~40us.
     */
    gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_INPUT);

    irq_key = irq_lock();

    /*
     * Wait for DHT11 response: low ~80us, then high ~80us.
     * Then DHT11 pulls low to start first data bit (50us low).
     * We eat that first 50us low here so bit loop is clean.
     */
    w = 0;
    while (gpio_pin_get(dht11_dev, DHT11_PIN) == 1) {
        if (++w > 500) { printk("DHT11: no resp\n"); ret = -ETIMEDOUT; goto out; }
        delay_us(1);
    }
    w = 0;
    while (gpio_pin_get(dht11_dev, DHT11_PIN) == 0) {
        if (++w > 200) { ret = -ETIMEDOUT; goto out; }
        delay_us(1);
    }
    w = 0;
    while (gpio_pin_get(dht11_dev, DHT11_PIN) == 1) {
        if (++w > 200) { ret = -ETIMEDOUT; goto out; }
        delay_us(1);
    }

    /*
     * Read 40 bits. Bit format:
     *   - 50us low
     *   - ~27us high = 0
     *   - ~70us high = 1
     *
     * After DHT11's 80us high response, the line falls for ~50us.
     * That fall is the start of bit 0 byte 0. We already consumed
     * the 80us high above, and the next edge we see will be the
     * 50us low of bit 0. So our first loop action (wait for low end)
     * correctly syncs to bit 0.
     */
    for (int b = 0; b < 5; b++) {
        uint8_t val = 0;

        for (int i = 0; i < 8; i++) {
            /* Wait for 50us low to end */
            w = 0;
            while (gpio_pin_get(dht11_dev, DHT11_PIN) == 0) {
                if (++w > 200) { ret = -ETIMEDOUT; goto out; }
                delay_us(1);
            }

            /* Wait 35us from rising edge. At this point:
             * - bit 0: 27us pulse is over, pin is low
             * - bit 1: 70us pulse still active, pin is high
             */
            delay_us(35);

            val <<= 1;
            if (gpio_pin_get(dht11_dev, DHT11_PIN) == 1) {
                val |= 1;
                /* Consume remainder of 70us high */
                w = 0;
                while (gpio_pin_get(dht11_dev, DHT11_PIN) == 1) {
                    if (++w > 200) break;
                    delay_us(1);
                }
            }
        }

        data[b] = val;
    }

    irq_unlock(irq_key);
    irq_key = 0;

    /*
     * Parse DHT11 data
     */
    printk("DHT11 raw: %02x %02x %02x %02x %02x\n",
           data[0], data[1], data[2], data[3], data[4]);

    *hum_x10  = (int16_t)data[0] * 10 + (int16_t)data[1];
    *temp_x10 = (int16_t)data[2] * 10 + (int16_t)data[3];
    ret = 0;

out:
    if (irq_key) {
        irq_unlock(irq_key);
    }
    gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_OUTPUT);
    gpio_pin_set(dht11_dev, DHT11_PIN, 1);
    k_mutex_unlock(&dht11_mutex);
    return ret;
}

完整main.c如下,其余部分主要是蓝牙,对温湿度获取没什么太大影响

/* main.c - Application main entry point */

/*
 * Copyright (c) 2024 Nordic Semiconductor ASA
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>
#include <zephyr/irq.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/services/bas.h>
#include <zephyr/bluetooth/services/hrs.h>

/* DHT11 sensor on PTB4 */
#define DHT11_NODE DT_NODELABEL(gpiob)
#define DHT11_PIN  4

static const struct device *dht11_dev;
static struct k_mutex dht11_mutex;

/* Simple delay using nop loops - avoids k_busy_wait overhead */
static inline void delay_us(uint32_t us)
{
    for (volatile uint32_t i = 0; i < us * 8; i++) {
        __asm__ volatile("nop");
    }
}

static int dht11_init(void)
{
    dht11_dev = DEVICE_DT_GET(DHT11_NODE);
    if (!device_is_ready(dht11_dev)) {
        printk("DHT11: GPIO device not ready\n");
        return -ENODEV;
    }
    k_mutex_init(&dht11_mutex);

    int ret = gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_OUTPUT);
    if (ret < 0) {
        printk("DHT11: config failed (err %d)\n", ret);
        return ret;
    }
    gpio_pin_set(dht11_dev, DHT11_PIN, 1);
    k_sleep(K_MSEC(1000));
    return 0;
}

static int dht11_read(int16_t *temp_x10, int16_t *hum_x10)
{
    uint8_t data[5] = {0};
    int ret = 0;
    unsigned int irq_key = 0;
    uint32_t w;

    k_mutex_lock(&dht11_mutex, K_FOREVER);

    /*
     * Start: pull low 20ms, then release high 30us
     */
    gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_OUTPUT);
    gpio_pin_set(dht11_dev, DHT11_PIN, 0);
    k_sleep(K_MSEC(20));
    gpio_pin_set(dht11_dev, DHT11_PIN, 1);
    delay_us(30);

    /*
     * Switch to input. DHT11 will respond within ~40us.
     */
    gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_INPUT);

    irq_key = irq_lock();

    /*
     * Wait for DHT11 response: low ~80us, then high ~80us.
     * Then DHT11 pulls low to start first data bit (50us low).
     * We eat that first 50us low here so bit loop is clean.
     */
    w = 0;
    while (gpio_pin_get(dht11_dev, DHT11_PIN) == 1) {
        if (++w > 500) { printk("DHT11: no resp\n"); ret = -ETIMEDOUT; goto out; }
        delay_us(1);
    }
    w = 0;
    while (gpio_pin_get(dht11_dev, DHT11_PIN) == 0) {
        if (++w > 200) { ret = -ETIMEDOUT; goto out; }
        delay_us(1);
    }
    w = 0;
    while (gpio_pin_get(dht11_dev, DHT11_PIN) == 1) {
        if (++w > 200) { ret = -ETIMEDOUT; goto out; }
        delay_us(1);
    }

    /*
     * Read 40 bits. Bit format:
     *   - 50us low
     *   - ~27us high = 0
     *   - ~70us high = 1
     *
     * After DHT11's 80us high response, the line falls for ~50us.
     * That fall is the start of bit 0 byte 0. We already consumed
     * the 80us high above, and the next edge we see will be the
     * 50us low of bit 0. So our first loop action (wait for low end)
     * correctly syncs to bit 0.
     */
    for (int b = 0; b < 5; b++) {
        uint8_t val = 0;

        for (int i = 0; i < 8; i++) {
            /* Wait for 50us low to end */
            w = 0;
            while (gpio_pin_get(dht11_dev, DHT11_PIN) == 0) {
                if (++w > 200) { ret = -ETIMEDOUT; goto out; }
                delay_us(1);
            }

            /* Wait 35us from rising edge. At this point:
             * - bit 0: 27us pulse is over, pin is low
             * - bit 1: 70us pulse still active, pin is high
             */
            delay_us(35);

            val <<= 1;
            if (gpio_pin_get(dht11_dev, DHT11_PIN) == 1) {
                val |= 1;
                /* Consume remainder of 70us high */
                w = 0;
                while (gpio_pin_get(dht11_dev, DHT11_PIN) == 1) {
                    if (++w > 200) break;
                    delay_us(1);
                }
            }
        }

        data[b] = val;
    }

    irq_unlock(irq_key);
    irq_key = 0;

    /*
     * Parse DHT11 data
     */
    printk("DHT11 raw: %02x %02x %02x %02x %02x\n",
           data[0], data[1], data[2], data[3], data[4]);

    *hum_x10  = (int16_t)data[0] * 10 + (int16_t)data[1];
    *temp_x10 = (int16_t)data[2] * 10 + (int16_t)data[3];
    ret = 0;

out:
    if (irq_key) {
        irq_unlock(irq_key);
    }
    gpio_pin_configure(dht11_dev, DHT11_PIN, GPIO_OUTPUT);
    gpio_pin_set(dht11_dev, DHT11_PIN, 1);
    k_mutex_unlock(&dht11_mutex);
    return ret;
}

static bool hrf_ntf_enabled;

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_UUID16_ALL,
              BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
              BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
              BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)),
};

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

enum {
    STATE_CONNECTED,
    STATE_DISCONNECTED,
    STATE_BITS,
};

static ATOMIC_DEFINE(state, STATE_BITS);

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");
        (void)atomic_set_bit(state, STATE_CONNECTED);
    }
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
    printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
    (void)atomic_set_bit(state, STATE_DISCONNECTED);
}

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

static void hrs_ntf_changed(bool enabled)
{
    hrf_ntf_enabled = enabled;
    printk("HRS notification status changed: %s\n", enabled ? "enabled" : "disabled");
}

static struct bt_hrs_cb hrs_cb = {
    .ntf_changed = hrs_ntf_changed,
};

static void auth_cancel(struct bt_conn *conn)
{
    char addr[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    printk("Pairing cancelled: %s\n", addr);
}

static struct bt_conn_auth_cb auth_cb_display = {
    .cancel = auth_cancel,
};

static void bas_notify(void)
{
    uint8_t battery_level = bt_bas_get_battery_level();
    battery_level--;
    if (!battery_level) {
        battery_level = 100U;
    }
    bt_bas_set_battery_level(battery_level);
}

static void hrs_notify(void)
{
    static uint8_t heartrate = 90U;
    heartrate++;
    if (heartrate == 160U) {
        heartrate = 90U;
    }
    if (hrf_ntf_enabled) {
        bt_hrs_notify(heartrate);
    }
}

int main(void)
{
    int err;

    printk("Initializing DHT11 sensor on PTB4...\n");
    err = dht11_init();
    if (err) {
        printk("DHT11: Init failed (err %d)\n", err);
    } else {
        printk("DHT11: Init OK\n");

        int16_t temperature_x10;
        int16_t humidity_x10;
        int read_ret = dht11_read(&temperature_x10, &humidity_x10);

        if (read_ret == 0) {
            printk("Temperature: %d.%d C, Humidity: %d.%d %%RH\n",
                   temperature_x10 / 10, temperature_x10 % 10,
                   humidity_x10 / 10, humidity_x10 % 10);
        } else {
            printk("DHT11: first read failed (err %d)\n", read_ret);
        }
    }

    while (1) {
        k_sleep(K_SECONDS(2));
        if (dht11_dev != NULL) {
            int16_t temperature_x10;
            int16_t humidity_x10;
            if (dht11_read(&temperature_x10, &humidity_x10) == 0) {
                printk("Temperature: %d.%d C, Humidity: %d.%d %%RH\n",
                       temperature_x10 / 10, temperature_x10 % 10,
                       humidity_x10 / 10, humidity_x10 % 10);
            }
        }
    }
    return 0;
}

串口输出如下,输出包括原始数据和解析得到的温湿度数据

image.png

用嘴巴对着温湿度计哈气,可以看到温湿度均升高了

image.png

完整工程代码如下

peripheral_hr.zip




关键词: DHT11     温湿度     FRDM-MCXW71    

共1条 1/1 1 跳转至

回复

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