Files
Wang Beihong ffdb7065e3 功能:集成SU-03T语音模块,完善UI代码文档
- 在CMakeLists.txt中添加SU-03T语音模块依赖。
- 在main.cpp中实现SU-03T接收回调函数,处理接收消息。
- 完善各UI源文件文档,包括动作、屏幕和字体,明确模块作用与数据流向。
- 更新主应用逻辑,初始化并启动SU-03T接收器。
- 修改过程中确保兼容性,保留原有接口。
2026-04-22 01:06:10 +08:00

218 lines
5.5 KiB
C

/*
* 文件: components/JW01/JW01.c
* 角色: JW01 气体传感器串口解析与数据提取
* 说明:
* - 本文件用于实现当前模块的核心功能或接口定义。
* - 修改前请先确认该模块与其它任务/外设之间的数据流关系。
* - 涉及协议与硬件时,优先保持现有接口兼容,避免联调回归。
*/
#include "JW01.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "driver/uart.h"
#include "esp_check.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
static const char *TAG = "JW01";
static bool s_inited = false;
static uint32_t s_parse_fail_count = 0;
/* 函数: jw01_parse_binary_frames
* 作用: 执行模块内与函数名对应的业务逻辑。
* 重点: 关注输入合法性、返回码与并发安全。
*/
static esp_err_t jw01_parse_binary_frames(const uint8_t *buf, int len, jw01_data_t *out)
{
bool found = false;
float last_co2 = 0.0f;
for (int i = 0; i + 5 < len; ++i) {
if (buf[i] != 0x2C) {
continue;
}
uint8_t cs = (uint8_t)(buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3] + buf[i + 4]);
if (cs != buf[i + 5]) {
continue;
}
uint16_t value = ((uint16_t)buf[i + 1] << 8) | buf[i + 2];
last_co2 = (float)value;
found = true;
i += 5;
}
if (!found) {
return ESP_ERR_INVALID_RESPONSE;
}
out->co2 = last_co2;
out->co2_valid = true;
return ESP_OK;
}
/* 函数: str_to_upper_inplace
* 作用: 执行模块内与函数名对应的业务逻辑。
* 重点: 关注输入合法性、返回码与并发安全。
*/
static void str_to_upper_inplace(char *s)
{
while (*s != '\0') {
*s = (char)toupper((unsigned char)*s);
++s;
}
}
/* 函数: extract_float_by_key
* 作用: 执行模块内与函数名对应的业务逻辑。
* 重点: 关注输入合法性、返回码与并发安全。
*/
static bool extract_float_by_key(const char *line, const char *key, float *out)
{
char up_line[160];
char up_key[24];
strncpy(up_line, line, sizeof(up_line) - 1);
up_line[sizeof(up_line) - 1] = '\0';
strncpy(up_key, key, sizeof(up_key) - 1);
up_key[sizeof(up_key) - 1] = '\0';
str_to_upper_inplace(up_line);
str_to_upper_inplace(up_key);
char *pos = strstr(up_line, up_key);
if (pos == NULL) {
return false;
}
while (*pos != '\0' && *pos != ':' && *pos != '=') {
++pos;
}
if (*pos == '\0') {
return false;
}
++pos;
while (*pos != '\0' && (isspace((unsigned char)*pos) || *pos == ',')) {
++pos;
}
char *endptr = NULL;
float value = strtof(pos, &endptr);
if (endptr == pos) {
return false;
}
*out = value;
return true;
}
/* 函数: jw01_parse_line
* 作用: 执行模块内与函数名对应的业务逻辑。
* 重点: 关注输入合法性、返回码与并发安全。
*/
static esp_err_t jw01_parse_line(const char *line, jw01_data_t *out)
{
jw01_data_t data = {0};
data.tvoc_valid = extract_float_by_key(line, "TVOC", &data.tvoc);
if (!data.tvoc_valid) {
data.tvoc_valid = extract_float_by_key(line, "VOC", &data.tvoc);
}
data.hcho_valid = extract_float_by_key(line, "HCHO", &data.hcho);
if (!data.hcho_valid) {
data.hcho_valid = extract_float_by_key(line, "CH2O", &data.hcho);
}
data.co2_valid = extract_float_by_key(line, "CO2", &data.co2);
if (!data.tvoc_valid && !data.hcho_valid && !data.co2_valid) {
return ESP_ERR_INVALID_RESPONSE;
}
*out = data;
return ESP_OK;
}
/* 函数: jw01_init
* 作用: 执行模块内与函数名对应的业务逻辑。
* 重点: 关注输入合法性、返回码与并发安全。
*/
esp_err_t jw01_init(void)
{
if (s_inited) {
return ESP_OK;
}
const uart_config_t uart_cfg = {
.baud_rate = JW01_UART_BAUDRATE,
.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_RETURN_ON_ERROR(uart_driver_install((uart_port_t)JW01_UART_PORT, 1024, 0, 0, NULL, 0),
TAG, "uart driver install failed");
ESP_RETURN_ON_ERROR(uart_param_config((uart_port_t)JW01_UART_PORT, &uart_cfg),
TAG, "uart param config failed");
ESP_RETURN_ON_ERROR(uart_set_pin((uart_port_t)JW01_UART_PORT,
JW01_UART_TX_GPIO,
JW01_UART_RX_GPIO,
UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE),
TAG, "uart set pin failed");
ESP_RETURN_ON_ERROR(uart_flush_input((uart_port_t)JW01_UART_PORT), TAG, "uart flush failed");
s_inited = true;
ESP_LOGI(TAG, "JW01 init done on UART%d TX=%d RX=%d baud=%d",
JW01_UART_PORT, JW01_UART_TX_GPIO, JW01_UART_RX_GPIO, JW01_UART_BAUDRATE);
return ESP_OK;
}
/* 函数: jw01_read
* 作用: 执行模块内与函数名对应的业务逻辑。
* 重点: 关注输入合法性、返回码与并发安全。
*/
esp_err_t jw01_read(jw01_data_t *out_data, int timeout_ms)
{
ESP_RETURN_ON_FALSE(out_data != NULL, ESP_ERR_INVALID_ARG, TAG, "out_data is null");
ESP_RETURN_ON_FALSE(s_inited, ESP_ERR_INVALID_STATE, TAG, "jw01 not init");
char line[160] = {0};
int n = uart_read_bytes((uart_port_t)JW01_UART_PORT,
(uint8_t *)line,
sizeof(line) - 1,
pdMS_TO_TICKS(timeout_ms));
if (n <= 0) {
return ESP_ERR_TIMEOUT;
}
line[n] = '\0';
jw01_data_t data = {0};
esp_err_t ret = jw01_parse_binary_frames((const uint8_t *)line, n, &data);
if (ret != ESP_OK) {
ret = jw01_parse_line(line, &data);
}
if (ret == ESP_OK) {
*out_data = data;
}
if (ret != ESP_OK) {
s_parse_fail_count++;
if (s_parse_fail_count <= 5 || (s_parse_fail_count % 20) == 0) {
ESP_LOGW(TAG, "parse failed (%s), rx_len=%d", esp_err_to_name(ret), n);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, (const uint8_t *)line, n, ESP_LOG_WARN);
}
}
return ret;
}