添加 SU-03T 语音命令解析与分发功能,更新主流程以支持语音命令处理
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
idf_component_register(SRCS "su-03t.c"
|
||||
idf_component_register(SRCS "su-03t.c" "su-03t_voice.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES driver)
|
||||
REQUIRES driver log)
|
||||
|
||||
56
components/su-03t/include/su-03t_voice.h
Normal file
56
components/su-03t/include/su-03t_voice.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 文件: components/su-03t/include/su-03t_voice.h
|
||||
* 角色: SU-03T 语音命令解析与分发
|
||||
* 说明:
|
||||
* - 这里维护语音消息号到业务命令的映射。
|
||||
* - 主接收回调只负责拿到帧,这里负责做语义判断和后续分发。
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "su-03t.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SU03T_VOICE_CMD_ASK_TEMP = 0x00,
|
||||
SU03T_VOICE_CMD_ASK_HUMI = 0x01,
|
||||
SU03T_VOICE_CMD_ASK_LUX = 0x02,
|
||||
SU03T_VOICE_CMD_ASK_MQ2 = 0x03,
|
||||
SU03T_VOICE_CMD_ASK_CO2 = 0x04,
|
||||
SU03T_VOICE_CMD_ASK_WEIGHT = 0x05,
|
||||
SU03T_VOICE_CMD_ASK_HUM = 0x06,
|
||||
SU03T_VOICE_CMD_ASK_DOOR = 0x07,
|
||||
SU03T_VOICE_CMD_ASK_FAN = 0x08,
|
||||
SU03T_VOICE_CMD_ASK_LIGHT = 0x09,
|
||||
SU03T_VOICE_CMD_ASK_COOL = 0x0A,
|
||||
SU03T_VOICE_CMD_ASK_HOT = 0x0B,
|
||||
SU03T_VOICE_CMD_ASK_CONTROL = 0x0C,
|
||||
SU03T_VOICE_CMD_ASK_RICE = 0x0D,
|
||||
SU03T_VOICE_CMD_UNKNOWN = 0xFF,
|
||||
} su03t_voice_cmd_id_t;
|
||||
|
||||
typedef struct {
|
||||
su03t_voice_cmd_id_t id;
|
||||
uint8_t msgno;
|
||||
const char *key;
|
||||
const char *label;
|
||||
} su03t_voice_command_t;
|
||||
|
||||
const su03t_voice_command_t *su03t_voice_command_from_msgno(uint8_t msgno);
|
||||
const char *su03t_voice_command_key(uint8_t msgno);
|
||||
void su03t_voice_handle_frame(const su03t_frame_t *frame);
|
||||
|
||||
/* 以 8 字节 little-endian double 作为参数区发送一帧。 */
|
||||
esp_err_t su03t_voice_send_double_reply(uint8_t msgno, double value);
|
||||
|
||||
/* 从接收帧参数区解析 little-endian double(要求 params_len == 8)。 */
|
||||
bool su03t_voice_parse_double_payload(const su03t_frame_t *frame, double *out_value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
108
components/su-03t/su-03t_voice.c
Normal file
108
components/su-03t/su-03t_voice.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 文件: components/su-03t/su-03t_voice.c
|
||||
* 角色: SU-03T 语音命令解析与分发
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "su-03t_voice.h"
|
||||
|
||||
static const char *TAG = "su03t_voice";
|
||||
|
||||
const su03t_voice_command_t *su03t_voice_command_from_msgno(uint8_t msgno)
|
||||
{
|
||||
static const su03t_voice_command_t k_commands[] = {
|
||||
{SU03T_VOICE_CMD_ASK_TEMP, 0x00, "ask_temp", "温度是多少"},
|
||||
{SU03T_VOICE_CMD_ASK_HUMI, 0x01, "ask_humi", "湿度是多少"},
|
||||
{SU03T_VOICE_CMD_ASK_LUX, 0x02, "ask_lux", "光照强度是多少"},
|
||||
{SU03T_VOICE_CMD_ASK_MQ2, 0x03, "ask_mq2", "烟雾浓度是多少"},
|
||||
{SU03T_VOICE_CMD_ASK_CO2, 0x04, "ask_co2", "二氧化碳浓度是多少"},
|
||||
{SU03T_VOICE_CMD_ASK_WEIGHT, 0x05, "ask_weight", "粮食重量是多少"},
|
||||
{SU03T_VOICE_CMD_ASK_HUM, 0x06, "ask_hum", "有人在吗"},
|
||||
{SU03T_VOICE_CMD_ASK_DOOR, 0x07, "ask_door", "门磁状态"},
|
||||
{SU03T_VOICE_CMD_ASK_FAN, 0x08, "ask_fan", "风扇继电器状态"},
|
||||
{SU03T_VOICE_CMD_ASK_LIGHT, 0x09, "ask_light", "照明继电器状态"},
|
||||
{SU03T_VOICE_CMD_ASK_COOL, 0x0A, "ask_cool", "制冷继电器状态"},
|
||||
{SU03T_VOICE_CMD_ASK_HOT, 0x0B, "ask_hot", "制热继电器状态"},
|
||||
{SU03T_VOICE_CMD_ASK_CONTROL, 0x0C, "ask_control", "当前控制模式"},
|
||||
{SU03T_VOICE_CMD_ASK_RICE, 0x0D, "ask_rice", "粮食状态"},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(k_commands) / sizeof(k_commands[0]); ++i)
|
||||
{
|
||||
if (k_commands[i].msgno == msgno)
|
||||
{
|
||||
return &k_commands[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *su03t_voice_command_key(uint8_t msgno)
|
||||
{
|
||||
const su03t_voice_command_t *cmd = su03t_voice_command_from_msgno(msgno);
|
||||
return cmd != NULL ? cmd->key : NULL;
|
||||
}
|
||||
|
||||
void su03t_voice_handle_frame(const su03t_frame_t *frame)
|
||||
{
|
||||
if (frame == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const su03t_voice_command_t *cmd = su03t_voice_command_from_msgno(frame->msgno);
|
||||
if (cmd == NULL)
|
||||
{
|
||||
ESP_LOGW(TAG, "unknown voice command: msgno=0x%02X", frame->msgno);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd->id)
|
||||
{
|
||||
case SU03T_VOICE_CMD_ASK_TEMP:
|
||||
case SU03T_VOICE_CMD_ASK_HUMI:
|
||||
case SU03T_VOICE_CMD_ASK_LUX:
|
||||
case SU03T_VOICE_CMD_ASK_MQ2:
|
||||
case SU03T_VOICE_CMD_ASK_CO2:
|
||||
case SU03T_VOICE_CMD_ASK_WEIGHT:
|
||||
case SU03T_VOICE_CMD_ASK_HUM:
|
||||
case SU03T_VOICE_CMD_ASK_DOOR:
|
||||
case SU03T_VOICE_CMD_ASK_FAN:
|
||||
case SU03T_VOICE_CMD_ASK_LIGHT:
|
||||
case SU03T_VOICE_CMD_ASK_COOL:
|
||||
case SU03T_VOICE_CMD_ASK_HOT:
|
||||
case SU03T_VOICE_CMD_ASK_CONTROL:
|
||||
case SU03T_VOICE_CMD_ASK_RICE:
|
||||
ESP_LOGI(TAG, "voice command matched: %s (%s)", cmd->key, cmd->label);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "voice command id not handled: msgno=0x%02X", frame->msgno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t su03t_voice_send_double_reply(uint8_t msgno, double value)
|
||||
{
|
||||
uint8_t payload[sizeof(double)];
|
||||
memcpy(payload, &value, sizeof(payload));
|
||||
return su03t_send_frame(msgno, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
bool su03t_voice_parse_double_payload(const su03t_frame_t *frame, double *out_value)
|
||||
{
|
||||
if (frame == NULL || out_value == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frame->params_len != sizeof(double))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(out_value, frame->params, sizeof(double));
|
||||
return true;
|
||||
}
|
||||
102
main/main.cpp
102
main/main.cpp
@@ -38,6 +38,7 @@
|
||||
#include "fire_sensor.h"
|
||||
#include "hx711.hpp"
|
||||
#include "su-03t.h"
|
||||
#include "su-03t_voice.h"
|
||||
#include "agri_env.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
@@ -406,6 +407,18 @@ static void sntp_task(void *arg)
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void su03t_reply_double_value(uint8_t reply_msgno, double value, const char *label)
|
||||
{
|
||||
esp_err_t ret = su03t_voice_send_double_reply(reply_msgno, value);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "SU03T TX %s failed: %s", label, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "SU03T TX %s msgno=0x%02X value=%.3f", label, reply_msgno, value);
|
||||
}
|
||||
|
||||
/* 函数: su03t_rx_callback
|
||||
* 作用: 执行模块内与函数名对应的业务逻辑。
|
||||
* 重点: 关注输入合法性、返回码与并发安全。
|
||||
@@ -418,26 +431,79 @@ static void su03t_rx_callback(const su03t_frame_t *frame, void *user_ctx)
|
||||
return;
|
||||
}
|
||||
|
||||
char hex_buf[256];
|
||||
size_t pos = 0;
|
||||
for (size_t i = 0; i < frame->params_len && pos + 4 < sizeof(hex_buf); ++i)
|
||||
const su03t_voice_command_t *cmd = su03t_voice_command_from_msgno(frame->msgno);
|
||||
if (cmd != NULL)
|
||||
{
|
||||
int n = snprintf(&hex_buf[pos], sizeof(hex_buf) - pos, "%02X ", frame->params[i]);
|
||||
if (n <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pos += (size_t)n;
|
||||
}
|
||||
if (pos == 0)
|
||||
{
|
||||
snprintf(hex_buf, sizeof(hex_buf), "(no params)");
|
||||
}
|
||||
ESP_LOGI(TAG, "SU03T RX msgno=0x%02X cmd=%s len=%u",
|
||||
frame->msgno,
|
||||
cmd->key,
|
||||
(unsigned)frame->params_len);
|
||||
|
||||
ESP_LOGI(TAG, "SU03T RX msgno=0x%02X len=%u params=%s",
|
||||
frame->msgno,
|
||||
(unsigned)frame->params_len,
|
||||
hex_buf);
|
||||
double reply_value = 0.0;
|
||||
uint8_t reply_msgno = 0;
|
||||
const char *reply_label = NULL;
|
||||
bool need_reply = false;
|
||||
|
||||
if (s_env_data_lock)
|
||||
{
|
||||
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
|
||||
switch (cmd->id)
|
||||
{
|
||||
case SU03T_VOICE_CMD_ASK_TEMP:
|
||||
reply_msgno = 0x01;
|
||||
reply_value = (double)s_env_data.temp;
|
||||
reply_label = "temp";
|
||||
need_reply = true;
|
||||
break;
|
||||
case SU03T_VOICE_CMD_ASK_HUMI:
|
||||
reply_msgno = 0x02;
|
||||
reply_value = (double)s_env_data.humidity;
|
||||
reply_label = "humidity";
|
||||
need_reply = true;
|
||||
break;
|
||||
case SU03T_VOICE_CMD_ASK_LUX:
|
||||
reply_msgno = 0x03;
|
||||
reply_value = (double)s_env_data.lux;
|
||||
reply_label = "lux";
|
||||
need_reply = true;
|
||||
break;
|
||||
case SU03T_VOICE_CMD_ASK_MQ2:
|
||||
reply_msgno = 0x04;
|
||||
reply_value = (double)s_env_data.gas_percent;
|
||||
reply_label = "air_quality";
|
||||
need_reply = true;
|
||||
break;
|
||||
case SU03T_VOICE_CMD_ASK_CO2:
|
||||
reply_msgno = 0x05;
|
||||
reply_value = (double)s_env_data.co2;
|
||||
reply_label = "co2";
|
||||
need_reply = true;
|
||||
break;
|
||||
case SU03T_VOICE_CMD_ASK_WEIGHT:
|
||||
reply_msgno = 0x06;
|
||||
reply_value = (double)s_env_data.ice_weight;
|
||||
reply_label = "weight";
|
||||
need_reply = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
xSemaphoreGive(s_env_data_lock);
|
||||
}
|
||||
|
||||
if (need_reply)
|
||||
{
|
||||
su03t_reply_double_value(reply_msgno, reply_value, reply_label);
|
||||
}
|
||||
|
||||
su03t_voice_handle_frame(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "SU03T RX msgno=0x%02X cmd=unknown len=%u",
|
||||
frame->msgno,
|
||||
(unsigned)frame->params_len);
|
||||
}
|
||||
|
||||
if (s_env_data_lock)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user