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

共1条 1/1 1 跳转至

【e起DIY】低功耗蓝牙温湿度计-成果贴:基于FRDM-MCXN947+esp32-s3的蓝牙温湿度计

菜鸟
2026-06-20 13:00:12     打赏
FRDM-MCXN947 与 ESP32-S3 温湿度蓝牙链路开发报告

1. 项目概述

本项目实现了一条从 DHT11 温湿度传感器到微信小程序显示界面的低功耗蓝牙广播链路。系统由三部分组成:

  1. FRDM-MCXN947:运行 Zephyr 程序,负责读取 DHT11 温湿度数据,并通过 UART 输出标准化 ASCII 数据帧。

  2. ESP32-S3:运行 ESP-IDF 程序,作为 UART 到 BLE 广播的桥接节点,接收 FRDM 的温湿度数据并写入 BLE manufacturer data。

  3. 微信小程序:持续扫描 BLE 广播,解析 ESP32-S3 广播中的温湿度数据并显示。

最终数据链路为:

DHT11 -> FRDM-MCXN947 -> UART -> ESP32-S3 -> BLE Advertising -> 微信小程序

本设计使用无连接 BLE 广播。这样可以减少连接状态管理,微信小程序只需要扫描广播即可得到最新温湿度数据。

2. 硬件连接

DHT11 与 FRDM-MCXN947

DHT11 引脚FRDM-MCXN947
VCC3.3V
GNDGND
DATAArduino D2

DHT11 数据线配置在 app.overlay 中:

dio-gpios = <&arduino_header ARDUINO_HEADER_R3_D2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;

如果使用裸 DHT11 传感器,DATA 与 3.3V 之间需要约 4.7 kOhm 上拉电阻。如果使用常见三针 DHT11 模块,模块上通常已经带上拉电阻。

FRDM-MCXN947 与 ESP32-S3

FRDM-MCXN947ESP32-S3
Arduino D1 / TXGPIO18
GNDGND

注意事项:

  • ESP32-S3 程序默认监听的是 UART1 的 RX GPIO18。

  • 不要把 FRDM TX 接到 ESP32-S3 板子上标注为 RX 或 U0RXD 的 USB 下载串口引脚。

  • 本项目只需要 FRDM 向 ESP32-S3 单向发送数据,因此 ESP32-S3 TX 不必接回 FRDM。

  • 两块开发板必须共地,否则 UART 电平没有共同参考点,会出现乱码或 ESP32-S3 日志中的 UART line too long, dropping。

IMG_20260620_123551.jpg

图中展示了 FRDM-MCXN947、DHT11 模块与 ESP32-S3 的实际接线状态。FRDM 负责采集 DHT11 数据,并通过 Arduino D1/TX 将 UART 数据送入 ESP32-S3 的 GPIO18;两块开发板通过 GND 共地。

3. 数据链路与协议设计

总体链路

系统的数据流为:

1. FRDM-MCXN947 每 2 秒读取一次 DHT11。
2. FRDM 将温湿度转换为百分之一单位。
3. FRDM 通过 Arduino D1/TX 输出 UART ASCII 帧。
4. ESP32-S3 UART1 从 GPIO18 接收 ASCII 帧。
5. ESP32-S3 解析帧,更新内存中的最新温湿度。
6. ESP32-S3 将最新温湿度编码为 12 字节 BLE manufacturer data。
7. 微信小程序持续扫描 BLE 广播,校验并解析 manufacturer data。

FRDM 到 ESP32-S3 的 UART 帧

UART 参数:

115200 baud, 8 data bits, no parity, 1 stop bit

帧格式:

TH,<sequence>,<temperature_centi_c>,<humidity_centi_rh>\r\n

字段说明:

字段说明
TH固定帧头,表示 temperature/humidity
sequenceFRDM 端递增序号
temperature_centi_c温度,单位 0.01 deg C
humidity_centi_rh相对湿度,单位 0.01 %RH
\r\n行结束符

示例:

TH,12,2634,5810

含义:

  • 序号:12

  • 温度:26.34 deg C

  • 湿度:58.10 %RH

ESP32-S3 的 BLE 广播格式

ESP32-S3 设备名:

MCXN947_DHT11

为了避免传统 BLE 广播包 31 字节限制,设计中将设备名放在 scan response 中,广播包本体只放 manufacturer data。

