1. 项目概述
本项目实现了一条从 DHT11 温湿度传感器到微信小程序显示界面的低功耗蓝牙广播链路。系统由三部分组成:
FRDM-MCXN947:运行 Zephyr 程序,负责读取 DHT11 温湿度数据,并通过 UART 输出标准化 ASCII 数据帧。
ESP32-S3:运行 ESP-IDF 程序,作为 UART 到 BLE 广播的桥接节点,接收 FRDM 的温湿度数据并写入 BLE manufacturer data。
微信小程序:持续扫描 BLE 广播,解析 ESP32-S3 广播中的温湿度数据并显示。
最终数据链路为:
DHT11 -> FRDM-MCXN947 -> UART -> ESP32-S3 -> BLE Advertising -> 微信小程序
本设计使用无连接 BLE 广播。这样可以减少连接状态管理,微信小程序只需要扫描广播即可得到最新温湿度数据。
2. 硬件连接
DHT11 与 FRDM-MCXN947
| VCC | 3.3V |
| GND | GND |
| DATA | Arduino 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
| Arduino D1 / TX | GPIO18 |
| GND | GND |
注意事项:
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。

图中展示了 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 |
| sequence | FRDM 端递增序号 |
| 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 字节:
| 0..1 | Company ID | 小端,默认 0x02E5 |
| 2..3 | ASCII TH | 协议标识 |
| 4 | Protocol version | 当前为 1 |
| 5 | Sequence LSB | FRDM 序号低 8 位 |
| 6..7 | Temperature | int16 little-endian,单位 0.01 deg C |
| 8..9 | Humidity | uint16 little-endian,单位 0.01 %RH |
| 10 | Flags | bit0 = 1 表示测量值有效 |
| 11 | Checksum | 前 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 秒执行一次:
sensor_sample_fetch(dht) 触发 DHT11 采样。
sensor_channel_get(..., SENSOR_CHAN_AMBIENT_TEMP, ...) 读取温度。
sensor_channel_get(..., SENSOR_CHAN_HUMIDITY, ...) 读取湿度。
通过调试串口打印温湿度。
通过 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 程序负责两个任务:
UART 接收任务:从 GPIO18 接收 FRDM 的 TH,... 数据帧。
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_pending | adv 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、序号、最近刷新时间小程序识别目标设备的方式有两种:
设备名为 MCXN947_DHT11。
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.bin | 28820 bytes |
| zephyr.elf | 770944 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.bin | 694992 bytes |
| dht11_ble.elf | 8220684 bytes |
| bootloader.bin | 20832 bytes |
| partition-table.bin | 3072 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。
修正接线后链路工作正常。

图中左侧为 FRDM-MCXN947 的 VCOM 日志,可以看到 DHT11 温湿度数据持续输出;右侧为 ESP32-S3 串口日志,可以看到 ESP32-S3 已连续解析 measurement seq=... 数据帧,并重复启动 MCXN947_DHT11 BLE 广播。
8. 关键设计理由
使用 BLE 广播:小程序只需要实时显示最新温湿度,广播方式无需连接、无需服务发现,状态更简单。
UART 帧使用 ASCII:便于串口调试,可以直接在日志或串口工具中观察 TH,... 数据帧。
BLE manufacturer data 使用固定 12 字节:解析简单,包长可控,适合传统 BLE 广播。
温湿度使用 0.01 单位整数:避免跨平台浮点编码问题,同时保留两位小数显示能力。
设备名放入 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
我要赚赏金
