内容介绍
内容介绍
Funpack ESP32-BOX-LITE
项目描述
硬件介绍
ESP-BOX 是乐鑫发布的新一代 AIoT 开发平台,ESP32-S3-BOX-Lite 开发套件配备了一块 2.4 寸 LCD 显示屏、双麦克风、一个扬声器、两个用于硬件拓展的 Pmod™ 兼容接口和3个独立按键,可构建多样的 HMI 人机交互应用。开发板可实现离线语音唤醒和命令词识别,支持乐鑫自研的高性能声学前端算法构建语音交互系统。开发者可利用开源的 SDK轻松构建在线离线语音助手、智能语音设备、HMI 人机交互设备、多协议网关等多样的应用。
特性:
- 双麦克风支持远场语音交互
- 高唤醒率的离线语音唤醒
- 高识别率的离线中英文命令词识别
- 可重新配置的200+中文和英文语音命令
- 可连续识别和唤醒中断
- 灵活且可重用的 GUI 框架
- 端到端一站式接入云平台AIoT开发框架ESP-RainMaker
- Pmod™兼容接口支持扩展外设模块
- 提供了大量的使用说明和开发案例
而本项目用到的主要硬件:
- Display:
- ST7789V
- Microphone:
- Dual Microphone
- ADC: ES7243
- Speaker:
- ES8156
硬件框图如下:
项目介绍
本项目实现的主要功能如下:
- 上电后会自动连接配置的 WiFi,并由 sntp 协议自动获取互联网时间,同步到系统时间
- 通过 WiFi 和 https 协议从互联网获取天气信息,并更新到系统中
- 通过 TTS 对获取到的天气信息进行播报
- 加载 AFE 模型并启用按键检测,当按下 BOOT 按键后会自动记录当前 5s 左右的声音,并在记录完毕后通过 AFE 算法对该音频进行处理,之后通过扬声器播放处理后的声音信息
设计思路
主要的处理步骤如下:
- 输出芯片详情
- 系统启动,初始化各种外设,主要包括:
i. 音频相关的 codec、adc 等芯片的初始化以及对应的 iic、i2s 等协议的初始化
ii. 按键的初始化。由于 boxlite 上按键为同一个 io,利用 adc 识别,所以这里还需要处理 adc 的初始化
iii. led 初始化(受控于 pwm,所以这里还包括 pwm 的初始化)
iv. lcd 及其控制器的初始化 - 一些第三方库的初始化(主要就是lvgl初始化)
- 启动各个模块的电源,使能对应的模块
- 一些较为复杂的硬件的初始化和功能的准备阶段
i. wifi 初始化,并等待 wifi 连接
ii. tts 模型的加载与对应 handler 的创建 - 启动天气应用,进入天气界面
i. 这里边会通过 WiFi 同步时间和当地的天气信息
ii. 并将对应的天气信息送入 TTS 进行语音合成和输出
iii. 同时会反复的刷新屏幕上时间、天气信息 - 加载 AFE 模型,开始监听 mic 的音频信号
i. 该阶段会监控按键,一旦按键按下就会通过 mic 记录音频数据
ii. 并将该音频数据送入 AFE 模型中进行语音处理,之后通过扬声器进行输出处理后的音频
主要代码片段及说明
mian 函数中的主要逻辑如下:
void app_main(void)
{
ESP_LOGI(TAG, "");
ESP_LOGI(TAG, "==========================================");
ESP_LOGI(TAG, "Compile time: %s %s", __DATE__, __TIME__);
/* Print chip information */
{
...
}
ESP_ERROR_CHECK(bsp_board_init());
ESP_ERROR_CHECK(bsp_board_power_ctrl(POWER_MODULE_AUDIO, true));
// led init
const board_res_desc_t *brd = bsp_board_get_description();
app_pwm_led_init(brd->PMOD2->row1[1], brd->PMOD2->row1[2], brd->PMOD2->row1[3]);
lcd_init();
dev_wifi_init();
while(!(get_wifi_status() & WIFI_CONNECTED_BIT)) {
ESP_LOGI(TAG, "wait for wifi connect");
vTaskDelay(pdMS_TO_TICKS(1000));
}
tts_init();
weather_init();
sr_init();
}
- 这里主要是上文所说的主要流程的代码实现
- 音频相关的 codec、adc 等芯片的初始化以及对应的 iic、i2s 等协议的初始化
- 按键的初始化。由于 boxlite 上按键为同一个 io,利用 adc 识别,所以这里还需要处理 adc 的初始化
- led 初始化(受控于 pwm,所以这里还包括 pwm 的初始化)
- lcd 及其控制器的初始化
main 中有大部分是该板卡自带的 SDK 中提供的函数,再此不做过多的声明,以下主要展示自己写的以下函数:
dev_wifi_init:
void dev_wifi_init(void)
{
//Initialize NVS
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);
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();
}
- 初始化NVS flash
- 如果是第一次需要先擦除
- 初始化wifi作为station,并连接wifi
tts_init:
void tts_init()
{
const esp_partition_t* part=esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "voice_data");
if (part==NULL) {
ESP_LOGI(TAG, "Couldn't find voice data partition!");
} else {
ESP_LOGI(TAG, "voice_data paration size:%d", part->size);
}
void* voicedata;
spi_flash_mmap_handle_t mmap;
esp_err_t err=esp_partition_mmap(part, 0, part->size, SPI_FLASH_MMAP_DATA, &voicedata, &mmap);
if (err != ESP_OK) {
ESP_LOGI(TAG, "Couldn't map voice data partition!");
}
esp_tts_voice_t *voice=esp_tts_voice_set_init(&esp_tts_voice_template, (int16_t*)voicedata);
g_tts_handle=esp_tts_create(voice);
}
- 找到烧录进去的 voice_data 扇区
- 获取扇区大小
- 将该扇区映射到一个变量mmap中,方便直接对该模型操作
- 直接调用现成的api从扇区中读取模型并初始化对应的数据结构
- 创建tts模型,得到对应的操作句柄
weather_init:
void weather_init(void)
{
ui_acquire();
weather_gui_init();
ui_release();
init_sntp_time();
update_weather();
vTaskDelay(pdMS_TO_TICKS(1000));
xTaskCreatePinnedToCore(weather_task, "weather_task", 1024 * 6, NULL, configMAX_PRIORITIES - 3, NULL, 0);
}
- 初始化最开始的 gui,确保界面正确显示
- 通过wifi联网并通过sntp服务更新系统时间
- 从天气服务商获取天气数据并更新界面
- 最后会启动一个线程来刷新界面,主要为了时间和界面上的 gif 动态显示,所以需要单独处理
sr_init:
void sr_init()
{
srmodel_list_t *models = esp_srmodel_init("model");
if (models!=NULL) {
for (int i=0; i<models->num; i++) {
ESP_LOGI(TAG, "Load: %s", models->model_name[i]);
}
}
char *wn_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);
afe_handle = (esp_afe_sr_iface_t *)&ESP_AFE_SR_HANDLE;
afe_config_t afe_config = AFE_CONFIG_DEFAULT();
afe_config.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
afe_config.wakenet_init = true;
afe_config.wakenet_model_name = wn_name;
afe_config.voice_communication_init = false;
afe_config.aec_init = false;
afe_data = afe_handle->create_from_config(&afe_config);
ESP_LOGI(TAG, "get_total_channel_num: %d", afe_handle->get_total_channel_num(afe_data));
g_record_data_maxlen = afe_handle->get_total_channel_num(afe_data) * 2 * 16000 * 2;// 5s
g_record_data = heap_caps_malloc(g_record_data_maxlen, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (g_record_data) {
memset(g_record_data, 0, g_record_data_maxlen);
}
g_record_data_idx = 0;
task_flag = 1;
xTaskCreatePinnedToCore(&debug_pcm_task, "debug_pcm_save", 2 * 1024, NULL, 5, NULL, 1);
xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void*)afe_data, 5, NULL, 0);
xTaskCreatePinnedToCore(&detect_Task, "detect", 4 * 1024, (void*)afe_data, 5, NULL, 1);
bsp_btn_register_callback(BOARD_BTN_ID_BOOT, BUTTON_PRESS_DOWN, enter_btn_press_cb, NULL);
bsp_btn_register_callback(BOARD_BTN_ID_BOOT, BUTTON_PRESS_UP, enter_btn_press_cb, NULL);
}
- 这里会启动三个线程分别处理不同的任务:
- debug_pcm_save:负责最后播放处理后的音频数据
- feed:负责将原始的音频数据送入模型处理
- detect:负责取出处理完毕后的音频信号
- 同时这一块中还有一个需要注意的点是:mic 和扬声器所用的 ADC:ES8156 之间的通道数是不一样的,所以在通过 mic 接受数据和通过 adc 播放声音的时候需要分别设置不同的通道数量,这一点尤为重要,不然声音会显得不正常
上述仅仅只放出了具体的初始化函数及其实现细节以及部分注意事项,具体功能受限于篇幅问题不再展开细说,具体实现的细节可以参考文末附带的工程源码
功能展示及说明
主要界面如下:
功能展示详见视频
对本活动的心得体会
很高兴硬禾能够举办这种活动,让我有机会能够学到更多的东西
由于附件不能超过 10 M ,所以代码直接放网盘了:
链接:https://pan.baidu.com/s/1FQFGsGE26-ZvXydzEDncmg
提取码:6o8t
团队介绍
本项目由本人独立完成
评论
0 / 100
查看更多
猜你喜欢
基于乐鑫SmartConfig的智能配网诗词助手硬件使用ESP-BOX-LITE,使用乐鑫的SMART config智能配网技术,结合TTS以及HTTPS组件,获取网络诗词后,朗读出来。
playlikework
1287
[Funpack2-5]基于ESP32-S3的一个语音播报系统Funpack活动项目,基于ESP32-Box-lite实现的语音交互小盒子。使用ESP32的WiFi和TTS功能,实现一个语音播报系统,如联网获取粉丝数并播报或者获取天气并播报。
Hessian
2441
「Funpack2-5期ESP32-S3」基于esp32 box lite的局域网小说阅读器使用微软的Playwright框架抓取小说章节内容,使用Golang解析小说目录并调用脚本抓取完整小说章节并保存到本地。用Gin框架实现小说局域网阅读的微服务。
忙碌的死龙
921