manufacturer data 固定为 12 字节:

Byte内容说明
0..1Company ID小端,默认 0x02E5
2..3ASCII TH协议标识
4Protocol version当前为 1
5Sequence LSBFRDM 序号低 8 位
6..7Temperatureint16 little-endian,单位 0.01 deg C
8..9Humidityuint16 little-endian,单位 0.01 %RH
10Flagsbit0 = 1 表示测量值有效
11Checksum前 11 字节累加和,低 8 位

示例数据 26.34 deg C / 58.10 %RH:

temperature = 2634 = 0x0A4A -> 4A 0A
humidity    = 5810 = 0x16B2 -> B2 16

如果序号低 8 位为 0x0C,则前 11 字节为:

E5 02 54 48 01 0C 4A 0A B2 16 01

最后 1 字节为前 11 字节累加校验和。

4. FRDM-MCXN947 程序核心逻辑

FRDM 程序基于 Zephyr sensor 子系统读取 DHT11。核心设备由 devicetree 指定:

#define DHT_NODE DT_ALIAS(dht0)
#define ESP32_UART_NODE DT_NODELABEL(arduino_serial)

其中:

  • dht0 在 app.overlay 中绑定到 Arduino D2。

  • arduino_serial 是 FRDM-MCXN947 Zephyr board 中定义的 Arduino 串口,实际 TX 对应 Arduino D1。

程序启动时先检查 DHT11 和 UART 设备是否 ready:

if (!device_is_ready(dht)) {
    printk("DHT11 device %s is not ready\r\n", dht->name);
    return 0;
}

if (!device_is_ready(esp32_uart)) {
    printk("ESP32 UART device %s is not ready\r\n", esp32_uart->name);
    return 0;
}

主循环每 2 秒执行一次:

  1. sensor_sample_fetch(dht) 触发 DHT11 采样。

  2. sensor_channel_get(..., SENSOR_CHAN_AMBIENT_TEMP, ...) 读取温度。

  3. sensor_channel_get(..., SENSOR_CHAN_HUMIDITY, ...) 读取湿度。

  4. 通过调试串口打印温湿度。

  5. 通过 arduino_serial 输出 TH,... UART 帧。

Zephyr 的 struct sensor_value 使用 val1 和 val2 表示数值,其中 val2 单位是百万分之一。程序将其转换为百分之一单位:

return (value->val1 * 100) + (value->val2 / 10000);

这样 FRDM 和 ESP32-S3 之间不传浮点数,而是传整数,避免不同平台之间浮点格式和字符串格式差异。

5. ESP32-S3 程序核心逻辑

ESP32-S3 程序负责两个任务:

  1. UART 接收任务:从 GPIO18 接收 FRDM 的 TH,... 数据帧。

  2. BLE GAP 广播:把最新测量值放入 manufacturer data 并重新广播。

UART 初始化

UART 配置为:

#define UART_PORT UART_NUM_1
#define UART_BAUD_RATE 115200

#ifndef CONFIG_DHT11_UART_RX_GPIO
#define CONFIG_DHT11_UART_RX_GPIO 18
#endif

#ifndef CONFIG_DHT11_UART_TX_GPIO
#define CONFIG_DHT11_UART_TX_GPIO 17
#endif

实际链路只需要 RX 和 GND。TX GPIO17 是为 UART 完整配置保留,当前不参与数据链路。

UART 行解析

uart_rx_task() 每次读取 1 字节,直到遇到 \n 才认为一行结束。\r 会被忽略,和 FRDM 输出的 \r\n 匹配。

解析函数要求同时满足:

  • 帧头为 TH

  • 有 4 个字段

  • 温度在 int16_t 范围内

  • 湿度在 uint16_t 范围内

如果解析成功,则更新:

sequence_lsb = (uint8_t)sequence;
temperature_centi = (int16_t)temperature;
humidity_centi = (uint16_t)humidity;
measurement_valid = true;

随后调用 request_advertising_update() 更新 BLE 广播。

BLE 广播更新状态机

BLE 广播数据更新不能直接覆盖正在广播的数据。程序采用如下流程:

收到新 UART 数据
 -> 重建 manufacturer data
 -> 如果当前正在广播,先 stop advertising
 -> 等待 ADV_STOP_COMPLETE 事件
 -> 配置 adv data 和 scan response
 -> 等待 ADV_DATA_SET_COMPLETE 与 SCAN_RSP_DATA_SET_COMPLETE
 -> start advertising

