1. 进一步拓展了语音识别,目前可以从sd卡导入模型,避免了model文件占用flash分区大小

2. 进一步修改了语音识别,关闭了关键词唤醒功能,只保留了指令识别功能
3. 构建了业务层的基本框架(增加了底层驱动对于的C++兼容),业务代码采用C++编写,启用了RTTI(运行时类型识别)
This commit is contained in:
Misaki
2025-09-03 00:19:14 +08:00
parent ce0998c1c6
commit 5d79f88918
42 changed files with 822 additions and 100 deletions
+4
View File
@@ -0,0 +1,4 @@
//
// Created by misaki on 2025/9/2.
//
#include "CommClass.h"
+17
View File
@@ -0,0 +1,17 @@
//
// Created by misaki on 2025/9/2.
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
+10
View File
@@ -0,0 +1,10 @@
//
// Created by misaki on 2025/9/2.
//
#include "CppHandle.h"
#include "OTAClass.h"
void Cpp_Hand() {
OTAClass::Init();
}
+17
View File
@@ -0,0 +1,17 @@
//
// Created by misaki on 2025/9/2.
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void Cpp_Hand();
#ifdef __cplusplus
}
#endif
+9
View File
@@ -0,0 +1,9 @@
//
// Created by misaki on 2025/9/2.
//
#include "OTAClass.h"
#include "esp_log.h"
void OTAClass::Init() {
ESP_LOGI("OTA", "Init");
}
+21
View File
@@ -0,0 +1,21 @@
//
// Created by misaki on 2025/9/2.
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
class OTAClass {
public:
static void Init(void);
void Update(void);
};
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,4 @@
//
// Created by misaki on 2025/9/2.
//
#include "PetBaseClass.h"
+16
View File
@@ -0,0 +1,16 @@
//
// Created by misaki on 2025/9/2.
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
+5
View File
@@ -0,0 +1,5 @@
//
// Created by misaki on 2025/9/2.
//
#include "ToolsClass.h"
+16
View File
@@ -0,0 +1,16 @@
//
// Created by misaki on 2025/9/2.
//
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
Binary file not shown.
+56
View File
@@ -0,0 +1,56 @@
from pypinyin import pinyin, Style
import argparse
import re
def chinese_to_pinyin(text):
# Use regular expression to extract Chinese characters
chinese_text = ''.join(re.findall(r'[\u4e00-\u9fa5]', text))
out = "static const sr_cmd_t sr_commands[] = {\n"
enum = "enum {\n"
cmd_id = 0
text_list = chinese_text.split(";")
for item in text_list:
item = item.strip() # Remove leading and trailing spaces
if not item:
continue
item = item.split(",")
phrase_id = 0
pinyin_list_all = []
for phrase in item:
phrase = phrase.strip() # Remove leading and trailing spaces
if not phrase:
continue
# Convert Chinese phrase to pinyin with spaces between each syllable
pinyin_list = pinyin(phrase, style=Style.NORMAL, heteronym=False)
# Join pinyin list with spaces
phoneme = ' '.join([py[0] for py in pinyin_list])
out += f' {{ {cmd_id}, "{phoneme}", "{phoneme}" }},\n'
if phrase_id == 0:
enum += f' SR_CMD_{phoneme.upper().replace(" ", "_")},\n'
phrase_id += 1
cmd_id += 1
out += "};\n"
enum += "};\n"
print(enum)
print(out)
return out
if __name__ == "__main__":
parser = argparse.ArgumentParser(prog="Chinese Speech Commands to Pinyin")
parser.add_argument("text", type=str, default=None, help="input text")
args = parser.parse_args()
if args.text is not None:
chinese_to_pinyin(args.text)
+9 -1
View File
@@ -1,5 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_log.h"
#include "esp_check.h"
#include "unity.h"
@@ -51,4 +55,8 @@ void Music_pause(void);
uint32_t Music_Duration(void);
uint32_t Music_Elapsed(void);
uint16_t Music_Energy(void);
void Volume_adjustment(uint8_t Volume);
void Volume_adjustment(uint8_t Volume);
#ifdef __cplusplus
}
#endif
+9 -1
View File
@@ -1,5 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_log.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
@@ -18,4 +22,8 @@
extern float BAT_analogVolts;
void BAT_Init(void);
float BAT_Get_Volts(void);
float BAT_Get_Volts(void);
#ifdef __cplusplus
}
#endif
+10
View File
@@ -1,4 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
#include "esp_log.h"
#include <stdio.h>
@@ -68,3 +73,8 @@ void LCD_addWindow(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yen
void Backlight_Init(void); // Initialize the LCD backlight, which has been called in the LCD_Init function, ignore it
void Set_Backlight(uint8_t Light); // Call this function to adjust the brightness of the backlight. The value of the parameter Light ranges from 0 to 100
#ifdef __cplusplus
}
#endif
+11 -1
View File
@@ -1,4 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@@ -22,4 +27,9 @@ void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t
void example_lvgl_port_update_callback(lv_disp_drv_t *drv);
void example_increase_lvgl_tick(void *arg);
void LVGL_Init(void); // Call this function to initialize the screen (must be called in the main function) !!!!!
void LVGL_Init(void); // Call this function to initialize the screen (must be called in the main function) !!!!!
#ifdef __cplusplus
}
#endif
+10 -1
View File
@@ -1,5 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "lvgl.h"
#include "demos/lv_demos.h"
@@ -16,4 +20,9 @@
void Backlight_adjustment_event_cb(lv_event_t * e);
void Lvgl_Example1(void);
void LVGL_Backlight_adjustment(uint8_t Backlight);
void LVGL_Backlight_adjustment(uint8_t Backlight);
#ifdef __cplusplus
}
#endif
+9
View File
@@ -1,4 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
@@ -67,3 +72,7 @@ void LVGL_Resume_Music();
void LVGL_Pause_Music();
void LVGL_Play_Music(uint32_t ID);
void LVGL_volume_adjustment(uint8_t Volume);
#ifdef __cplusplus
}
#endif
+10
View File
@@ -4,6 +4,12 @@
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
@@ -68,3 +74,7 @@ esp_err_t esp_lcd_touch_new_i2c_cst816s(const esp_lcd_panel_io_handle_t io, cons
extern esp_lcd_touch_handle_t tp;
void Touch_Init(void);
#ifdef __cplusplus
}
#endif
+11
View File
@@ -1,5 +1,10 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include "I2C_Driver.h"
@@ -42,3 +47,9 @@ void Set_Toggle(uint8_t Pin); // Flip the level of
void TCA9554PWR_Init(uint8_t PinState); // Set the seven pins to PinState state, for example :PinState=0x23, 0010 0011 State (the highest bit is not used) (Output mode or input mode) 0= Output mode 1= Input mode. The default value is output mode
esp_err_t EXIO_Init(void);
#ifdef __cplusplus
}
#endif
+10 -1
View File
@@ -1,5 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <string.h> // For memcpy
#include "esp_log.h"
@@ -20,4 +24,9 @@
void I2C_Init(void);
// Reg addr is 8 bit
esp_err_t I2C_Write(uint8_t Driver_addr, uint8_t Reg_addr, const uint8_t *Reg_data, uint32_t Length);
esp_err_t I2C_Read(uint8_t Driver_addr, uint8_t Reg_addr, uint8_t *Reg_data, uint32_t Length);
esp_err_t I2C_Read(uint8_t Driver_addr, uint8_t Reg_addr, uint8_t *Reg_data, uint32_t Length);
#ifdef __cplusplus
}
#endif
+55
View File
@@ -0,0 +1,55 @@
### 如你所见,这是一份帮助文档。遍布于项目的子目录当中
下面把这段 `afe_config_t` 里的 **每一个成员** 按“它是干什么的 → 典型取值 → 建议” 三步法讲清。
看完你就知道哪些可以大胆改、哪些最好别动。
---
### 1 开关类(true/false
| 成员 | 作用 | 典型取值 | 建议 |
|---|---|---|---|
| **aec_init** | 是否启用 **回声消除**AEC)。<br>用于“喇叭→麦克风”回声场景。 | true:有扬声器<br>false:无扬声器 | 无喇叭就关 |
| **se_init** | 是否启用 **稳态噪声抑制**(降噪)。<br>抑制风扇/空调/胎噪等。 | true:嘈杂环境<br>false:安静环境 | 按场景开关 |
| **vad_init** | 是否启用 **语音活动检测**。<br>只把“有人说话”的区段送给 MultiNet,降低误识别。 | true:怕误触发<br>false:想最省电 | 见上一贴 |
| **wakenet_init** | 是否启用 **唤醒词**WakeNet)。 | true:需要“Hi, Lexin”唤醒<br>false:仅指令识别 | 本帖关 |
| **voice_communication_init** | 是否把 AFE 调成“通话模式”<br>(打开 WebRTC 级 AEC/NS/AGC)。 | trueVoIP/会议<br>false:离线语音识别 | 99% 场景关 |
| **voice_communication_agc_init** | 通话模式下才生效的 AGC 开关。 | 同上 | 同上 |
| **debug_init** | 打开 AFE 内部调试日志。 | true:调试<br>false:量产 | 量产关 |
---
### 2 数值/枚举类
| 成员 | 作用 | 取值范围 & 含义 | 建议 |
|---|---|---|---|
| **vad_mode** | VAD 的灵敏度等级。<br>`VAD_MODE_0` 最松,`VAD_MODE_3` 最严。 | 0~3 | 0=关,1=轻,2=中,3=严 |
| **wakenet_mode** | WakeNet 工作模式(决定通道数/检测阈值)。 | `DET_MODE_0`=关<br>`DET_MODE_2CH_90`=2 麦 90% 阈值 | 关唤醒就设 0 |
| **afe_mode** | AFE 整体运行档。<br>`SR_MODE_HIGH_PERF` 最准但吃资源,`SR_MODE_LOW_COST` 最省。 | LOW_COST / HIGH_PERF | ESP32-S3 建议 LOW_COST |
| **afe_perferred_core** | feed/fetch 任务优先跑在哪个核。 | 0 / 1 | 与业务任务错峰即可 |
| **afe_perferred_priority** | AFE 内部线程优先级(5~15)。 | 5=低,15=高 | 一般 5 就够 |
| **afe_ringbuf_size** | AFE 内部环形缓冲帧数。<br>越大越抗抖动,越大吃 RAM。 | 10~100 | 不开唤醒 10~20 即可 |
| **memory_alloc_mode** | 模型/缓冲放在 PSRAM 还是内部 SRAM。 | `AFE_MEMORY_ALLOC_MORE_PSRAM` → 优先 PSRAM,省内部 RAM | 有 PSRAM 就开 |
| **afe_linear_gain** | 线性数字增益(1.0=不变)。 | 0.1~4.0 | 麦克风灵敏度低可调到 1.5 |
| **agc_mode** | 自动增益控制策略。 | `AFE_MN_PEAK_AGC_MODE_0/1/2` | 默认 2 即可 |
| **voice_communication_agc_gain** | 通话模式下 AGC 目标增益 dB。 | 0~31 | 仅通话模式生效 |
---
### 3 子结构体 `pcm_config`
| 成员 | 作用 | 典型取值 | 说明 |
|---|---|---|---|
| **total_ch_num** | 前端接收的 **总通道数**mic + ref 之和)。 | 2 | I²S 数据里一共几路 |
| **mic_num** | 其中 **麦克风通道** 数量。 | 1 | 单麦就写 1 |
| **ref_num** | **参考通道**(回声参考、噪声参考)数量。 | 1 | 无回声可 0,有回声就 1 |
| **sample_rate** | 采样率。 | 16000 | MultiNet 固定 16 kHz |
---
### 4 一句话总结
- **想最省电/省 RAM**
`aec/se/vad/wakenet` 全关,ringbuf 10LOW_COST,优先 PSRAM。
- **想最稳最抗噪**
`vad_init=true, vad_mode=2, se_init=true`ringbuf 50。
+144 -29
View File
@@ -107,6 +107,117 @@ static void detect_hander(AppSpeech *self)
break;
}
// if (res->wakeup_state == WAKENET_DETECTED) {
// ESP_LOGI(TAG, "WAKEWORD DETECTED\n");
// multinet->clean(model_data); // clean all status of multinet
// LCD_Backlight_original = LCD_Backlight;
// } else if (res->wakeup_state == WAKENET_CHANNEL_VERIFIED) {
// ESP_LOGI(TAG, "AFE_FETCH_CHANNEL_VERIFIED, channel index: %d\n", res->trigger_channel_id);
// ESP_LOGI(TAG, ">>> Say your command <<<");
// self->detected = true;
// self->afe_handle->disable_wakenet(afe_data);
// LCD_Backlight = 35;
//
// }
esp_mn_state_t mn_state = multinet->detect(model_data, res->data);
if (mn_state == ESP_MN_STATE_DETECTING) {
self->command = COMMAND_NOT_DETECTED;
continue;
} else if (mn_state == ESP_MN_STATE_DETECTED) {
esp_mn_results_t *mn_result = multinet->get_results(model_data);
// for (int i = 0; i < mn_result->num; i++) {
// ESP_LOGI(TAG, "TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n",
// i+1, mn_result->command_id[i], mn_result->phrase_id[i], mn_result->string, mn_result->prob[i]);
// }
ESP_LOGI(TAG, "TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n",
1, mn_result->command_id[0], mn_result->phrase_id[0], mn_result->string, mn_result->prob[0]);
switch (mn_result->command_id[0]) {
case 0:
LCD_Backlight = 100;
break;
case 1:
LCD_Backlight = 30;
break;
case 2:
LCD_Backlight = 0;
break;
case 3:
LCD_Backlight = 100;
break;
case 4:
play_Music_Flag = 1;
break;
default: printf("Unknown Command!\r\n"); break;
}
self->command = (command_word_t)mn_result->command_id[0];
// self->afe_handle->enable_wakenet(afe_data);
// self->detected = false;
// self->afe_handle->disable_wakenet(afe_data); // 停止唤醒
self->detected = true;
ESP_LOGI(TAG, ">>> Say your command <<<");
self->command = COMMAND_TIMEOUT;
} else if (mn_state == ESP_MN_STATE_TIMEOUT) {
esp_mn_results_t *mn_result = multinet->get_results(model_data);
ESP_LOGI(TAG, "timeout, string:%s\n", mn_result->string);
self->command = COMMAND_TIMEOUT;
// self->afe_handle->enable_wakenet(afe_data);
self->detected = false;
ESP_LOGI(TAG, ">>> Waiting to be waken up <<<");
LCD_Backlight = LCD_Backlight_original;
if(play_Music_Flag){
play_Music_Flag = 0;
if(ACTIVE_TRACK_CNT)
_lv_demo_music_resume();
else
printf("No MP3 file found in SD card!\r\n");
}
}
}
if (model_data) {
multinet->destroy(model_data);
model_data = NULL;
}
self->afe_handle->destroy(afe_data);
vTaskDelete(NULL);
}
// 下面的函数是上面的备份,使用前需要在idf.py menuconfig中先配置打开唤醒模型
static void detect_handler_continuous(AppSpeech *self)
{
esp_afe_sr_data_t *afe_data = self->afe_data;
int afe_chunksize = self->afe_handle->get_fetch_chunksize(afe_data);
#if defined(CONFIG_SR_MN_CN_MULTINET5_RECOGNITION_QUANT8) || defined(CONFIG_SR_MN_CN_MULTINET6_QUANT) || defined(CONFIG_SR_MN_CN_MULTINET6_AC_QUANT)
char *mn_name = esp_srmodel_filter(self->models, ESP_MN_PREFIX, ESP_MN_CHINESE);
#else
char *mn_name = esp_srmodel_filter(self->models, ESP_MN_PREFIX, ESP_MN_ENGLISH);
#endif // CONFIG_IDF_TARGET_ESP32S3
ESP_LOGI(TAG, "multinet:%s\n", mn_name);
esp_mn_iface_t *multinet = esp_mn_handle_from_name(mn_name);
model_iface_data_t *model_data = multinet->create(mn_name, 6000);
esp_mn_commands_update_from_sdkconfig(multinet, model_data); // Add speech commands from sdkconfig
int mu_chunksize = multinet->get_samp_chunksize(model_data);
assert(mu_chunksize == afe_chunksize);
// FILE *fp = fopen("/sdcard/out", "w");
// if (fp == NULL) ESP_LOGE(TAG,"can not open file\n");
//print active speech commands
multinet->print_active_speech_commands(model_data);
ESP_LOGI(TAG, "Ready");
self->detected = false;
while (true)
{
afe_fetch_result_t* res = self->afe_handle->fetch(afe_data);
if (!res || res->ret_value == ESP_FAIL) {
ESP_LOGE(TAG, "fetch error!\n");
break;
}
if (res->wakeup_state == WAKENET_DETECTED) {
ESP_LOGI(TAG, "WAKEWORD DETECTED\n");
multinet->clean(model_data); // clean all status of multinet
@@ -117,7 +228,7 @@ static void detect_hander(AppSpeech *self)
self->detected = true;
self->afe_handle->disable_wakenet(afe_data);
LCD_Backlight = 35;
}
if (self->detected) {
@@ -129,33 +240,33 @@ static void detect_hander(AppSpeech *self)
} else if (mn_state == ESP_MN_STATE_DETECTED) {
esp_mn_results_t *mn_result = multinet->get_results(model_data);
// for (int i = 0; i < mn_result->num; i++) {
// ESP_LOGI(TAG, "TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n",
// ESP_LOGI(TAG, "TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n",
// i+1, mn_result->command_id[i], mn_result->phrase_id[i], mn_result->string, mn_result->prob[i]);
// }
ESP_LOGI(TAG, "TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n",
ESP_LOGI(TAG, "TOP %d, command_id: %d, phrase_id: %d, string:%s prob: %f\n",
1, mn_result->command_id[0], mn_result->phrase_id[0], mn_result->string, mn_result->prob[0]);
switch (mn_result->command_id[0]) {
case 0:
LCD_Backlight = 100;
case 0:
LCD_Backlight = 100;
break;
case 1:
LCD_Backlight = 30;
case 1:
LCD_Backlight = 30;
break;
case 2:
LCD_Backlight = 0;
case 2:
LCD_Backlight = 0;
break;
case 3:
LCD_Backlight = 100;
case 3:
LCD_Backlight = 100;
break;
case 4:
play_Music_Flag = 1;
case 4:
play_Music_Flag = 1;
break;
default: printf("Unknown Command!\r\n"); break;
}
self->command = (command_word_t)mn_result->command_id[0];
// self->afe_handle->enable_wakenet(afe_data);
// self->detected = false;
self->afe_handle->disable_wakenet(afe_data);
self->detected = true;
ESP_LOGI(TAG, ">>> Say your command <<<");
@@ -171,9 +282,9 @@ static void detect_hander(AppSpeech *self)
if(play_Music_Flag){
play_Music_Flag = 0;
if(ACTIVE_TRACK_CNT)
_lv_demo_music_resume();
_lv_demo_music_resume();
else
printf("No MP3 file found in SD card!\r\n");
printf("No MP3 file found in SD card!\r\n");
}
}
}
@@ -187,32 +298,32 @@ static void detect_hander(AppSpeech *self)
}
// 初始化
void MIC_Speech_init()
{
MIC_Speech.afe_handle = &ESP_AFE_SR_HANDLE;
MIC_Speech.detected = false;
MIC_Speech.command = COMMAND_TIMEOUT;
MIC_Speech.models = esp_srmodel_init("model"); // 这边配置为SD卡当中的文件路径
MIC_Speech.models = esp_srmodel_init("/sdcard/srmodels"); // 这边配置为SD卡当中的文件路径
i2s_init(I2S_NUM_1, 16000, 2, 32);
// sd_card_mount("/sdcard");
afe_config_t afe_config = {
.aec_init = true,
.se_init = true,
.vad_init = true,
.wakenet_init = true,
.aec_init = true, // 回声消除(当用户在播放音频的时候使用语音识别可以有效提告识别率)
.se_init = true, // 降噪
.vad_init = true, // VDA(语音活动检测),用于检测当前是否处于说话状态,如果是,就将音频数据发送给 multinet
.wakenet_init = false, // 关闭唤醒词
.voice_communication_init = false,
.voice_communication_agc_init = false,
.voice_communication_agc_gain = 15,
.vad_mode = VAD_MODE_3,
.wakenet_model_name = NULL,
.vad_mode = VAD_MODE_0, /*VAD_MODE_3,*/ // VAD 灵敏度等级
.wakenet_model_name = NULL, // 不再指定 wakenet
.wakenet_model_name_2 = NULL,
.wakenet_mode = DET_MODE_2CH_90,
.wakenet_mode = DET_MODE_2CH_90, // 0 = 关闭
.afe_mode = SR_MODE_LOW_COST,
.afe_perferred_core = 0,
.afe_perferred_priority = 5,
.afe_ringbuf_size = 50,
.afe_ringbuf_size = 50, // AFE ringbuffer 环形缓冲区大小
.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM,
.afe_linear_gain = 1.0,
.agc_mode = AFE_MN_PEAK_AGC_MODE_2,
@@ -222,7 +333,7 @@ void MIC_Speech_init()
.ref_num = 1,
.sample_rate = 16000,
},
.debug_init = false,
.debug_init = false, // afe内部调试
.debug_hook = {{AFE_DEBUG_HOOK_MASE_TASK_IN, NULL}, {AFE_DEBUG_HOOK_FETCH_TASK_IN, NULL}},
};
afe_config.aec_init = false;
@@ -235,6 +346,10 @@ void MIC_Speech_init()
afe_config.pcm_config.sample_rate = 16000;
afe_config.wakenet_model_name = esp_srmodel_filter(MIC_Speech.models, ESP_WN_PREFIX, NULL);
MIC_Speech.afe_data = MIC_Speech.afe_handle->create_from_config(&afe_config);
xTaskCreatePinnedToCore((TaskFunction_t)feed_handler, "App/SR/Feed", 4 * 1024, &MIC_Speech, 5, NULL, 1);
xTaskCreatePinnedToCore((TaskFunction_t)detect_hander, "App/SR/Detect", 5 * 1024, &MIC_Speech, 5, NULL, 1);
// 注意两个任务被分配了不同的核心与优先级,这是为了防止AFE(Audio Front-End)内部环形缓冲区溢出
// 也就是“喂数据线程” 比 “取数据线程” 跑得快,生产 > 消费,经典的生产者消费者问题
// 但即使这么做了,由于i2s在开始读取数据的时候,识别模型还没加载完成,因此在开始阶段必然会出现环形缓冲区满的警告,问题不大
xTaskCreatePinnedToCore((TaskFunction_t)feed_handler, "App/SR/Feed", 4 * 1024, &MIC_Speech, 4, NULL, 0);
xTaskCreatePinnedToCore((TaskFunction_t)detect_hander, "App/SR/Detect", 5 * 1024, &MIC_Speech, 6, NULL, 1);
}
+9
View File
@@ -1,5 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_afe_sr_iface.h"
#include "esp_process_sdkconfig.h"
#include "model_path.h"
@@ -45,3 +49,8 @@ typedef struct {
void MIC_Speech_init();
#ifdef __cplusplus
}
#endif
+11 -1
View File
@@ -1,5 +1,10 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "I2C_Driver.h"
@@ -99,4 +104,9 @@ void datetime_to_str(char *datetime_str,datetime_t time);
// 3 - wednesday
// 4 - thursday
// 5 - friday
// 6 - saturday
// 6 - saturday
#ifdef __cplusplus
}
#endif
+12 -1
View File
@@ -1,4 +1,10 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "ST77916.h"
#define PWR_KEY_Input_PIN 6 // 电源按键输入引脚(GPIO6)
@@ -13,4 +19,9 @@ void Shutdown(void);
void Restart(void);
void PWR_Init(void);
void PWR_Loop(void);
void PWR_Loop(void);
#ifdef __cplusplus
}
#endif
+12 -1
View File
@@ -1,5 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "I2C_Driver.h"
//device address
@@ -158,4 +164,9 @@ float getGyroX();
float getGyroY();
float getGyroZ();
void getAccelerometer(void);
void getGyroscope(void);
void getGyroscope(void);
#ifdef __cplusplus
}
#endif
+10 -2
View File
@@ -1,6 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
@@ -35,4 +38,9 @@ extern uint32_t Flash_Size;
void SD_Init(void);
void Flash_Searching(void);
FILE* Open_File(const char *file_path);
uint16_t Folder_retrieval(const char* directory, const char* fileExtension, char File_Name[][100],uint16_t maxFiles);
uint16_t Folder_retrieval(const char* directory, const char* fileExtension, char File_Name[][100],uint16_t maxFiles);
#ifdef __cplusplus
}
#endif
+10 -2
View File
@@ -1,11 +1,14 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include <stdio.h>
#include <string.h> // For memcpy
#include "esp_system.h"
@@ -42,4 +45,9 @@ uint16_t BLE_Scan(void);
uint16_t wireless_get_wifi_ap_list(wifi_ap_info_t *ap_list, uint16_t max_aps);
uint16_t wireless_get_ble_device_list(ble_device_info_t *device_list, uint16_t max_devices);
void wireless_print_wifi_aps(void);
void wireless_print_ble_devices(void);
void wireless_print_ble_devices(void);
#ifdef __cplusplus
}
#endif
+200
View File
@@ -0,0 +1,200 @@
### 如你所见,这只是一个临时文件
```c++
// 描述了每个驱动的启动顺序
#include "ST77916.h"
#include "PCF85063.h"
#include "QMI8658.h"
#include "SD_MMC.h"
#include "Wireless.h"
#include "TCA9554PWR.h"
#include "LVGL_Example.h"
#include "BAT_Driver.h"
#include "PWR_Key.h"
#include "PCM5101.h"
#include "MIC_Speech.h"
void Driver_Loop(void *parameter)
{
Wireless_Init();
while(1)
{
QMI8658_Loop();
PCF85063_Loop();
BAT_Get_Volts();
PWR_Loop();
vTaskDelay(pdMS_TO_TICKS(100));
}
vTaskDelete(NULL);
}
void Driver_Init(void)
{
PWR_Init();
BAT_Init();
I2C_Init();
EXIO_Init(); // Example Initialize EXIO
Flash_Searching();
PCF85063_Init();
QMI8658_Init();
xTaskCreatePinnedToCore(
Driver_Loop,
"Other Driver task",
4096,
NULL,
3,
NULL,
0);
}
void app_main(void)
{
Driver_Init();
SD_Init();
LCD_Init();
Audio_Init();
MIC_Speech_init();
// Play_Music("/sdcard","AAA.mp3");
LVGL_Init(); // returns the screen object
// /********************* Demo *********************/
// Lvgl_Example1();
// lv_demo_widgets();
lv_demo_keypad_encoder();
// lv_demo_benchmark();
// lv_demo_stress();
// lv_demo_music();
while (1) {
// raise the task priority of LVGL and/or reduce the handler period can improve the performance
vTaskDelay(pdMS_TO_TICKS(10));
// The task running lv_timer_handler should have lower priority than that running `lv_tick_inc`
lv_timer_handler();
}
}
```
```c++
// esp idf提供的pthread库的使用,主要服务于C++
/* pthread/std::thread example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <iostream>
#include <thread>
#include <chrono>
#include <memory>
#include <string>
#include <sstream>
#include <esp_pthread.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>
using namespace std::chrono;
const auto sleep_time = seconds {
5
};
void print_thread_info(const char *extra = nullptr)
{
std::stringstream ss;
if (extra) {
ss << extra;
}
ss << "Core id: " << xPortGetCoreID()
<< ", prio: " << uxTaskPriorityGet(nullptr)
<< ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes.";
ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str());
}
void thread_func_inherited()
{
while (true) {
print_thread_info("This is the INHERITING thread with the same parameters as our parent, including name. ");
std::this_thread::sleep_for(sleep_time);
}
}
void spawn_another_thread()
{
// Create a new thread, it will inherit our configuration
std::thread inherits(thread_func_inherited);
while (true) {
print_thread_info();
std::this_thread::sleep_for(sleep_time);
}
}
void thread_func_any_core()
{
while (true) {
print_thread_info("This thread (with the default name) may run on any core.");
std::this_thread::sleep_for(sleep_time);
}
}
void thread_func()
{
while (true) {
print_thread_info();
std::this_thread::sleep_for(sleep_time);
}
}
esp_pthread_cfg_t create_config(const char *name, int core_id, int stack, int prio)
{
auto cfg = esp_pthread_get_default_config();
cfg.thread_name = name;
cfg.pin_to_core = core_id;
cfg.stack_size = stack;
cfg.prio = prio;
return cfg;
}
extern "C" void app_main(void)
{
// Create a thread using default values that can run on any core
auto cfg = esp_pthread_get_default_config();
esp_pthread_set_cfg(&cfg);
std::thread any_core(thread_func_any_core);
// Create a thread on core 0 that spawns another thread, they will both have the same name etc.
cfg = create_config("Thread 1", 0, 3 * 1024, 5);
cfg.inherit_cfg = true;
esp_pthread_set_cfg(&cfg);
std::thread thread_1(spawn_another_thread);
// Create a thread on core 1.
cfg = create_config("Thread 2", 1, 3 * 1024, 5);
esp_pthread_set_cfg(&cfg);
std::thread thread_2(thread_func);
// Let the main task do something too
while (true) {
std::stringstream ss;
ss << "core id: " << xPortGetCoreID()
<< ", prio: " << uxTaskPriorityGet(nullptr)
<< ", minimum free stack: " << uxTaskGetStackHighWaterMark(nullptr) << " bytes.";
ESP_LOGI(pcTaskGetName(nullptr), "%s", ss.str().c_str());
std::this_thread::sleep_for(sleep_time);
}
}
```
+9 -3
View File
@@ -2,11 +2,17 @@
// Created by misaki on 2025/8/25.
//
#ifndef BIONIC_SPHERE_ERR_HANDLE_H
#define BIONIC_SPHERE_ERR_HANDLE_H
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define ESP_GOTO(goto_tag) do { \
goto goto_tag; \
} while (0)
#endif //BIONIC_SPHERE_ERR_HANDLE_H
#ifdef __cplusplus
}
#endif
+9 -3
View File
@@ -6,9 +6,12 @@
* @brief 杂项工具类,存放一些工具函数
*/
#pragma once
#ifndef BIONIC_SPHERE_TOOLS_H
#define BIONIC_SPHERE_TOOLS_H
#ifdef __cplusplus
extern "C" {
#endif
// 打印系统内存使用情况
void print_sys_memory(void);
@@ -16,4 +19,7 @@ void print_sys_memory(void);
// 创建一个定时任务,每x秒打印一次系统内存使用情况
void create_print_sys_memory_task();
#endif //BIONIC_SPHERE_TOOLS_H
#ifdef __cplusplus
}
#endif
+5 -3
View File
@@ -6,8 +6,7 @@
#include "err_handle.h"
#include "drivers_test.h"
#include "CppHandle.h"
void app_main(void)
{
@@ -33,7 +32,10 @@ void app_main(void)
// mic_speech_test();
mic_speech_and_gif_and_music_test();
// mic_speech_and_gif_and_music_test();
Cpp_Hand();
ESP_GOTO(err);
err:
ESP_LOGI("app_main", "app_main error");
+14
View File
@@ -1,10 +1,13 @@
idf_component_register(SRCS "Bionic_sphere.c"
# 测试
"../test/driver_test/drivers_test.c" # 测试用例
"../test/EXIO_Test/TCA9554PWR_Test.c" # 测试用例
"../test/audio_test/audio_test.c" # 测试用例
"../test/gif_displat_test/gif_test.c" # 测试用例
# 工具库
"../Util/general_err_handle/err_handle.c" # 错误处理库
"../Util/tools/tools.c" # 工具库
# 底层驱动库
"../Lib/I2C_Driver/I2C_Driver.c" # IIC底层驱动库
"../Lib/QMI8658/QMI8658.c" # IMU驱动库
"../Lib/BAT_Driver/BAT_Driver.c" # 电池adc驱动库
@@ -22,6 +25,12 @@ idf_component_register(SRCS "Bionic_sphere.c"
"../Lib/Display/Touch_Driver/esp_lcd_touch/esp_lcd_touch.c" # 触摸屏驱动库
"../Lib/PWR_Key/PWR_Key.c" # PWR按键驱动库
"../Lib/MIC_Driver/MIC_Speech.c" # 录音驱动库
# 业务代码(使用Cpp编写)
"../Bionic_Core/PetBaseClass/PetBaseClass.cpp" # 宠物基类库
"../Bionic_Core/OTAClass/OTAClass.cpp" # OTA类库
"../Bionic_Core/CommClass/CommClass.cpp" # 通信类库
"../Bionic_Core/ToolsClass/ToolsClass.cpp" # 工具类库
"../Bionic_Core/CppHandle/CppHandle.cpp" # C++&C兼容库
INCLUDE_DIRS "."
"../test/driver_test"
"../test/EXIO_Test"
@@ -46,6 +55,11 @@ idf_component_register(SRCS "Bionic_sphere.c"
"../Lib/Display/Touch_Driver/esp_lcd_touch"
"../Lib/PWR_Key"
"../Lib/MIC_Driver"
"../Bionic_Core/PetBaseClass"
"../Bionic_Core/OTAClass"
"../Bionic_Core/CommClass"
"../Bionic_Core/ToolsClass"
"../Bionic_Core/CppHandle"
PRIV_REQUIRES # 私有依赖
driver
bt
+2 -3
View File
@@ -13,7 +13,6 @@
nvs, data, nvs, , 0x6000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
ota_0, app, ota_0, , 4M,
ota_1, app, ota_1, , 4M,
model, data, , , 6000K
ota_0, app, ota_0, , 6M,
ota_1, app, ota_1, , 6M,
coredump, data, coredump,, 64K,
1 # ESP32-S3 16 MB Flash, OTA + 文件系统
13 otadata, data, ota, , 0x2000,
14 phy_init, data, phy, , 0x1000,
15 ota_0, app, ota_0, , 4M, ota_0, app, ota_0, , 6M,
16 ota_1, app, ota_1, , 4M, ota_1, app, ota_1, , 6M,
17 model, data, , , 6000K coredump, data, coredump,, 64K,
coredump, data, coredump,, 64K,
18
+4 -35
View File
@@ -539,43 +539,12 @@ CONFIG_PARTITION_TABLE_MD5=y
#
# ESP Speech Recognition
#
CONFIG_MODEL_IN_FLASH=y
# CONFIG_MODEL_IN_SDCARD is not set
# CONFIG_MODEL_IN_FLASH is not set
CONFIG_MODEL_IN_SDCARD=y
CONFIG_USE_AFE=y
CONFIG_AFE_INTERFACE_V1=y
# CONFIG_USE_NSNET is not set
CONFIG_USE_WAKENET=y
# CONFIG_SR_WN_WN8_ALEXA is not set
# CONFIG_SR_WN_WN9_HILEXIN is not set
# CONFIG_SR_WN_WN9_XIAOAITONGXUE is not set
# CONFIG_SR_WN_WN9_ALEXA is not set
# CONFIG_SR_WN_WN9_HIESP is not set
# CONFIG_SR_WN_WN9_HIMFIVE is not set
# CONFIG_SR_WN_WN9_NIHAOXIAOZHI_TTS is not set
# CONFIG_SR_WN_WN9_JARVIS_TTS is not set
# CONFIG_SR_WN_WN9_COMPUTER_TTS is not set
# CONFIG_SR_WN_WN9_HEYWILLOW_TTS is not set
# CONFIG_SR_WN_WN9_SOPHIA_TTS is not set
CONFIG_SR_WN_WN9_NIHAOXIAOXIN_TTS=y
# CONFIG_SR_WN_WN9_XIAOMEITONGXUE_TTS is not set
# CONFIG_SR_WN_WN9_HIXIAOXING_TTS is not set
# CONFIG_SR_WN_WN9_MYCROFT_TTS is not set
# CONFIG_SR_WN_WN9_HEYPRINTER_TTS is not set
# CONFIG_SR_WN_WN9_XIAOLONGXIAOLONG_TTS is not set
# CONFIG_SR_WN_WN9_MIAOMIAOTONGXUE_TTS is not set
# CONFIG_SR_WN_WN9_HIJOY_TTS is not set
# CONFIG_SR_WN_WN9_HILILI_TTS is not set
# CONFIG_SR_WN_WN9_HITELLY_TTS is not set
# CONFIG_SR_WN_WN9_HEYWANDA_TTS is not set
# CONFIG_SR_WN_WN9_HIMIAOMIAO_TTS is not set
# CONFIG_SR_WN_WN9_XIAOBINXIAOBIN_TTS is not set
# CONFIG_SR_WN_WN9_HAIXIAOWU_TTS is not set
# CONFIG_SR_WN_WN9_ASTROLABE_TTS is not set
# CONFIG_SR_WN_WN9_XIAOYAXIAOYA_TTS2 is not set
# CONFIG_SR_WN_WN9_HIJASON_TTS2 is not set
# CONFIG_SR_WN_WN9_LINAIBAN_TTS2 is not set
# CONFIG_SR_WN_WN9_CUSTOMWORD is not set
# CONFIG_SR_WN_LOAD_MULIT_WORD is not set
# CONFIG_USE_WAKENET is not set
CONFIG_USE_MULTINET=y
# CONFIG_SR_MN_CN_NONE is not set
CONFIG_SR_MN_CN_MULTINET5_RECOGNITION_QUANT8=y
@@ -809,7 +778,7 @@ CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2
# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set
CONFIG_COMPILER_HIDE_PATHS_MACROS=y
# CONFIG_COMPILER_CXX_EXCEPTIONS is not set
# CONFIG_COMPILER_CXX_RTTI is not set
CONFIG_COMPILER_CXX_RTTI=y
CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y
# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set
# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set
+2 -2
View File
@@ -539,8 +539,8 @@ CONFIG_PARTITION_TABLE_MD5=y
#
# ESP Speech Recognition
#
CONFIG_MODEL_IN_FLASH=y
# CONFIG_MODEL_IN_SDCARD is not set
# CONFIG_MODEL_IN_FLASH is not set
CONFIG_MODEL_IN_SDCARD=y
CONFIG_USE_AFE=y
CONFIG_AFE_INTERFACE_V1=y
# CONFIG_USE_NSNET is not set
+10 -1
View File
@@ -4,6 +4,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "esp_err.h"
@@ -19,4 +24,8 @@
* @return ESP_OK 全部测试通过
* ESP_FAIL 任一测试失败
*/
esp_err_t EXIO_Test(void);
esp_err_t EXIO_Test(void);
#ifdef __cplusplus
}
#endif
+9 -1
View File
@@ -3,4 +3,12 @@
//
#pragma once
void run_audio_test(void);
#ifdef __cplusplus
extern "C" {
#endif
void run_audio_test(void);
#ifdef __cplusplus
}
#endif
+9 -4
View File
@@ -2,8 +2,12 @@
// Created by misaki on 2025/8/23.
//
#ifndef BIONIC_SPHERE_DRIVERS_TEST_H
#define BIONIC_SPHERE_DRIVERS_TEST_H
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// imu测试
void imu_test(void);
@@ -41,5 +45,6 @@ void mic_speech_test(void);
// 语音识别+GIF+音乐测试
void mic_speech_and_gif_and_music_test(void);
#endif //BIONIC_SPHERE_DRIVERS_TEST_H
#ifdef __cplusplus
}
#endif
+7 -3
View File
@@ -2,12 +2,16 @@
// Created by misaki on 2025/8/25.
//
#ifndef BIONIC_SPHERE_GIF_TEST_H
#define BIONIC_SPHERE_GIF_TEST_H
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void gif_display_test(void);
void gif_display_test_1(void);
#endif //BIONIC_SPHERE_GIF_TEST_H
#ifdef __cplusplus
}
#endif
+15 -1
View File
@@ -173,4 +173,18 @@
#### Day8 2025.9.1
##### 主要目标:测试开发板官方提供的例程中的驱动能否使用
实际完成任务:
- [x] 1. 完成了语言识别测试,测试基本通过,后续需要修改从sd卡导入模型以配合ota
- [x] 1. 完成了语言识别测试,测试基本通过,后续需要修改从sd卡导入模型以配合ota
#### Day9 2025.9.2
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 进一步拓展了语音识别,目前可以从sd卡导入模型,避免了model文件占用flash分区大小
- [x] 2. 进一步修改了语音识别,关闭了关键词唤醒功能,只保留了指令识别功能
#### Day10 2025.9.3
##### 主要目标:完成具体业务开发&各种优化
实际完成任务:
- [x] 1. 构建了业务层的基本框架(增加了底层驱动对于的C++兼容),业务代码采用C++编写,启用了RTTI(运行时类型识别)
- [ ] 2.