共计 3376 个字符,预计需要花费 9 分钟才能阅读完成。
背景痛点
在工业现场和智能家居场景中,传统的温湿度显示方案通常需要人工查看屏幕或显示屏,这种方式存在几个明显的局限性:

- 非直观:需要用户主动查看,无法实现被动信息推送。
- 依赖视觉交互:在光线不足或视线受阻的环境中(如设备安装在角落或高处),查看数据变得困难。
- 实时性差:用户需要手动操作才能获取最新数据,无法实现即时反馈。
语音播报技术可以很好地解决这些问题,通过语音交互实现信息的主动推送和被动接收,大大提升了用户体验。
技术选型
在选择语音识别模块时,我们对比了几种常见的方案:
- ASRPRO:
- 优点:离线识别率高(实测可达 95% 以上),支持唤醒词定制,开发成本低。
- 缺点:对环境噪声较为敏感。
- LD3320:
- 优点:对噪声有一定的抗干扰能力。
- 缺点:识别率较低(实测约 85%),唤醒词定制复杂,开发成本较高。
考虑到工业现场和智能家居场景对识别率和成本的要求,我们最终选择了 ASRPRO 作为语音识别模块。
硬件架构
STM32 与 ASRPRO 的接口设计
ASRPRO 通过 UART 接口与 STM32 通信,以下是连接示意图:
ASRPRO TX ----> STM32 RX (PA10)
ASRPRO RX ----> STM32 TX (PA9)
ASRPRO GND ---> STM32 GND
DHT11 温湿度传感器设计
DHT11 通过单总线协议与 STM32 通信,以下是信号采集电路设计:
DHT11 DATA ----> STM32 PA0 (上拉电阻 4.7KΩ)
DHT11 VCC ----> 3.3V
DHT11 GND ----> GND
核心代码
STM32 端代码(HAL 库实现)
以下是 DHT11 数据采集的代码片段,包含 CRC 校验:
// DHT11 初始化
void DHT11_Init(void) {GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
}
// 读取 DHT11 数据
uint8_t DHT11_ReadData(uint8_t *temperature, uint8_t *humidity) {uint8_t data[5] = {0};
uint8_t i, j;
// 发送开始信号
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_Delay(18);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
delay_us(20);
// 等待 DHT11 响应
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET);
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET);
// 读取数据
for (i = 0; i < 5; i++) {for (j = 0; j < 8; j++) {while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
delay_us(30);
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) {data[i] |= (1 << (7 - j));
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET);
}
}
}
// CRC 校验
if (data[4] == (data[0] + data[1] + data[2] + data[3])) {*humidity = data[0];
*temperature = data[2];
return 1;
}
return 0;
}
ASRPRO 端配置
ASRPRO 通过语音指令树配置实现语音识别,以下是示例配置:
{
"commands": [
{
"name": "query_temperature",
"keywords": ["当前温度", "温度多少"],
"response": "当前温度为 ${temperature}摄氏度"
},
{
"name": "query_humidity",
"keywords": ["当前湿度", "湿度多少"],
"response": "当前湿度为 ${humidity}%"
}
],
"settings": {
"wakeup_word": "小助手",
"noise_filter": "medium",
"response_delay": 100
}
}
性能优化
波特率测试
我们在不同波特率下测试了语音指令的识别成功率,结果如下:
- 9600bps:识别延迟≤300ms,成功率 90%
- 115200bps:识别延迟≤200ms,成功率 95%
- 230400bps:识别延迟≤150ms,成功率 93%
综合考虑延迟和稳定性,我们最终选择了 115200bps 作为通信波特率。
RTOS 任务堆栈大小
在 FreeRTOS 环境下,我们测试了不同堆栈大小对系统稳定性的影响:
- 512 字节:偶尔出现栈溢出,系统不稳定
- 1024 字节:系统运行稳定,无明显问题
- 2048 字节:系统运行稳定,但资源占用较高
因此,我们建议将任务堆栈大小设置为 1024 字节。
避坑指南
UART 数据包粘包问题
为了解决 UART 数据包粘包问题,我们实现了一个环形缓冲区:
#define BUFFER_SIZE 256
typedef struct {uint8_t buffer[BUFFER_SIZE];
uint16_t head;
uint16_t tail;
} CircularBuffer;
void CircularBuffer_Init(CircularBuffer *cb) {
cb->head = 0;
cb->tail = 0;
}
uint8_t CircularBuffer_Write(CircularBuffer *cb, uint8_t data) {uint16_t next = (cb->head + 1) % BUFFER_SIZE;
if (next == cb->tail) {return 0; // 缓冲区已满}
cb->buffer[cb->head] = data;
cb->head = next;
return 1;
}
uint8_t CircularBuffer_Read(CircularBuffer *cb, uint8_t *data) {if (cb->head == cb->tail) {return 0; // 缓冲区为空}
*data = cb->buffer[cb->tail];
cb->tail = (cb->tail + 1) % BUFFER_SIZE;
return 1;
}
环境噪声过滤
ASRPRO 对环境噪声较为敏感,我们通过以下方法减少误触发:
- 在 ASRPRO 配置中启用噪声过滤(设置为
medium或high)。 - 在 STM32 端实现简单的软件滤波,如对连续几次采集的数据取平均值。
- 在硬件设计上,尽量将 ASRPRO 远离噪声源(如电机、风扇等)。
扩展思考
基于当前系统,可以进一步扩展以下功能:
- MQTT 上传:将温湿度数据通过 Wi-Fi 模块上传到云平台,实现远程监控。
- 多语言播报:在 ASRPRO 中配置多种语言的语音指令和响应,实现国际化支持。
- 数据存储:在 STM32 上添加 SD 卡模块,实现温湿度数据的本地存储和查询。
结语
通过本次项目,我们成功实现了基于 ASRPRO 语音识别与 STM32 的温湿度播报系统。该系统具有低延迟、高可靠性的特点,适用于工业现场和智能家居场景。希望本文能为嵌入式开发者在语音交互与传感器数据融合方面提供一些参考和启发。
正文完