相关状态变量:

变量作用
adv_started当前是否已经开始广播
adv_configuring当前是否正在配置广播数据
adv_update_queued配置或停止过程中是否又有新数据到来
adv_config_pendingadv data 和 scan response 配置完成状态
adv_config_failed本轮配置是否失败

这套状态机保证了 ESP32-S3 在持续接收 FRDM 数据时,广播包仍能稳定更新。

6. 微信小程序接收逻辑

微信小程序调用微信 BLE API 持续扫描广播。核心处理流程为:

wx.openBluetoothAdapter()
 -> wx.startBluetoothDevicesDiscovery({ allowDuplicatesKey: true })
 -> wx.onBluetoothDeviceFound()
 -> 解析 advertisData / manufacturerData
 -> 校验 company id、TH 标识、协议版本、checksum
 -> 显示温度、湿度、RSSI、序号、最近刷新时间

小程序识别目标设备的方式有两种:

  1. 设备名为 MCXN947_DHT11。

  2. manufacturer data 符合项目自定义协议。

由于温湿度值已经放在广播包中,小程序不需要建立连接,也不需要发现服务或读取特征值。

7. 构建与烧录结果

FRDM-MCXN947

构建命令:

D:\Zephyr\zephyr_env.ps1
west build -p always -d D:\Zephyr\zephyrproject\build\dht11_thermohygrometer -b frdm_mcxn947/mcxn947/cpu0 D:\Zephyr\zephyrproject\apps\frdm_mcxn947_dht11_thermohygrometer

烧录命令:

west flash -d D:\Zephyr\zephyrproject\build\dht11_thermohygrometer

构建产物:

文件大小
zephyr.bin28820 bytes
zephyr.elf770944 bytes

串口验证结果:

Temperature: 24.000000 deg C, Humidity: 53.000000 %RH
Temperature: 24.000000 deg C, Humidity: 54.000000 %RH

说明 DHT11 读取逻辑正常。

ESP32-S3

由于原工程路径包含中文和空格,ESP-IDF 构建时使用 ASCII 临时路径:

C:\codex_build\dht11_ble

构建命令:

D:\Espressif\frameworks\esp-idf-v5.5.4\export.ps1
idf.py -C C:\codex_build\dht11_ble set-target esp32s3
idf.py -C C:\codex_build\dht11_ble build

烧录命令:

idf.py -C C:\codex_build\dht11_ble -p COM11 flash

构建产物:

文件大小
dht11_ble.bin694992 bytes
dht11_ble.elf8220684 bytes
bootloader.bin20832 bytes
partition-table.bin3072 bytes

烧录验证结果:

Chip is ESP32-S3 (QFN56) (revision v0.2)
MAC: e0:72:a1:f4:cc:88
Hash of data verified.
Hard resetting via RTS pin...
Done

联调问题与解决

初次联调时 ESP32-S3 日志出现:

W DHT11_BLE: UART line too long, dropping

同时 FRDM 串口显示 DHT11 正常采集。该现象说明 ESP32-S3 程序运行正常,但 UART1 RX 没有收到合法 TH,...\n 帧,通常是 RX 悬空、接错引脚或未共地。

最终排查结论:

  • ESP32-S3 程序监听 GPIO18。

  • 板子上标注 RX 的底部串口不是本程序使用的 GPIO18。

  • FRDM Arduino D1/TX 必须接到 ESP32-S3 左侧排针的 18。

修正接线后链路工作正常。

16b07e07-dec0-4b40-b92f-9e16c2a66524.png

图中左侧为 FRDM-MCXN947 的 VCOM 日志,可以看到 DHT11 温湿度数据持续输出;右侧为 ESP32-S3 串口日志,可以看到 ESP32-S3 已连续解析 measurement seq=... 数据帧,并重复启动 MCXN947_DHT11 BLE 广播。

8. 关键设计理由

  1. 使用 BLE 广播:小程序只需要实时显示最新温湿度,广播方式无需连接、无需服务发现,状态更简单。

  2. UART 帧使用 ASCII:便于串口调试,可以直接在日志或串口工具中观察 TH,... 数据帧。

  3. BLE manufacturer data 使用固定 12 字节:解析简单,包长可控,适合传统 BLE 广播。

  4. 温湿度使用 0.01 单位整数:避免跨平台浮点编码问题,同时保留两位小数显示能力。

  5. 设备名放入 scan response:避免 manufacturer data 和设备名同时放入 31 字节广播包导致长度超限。

9.成果演示

10. 源代码

源码都放在压缩包中了:源码.zip

下面展示核心代码:

FRDM-MCXN947 Zephyr 主程序:dht11_thermohygrometer/src/main.c

/*
 * FRDM-MCXN947 DHT11 thermohygrometer.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#define DHT_NODE DT_ALIAS(dht0)
#define ESP32_UART_NODE DT_NODELABEL(arduino_serial)
#define SAMPLE_PERIOD K_SECONDS(2)

#if !DT_NODE_EXISTS(DHT_NODE)
#error "No dht0 alias found. Check app.overlay."
#endif

#if !DT_NODE_HAS_STATUS(ESP32_UART_NODE, okay)
#error "arduino_serial is not enabled for ESP32-S3 UART output."
#endif

static const struct device *const dht = DEVICE_DT_GET(DHT_NODE);
static const struct device *const esp32_uart = DEVICE_DT_GET(ESP32_UART_NODE);

static int32_t measurement_value_to_centi(const struct sensor_value *value)
{
    return (value->val1 * 100) + (value->val2 / 10000);
}

static void uart_write_string(const struct device *uart, const char *text)
{
    for (size_t i = 0; text[i] != '\0'; i++) {
        uart_poll_out(uart, text[i]);
    }
}

static void send_measurement_frame(uint32_t sequence,
                   const struct sensor_value *temperature,
                   const struct sensor_value *humidity)
{
    char frame[48];
    int len;

    len = snprintk(frame, sizeof(frame), "TH,%lu,%ld,%ld\r\n",
               (unsigned long)sequence,
               (long)measurement_value_to_centi(temperature),
               (long)measurement_value_to_centi(humidity));
    if (len > 0 && len < sizeof(frame)) {
        uart_write_string(esp32_uart, frame);
    }
}

static void print_sensor_value(const char *label, const char *unit,
                   const struct sensor_value *value)
{
    printk("%s: %d.%06d %s", label, value->val1, value->val2, unit);
}

int main(void)
{
    struct sensor_value temperature;
    struct sensor_value humidity;
    uint32_t sequence = 0;
    int ret;

    printk("FRDM-MCXN947 DHT11 thermohygrometer\r\n");
    printk("DATA: Arduino D2, VCC: 3.3V, GND: GND, pull-up: 4.7 kOhm\r\n");
    printk("ESP32-S3 UART: Arduino D1/TX -> ESP32 RX, GND -> GND, 115200 8N1\r\n");

    if (!device_is_ready(dht)) {
        printk("DHT11 device %s is not ready\r\n", dht->name);
        return 0;
    }

    if (!device_is_ready(esp32_uart)) {
        printk("ESP32 UART device %s is not ready\r\n", esp32_uart->name);
        return 0;
    }

    printk("DHT11 device %s is ready\r\n", dht->name);
    printk("ESP32 UART device %s is ready\r\n", esp32_uart->name);

    while (1) {
        ret = sensor_sample_fetch(dht);
        if (ret < 0) {
            printk("Failed to fetch DHT11 sample: %d\r\n", ret);
            k_sleep(SAMPLE_PERIOD);
            continue;
        }

        ret = sensor_channel_get(dht, SENSOR_CHAN_AMBIENT_TEMP, &temperature);
        if (ret < 0) {
            printk("Failed to read temperature: %d\r\n", ret);
            k_sleep(SAMPLE_PERIOD);
            continue;
        }

        ret = sensor_channel_get(dht, SENSOR_CHAN_HUMIDITY, &humidity);
        if (ret < 0) {
            printk("Failed to read humidity: %d\r\n", ret);
            k_sleep(SAMPLE_PERIOD);
            continue;
        }

        print_sensor_value("Temperature", "deg C", &temperature);
        printk(", ");
        print_sensor_value("Humidity", "%RH", &humidity);
        printk("\r\n");

        send_measurement_frame(sequence++, &temperature, &humidity);

        k_sleep(SAMPLE_PERIOD);
    }

    return 0;
}

FRDM-MCXN947 Devicetree Overlay:dht11_thermohygrometer/app.overlay

#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/gpio/arduino-header-r3.h>

/ {
    aliases {
        dht0 = &dht11_sensor;
    };

    dht11_sensor: dht11 {
        compatible = "aosong,dht";
        status = "okay";
        /* FRDM-MCXN947 Arduino D2 -> gpio0 pin 29. Add a 4.7 kOhm pull-up to 3.3 V. */
        dio-gpios = <&arduino_header ARDUINO_HEADER_R3_D2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
    };
};

ESP32-S3 ESP-IDF 主程序:dht11_ble/main/main.c

/*
 * ESP32-S3 bridge for FRDM-MCXN947 DHT11 measurements.
 *
 * UART input frame from FRDM:
 *   TH,<sequence>,<temperature_centi_c>,<humidity_centi_rh>\r\n
 *
 * BLE manufacturer data:
 *   company id: 0x02E5
 *   bytes 2..3: "TH"
 *   byte 4: protocol version
 *   byte 5: sequence LSB
 *   bytes 6..7: temperature in 0.01 deg C, signed little-endian
 *   bytes 8..9: humidity in 0.01 %RH, unsigned little-endian
 *   byte 10: flags, bit0 = valid measurement
 *   byte 11: additive checksum over bytes 0..10
 */

#include <stdbool.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"

#include "driver/uart.h"
#include "esp_bt.h"
#include "esp_bt_device.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
#include "esp_log.h"
#include "nvs_flash.h"

#define DEVICE_NAME "MCXN947_DHT11"
#define UART_PORT UART_NUM_1
#define UART_BAUD_RATE 115200
#define UART_BUF_SIZE 256
#define ADV_COMPANY_ID 0x02E5
#define ADV_PROTOCOL_VERSION 1
#define ADV_CONFIG_FLAG (1 << 0)
#define SCAN_RSP_CONFIG_FLAG (1 << 1)

#ifndef CONFIG_DHT11_UART_RX_GPIO
#define CONFIG_DHT11_UART_RX_GPIO 18
#endif

#ifndef CONFIG_DHT11_UART_TX_GPIO
#define CONFIG_DHT11_UART_TX_GPIO 17
#endif

static const char *TAG = "DHT11_BLE";

static SemaphoreHandle_t measurement_lock;
static uint8_t manufacturer_data[12] = {
    ADV_COMPANY_ID & 0xff,
    ADV_COMPANY_ID >> 8,
    'T',
    'H',
    ADV_PROTOCOL_VERSION,
};

static int16_t temperature_centi;
static uint16_t humidity_centi;
static uint8_t sequence_lsb;
static bool measurement_valid;
static bool adv_started;
static bool adv_configuring;
static bool adv_update_queued;
static bool adv_config_failed;
static uint8_t adv_config_pending;

static esp_ble_adv_data_t adv_data = {
    .set_scan_rsp = false,
    .include_name = false,
    .include_txpower = false,
    .appearance = 0x00,
    .manufacturer_len = sizeof(manufacturer_data),
    .p_manufacturer_data = manufacturer_data,
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = 0,
    .p_service_uuid = NULL,
    .flag = ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT,
};

static esp_ble_adv_data_t scan_rsp_data = {
    .set_scan_rsp = true,
    .include_name = true,
    .include_txpower = false,
    .appearance = 0x00,
    .manufacturer_len = 0,
    .p_manufacturer_data = NULL,
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = 0,
    .p_service_uuid = NULL,
    .flag = 0,
};

static esp_ble_adv_params_t adv_params = {
    .adv_int_min = 0x00a0,
    .adv_int_max = 0x0140,
    .adv_type = ADV_TYPE_SCAN_IND,
    .own_addr_type = BLE_ADDR_TYPE_PUBLIC,
    .channel_map = ADV_CHNL_ALL,
    .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

static uint8_t checksum(const uint8_t *data, size_t len)
{
    uint8_t sum = 0;

    for (size_t i = 0; i < len; i++) {
        sum = (uint8_t)(sum + data[i]);
    }

    return sum;
}

static void rebuild_manufacturer_data(void)
{
    manufacturer_data[5] = sequence_lsb;
    manufacturer_data[6] = temperature_centi & 0xff;
    manufacturer_data[7] = (temperature_centi >> 8) & 0xff;
    manufacturer_data[8] = humidity_centi & 0xff;
    manufacturer_data[9] = (humidity_centi >> 8) & 0xff;
    manufacturer_data[10] = measurement_valid ? 0x01 : 0x00;
    manufacturer_data[11] = checksum(manufacturer_data, sizeof(manufacturer_data) - 1);
}

static void configure_advertising(void)
{
    esp_err_t err;

    if (adv_configuring) {
        adv_update_queued = true;
        return;
    }

    adv_configuring = true;
    adv_update_queued = false;
    adv_config_failed = false;
    adv_config_pending = 0;

    err = esp_ble_gap_config_adv_data(&adv_data);
    if (err == ESP_OK) {
        adv_config_pending |= ADV_CONFIG_FLAG;
    } else {
        adv_config_failed = true;
        ESP_LOGE(TAG, "config adv data failed: %s", esp_err_to_name(err));
    }

    err = esp_ble_gap_config_adv_data(&scan_rsp_data);
    if (err == ESP_OK) {
        adv_config_pending |= SCAN_RSP_CONFIG_FLAG;
    } else {
        adv_config_failed = true;
        ESP_LOGE(TAG, "config scan response failed: %s", esp_err_to_name(err));
    }

    if (adv_config_pending == 0) {
        adv_configuring = false;
    }
}

static void request_advertising_update(void)
{
    if (xSemaphoreTake(measurement_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
        rebuild_manufacturer_data();
        xSemaphoreGive(measurement_lock);
    }

    if (adv_started) {
        adv_update_queued = true;
        esp_err_t err = esp_ble_gap_stop_advertising();
        if (err == ESP_ERR_INVALID_STATE) {
            adv_started = false;
            configure_advertising();
        } else if (err != ESP_OK) {
            ESP_LOGW(TAG, "stop advertising failed before update: %s", esp_err_to_name(err));
        }
        return;
    }

    configure_advertising();
}

static bool parse_measurement_line(char *line, int32_t *sequence,
                                   int32_t *temperature, int32_t *humidity)
{
    char prefix[3] = {0};
    long parsed_sequence;
    long parsed_temperature;
    long parsed_humidity;

    if (sscanf(line, "%2[^,],%ld,%ld,%ld",
               prefix, &parsed_sequence, &parsed_temperature, &parsed_humidity) != 4 ||
        strcmp(prefix, "TH") != 0 ||
        parsed_temperature < INT16_MIN ||
        parsed_temperature > INT16_MAX ||
        parsed_humidity < 0 ||
        parsed_humidity > UINT16_MAX) {
        return false;
    }

    *sequence = (int32_t)parsed_sequence;
    *temperature = (int32_t)parsed_temperature;
    *humidity = (int32_t)parsed_humidity;
    return true;
}

static void handle_measurement_line(char *line)
{
    int32_t sequence;
    int32_t temperature;
    int32_t humidity;

    if (!parse_measurement_line(line, &sequence, &temperature, &humidity)) {
        ESP_LOGW(TAG, "ignored UART line: %s", line);
        return;
    }

    if (xSemaphoreTake(measurement_lock, pdMS_TO_TICKS(100)) == pdTRUE) {
        sequence_lsb = (uint8_t)sequence;
        temperature_centi = (int16_t)temperature;
        humidity_centi = (uint16_t)humidity;
        measurement_valid = true;
        xSemaphoreGive(measurement_lock);
    }

    ESP_LOGI(TAG, "measurement seq=%" PRId32 " temp=%.2f C humidity=%.2f %%RH",
             sequence, temperature / 100.0, humidity / 100.0);
    request_advertising_update();
}

static void uart_rx_task(void *arg)
{
    uint8_t byte;
    char line[64];
    size_t line_len = 0;

    ESP_LOGI(TAG, "UART%d RX GPIO%d, baud %d",
             UART_PORT, CONFIG_DHT11_UART_RX_GPIO, UART_BAUD_RATE);

    while (true) {
        int len = uart_read_bytes(UART_PORT, &byte, 1, pdMS_TO_TICKS(1000));
        if (len <= 0) {
            continue;
        }

        if (byte == '\n') {
            line[line_len] = '\0';
            if (line_len > 0) {
                handle_measurement_line(line);
            }
            line_len = 0;
            continue;
        }

        if (byte == '\r') {
            continue;
        }

        if (line_len < sizeof(line) - 1) {
            line[line_len++] = (char)byte;
        } else {
            line_len = 0;
            ESP_LOGW(TAG, "UART line too long, dropping");
        }
    }
}

static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    switch (event) {
    case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
        adv_config_pending &= (uint8_t)~ADV_CONFIG_FLAG;
        if (param->adv_data_cmpl.status != ESP_BT_STATUS_SUCCESS) {
            adv_config_failed = true;
            ESP_LOGE(TAG, "advertising data set failed, status=%d", param->adv_data_cmpl.status);
        }
        if (adv_config_pending != 0) {
            break;
        }
        adv_configuring = false;
        if (adv_config_failed) {
            break;
        }
        if (adv_update_queued) {
            configure_advertising();
            break;
        }
        esp_ble_gap_start_advertising(&adv_params);
        break;

    case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
        adv_config_pending &= (uint8_t)~SCAN_RSP_CONFIG_FLAG;
        if (param->scan_rsp_data_cmpl.status != ESP_BT_STATUS_SUCCESS) {
            adv_config_failed = true;
            ESP_LOGE(TAG, "scan response set failed, status=%d", param->scan_rsp_data_cmpl.status);
        }
        if (adv_config_pending != 0) {
            break;
        }
        adv_configuring = false;
        if (adv_config_failed) {
            break;
        }
        if (adv_update_queued) {
            configure_advertising();
            break;
        }
        esp_ble_gap_start_advertising(&adv_params);
        break;

    case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
        if (param->adv_start_cmpl.status == ESP_BT_STATUS_SUCCESS) {
            adv_started = true;
            ESP_LOGI(TAG, "BLE advertising as %s", DEVICE_NAME);
        } else {
            adv_started = false;
            ESP_LOGE(TAG, "advertising start failed, status=%d", param->adv_start_cmpl.status);
        }
        break;

    case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
        adv_started = false;
        if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
            ESP_LOGW(TAG, "advertising stop status=%d", param->adv_stop_cmpl.status);
        }
        if (adv_update_queued) {
            configure_advertising();
        }
        break;

    default:
        break;
    }
}

static void init_uart(void)
{
    const uart_config_t uart_config = {
        .baud_rate = UART_BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };

    ESP_ERROR_CHECK(uart_driver_install(UART_PORT, UART_BUF_SIZE, 0, 0, NULL, 0));
    ESP_ERROR_CHECK(uart_param_config(UART_PORT, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(UART_PORT,
                                 CONFIG_DHT11_UART_TX_GPIO,
                                 CONFIG_DHT11_UART_RX_GPIO,
                                 UART_PIN_NO_CHANGE,
                                 UART_PIN_NO_CHANGE));
}

static void init_ble(void)
{
    esp_err_t ret;

    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ret = esp_bt_controller_init(&bt_cfg);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "initialize controller failed: %s", esp_err_to_name(ret));
        return;
    }

    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "enable controller failed: %s", esp_err_to_name(ret));
        return;
    }

    ret = esp_bluedroid_init();
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "init bluedroid failed: %s", esp_err_to_name(ret));
        return;
    }

    ret = esp_bluedroid_enable();
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "enable bluedroid failed: %s", esp_err_to_name(ret));
        return;
    }

    ESP_ERROR_CHECK(esp_ble_gap_register_callback(gap_event_handler));
    ESP_ERROR_CHECK(esp_ble_gap_set_device_name(DEVICE_NAME));
    request_advertising_update();
}

void app_main(void)
{
    esp_err_t ret = nvs_flash_init();

    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    measurement_lock = xSemaphoreCreateMutex();
    if (measurement_lock == NULL) {
        ESP_LOGE(TAG, "failed to create measurement mutex");
        return;
    }

    init_uart();
    init_ble();

    xTaskCreate(uart_rx_task, "uart_rx", 4096, NULL, 10, NULL);
}

ESP32-S3 Kconfig:dht11_ble/main/Kconfig.projbuild

menu "DHT11 BLE bridge"

    config DHT11_UART_RX_GPIO
        int "ESP32-S3 UART RX GPIO"
        default 18
        help
            Connect FRDM-MCXN947 Arduino D1/TX to this ESP32-S3 GPIO.

    config DHT11_UART_TX_GPIO
        int "ESP32-S3 UART TX GPIO"
        default 17
        help
            UART TX pin. It is configured for completeness; the bridge only
            requires RX plus a common GND.

endmenu



共1条 1/1 1 跳转至

回复

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