根据要求完成如下任务:
任务一:
使用ESP32的WiFi和TTS功能,实现一个语音播报系统,如联网获取粉丝数并播报或者获取天气并播报。
所以我做了一个获取天气并播报的系统。
核心微控制器方面:ESP32-S3 是一款集成 2.4G Wi-Fi 和 Bluetooth 5 的 MCU 芯片,搭Xtensa 32 位 LX7 双核处理器,主频高达 240 MHz,内置 512 KB SRAM,具有 45 个可编程 GPIO 管脚和丰富的通信接口。ESP32-S3 支持更大容量的高速 SPI flash 和片外 RAM,支持用户配置数据缓存与指令缓存。
开发板方面:esp32 box lite 有8MB PSRAM,16MB Flash。支持声学前端算法,支持远场噪音环境。离线语音支持自定义的200多条语音命令,并且可连续识别。开发板配备一块320x240分辨率 的2.4寸LCD显示屏、双麦克风、一个扬声器、两个用于硬件拓展的Pmod兼容接口(16 个可编程 GPIO)和5个按键,可构建多样的 HMI 人机交互的应用。
应用方向:开发者可利用开源的 SDK轻松构建在线离线语音助手、智能语音设备、HMI 人机交互设备、多协议网关等多样的应用。
设计思路:使用wifi连接到路由器,通过http协议获取心知天气api接口的天气数据。esp32box lite 处理天气数据,并使用扬声器播报出来。
程序流程图:
代码部分:
如下所示:使用ESP32的WiFi和TTS功能,实现天气语音播报。
在app_main函数里首先对 codec 芯片初始化,然后使能扬声器电源,开始连接wifi,在 wifi_init sta 函数里进行为wifi的连接,我连接wifi的名称是esp32,密码12345678 。
/* set wifi */
#define EXAMPLE_ESP_WIFI_SSID "esp32"
#define EXAMPLE_ESP_WIFI_PASS "12345678"
#define EXAMPLE_ESP_MAXIMUM_RETRY 3
//wifi init
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
/* Setting a password implies station will connect to all security modes including WEP/WPA.
* However these modes are deprecated and not advisable to be used. Incase your Access point
* doesn't support WPA2, these mode can be enabled by commenting below line */
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.pmf_cfg = {
.capable = true,
.required = false
},
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
/* The event will not be processed after unregister */
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
vEventGroupDelete(s_wifi_event_group);
}
void app_main(void)
{
bsp_board_init(); // 初始化codec芯片,配置好采样率、声道数、采样大小
bsp_board_power_ctrl(POWER_MODULE_AUDIO,true); //开启扬声器电源
//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();
ESP_LOGI(TAG, "---http_client---");
ESP_ERROR_CHECK(bsp_spiffs_init_default());
ESP_ERROR_CHECK(lv_port_init());
bsp_lcd_set_backlight(true);
image_display();
do {
lv_task_handler();
} while (vTaskDelay(1), true);
}
wifi 连接完成之后,由于我的tts代码放在按键回调函数btn event cb里,所以需要等待按键被按下。
当我按下box lite 正面中间按键时,box lite发起http请求并把数据保存在 output buffer,使用Cjson解析天气数据,解析完成后,tts接口把文字解析成拼音,并把拼音转换成PCM音频,通过扬声器播放音频,就会发出天气的播报声音。
代码如下:
tatic void btn_event_cb(lv_event_t *event)
{
lv_obj_t *img = (lv_obj_t *) event->user_data;
const char *file_name = lv_list_get_btn_text(lv_obj_get_parent(event->target), event->target);
char *file_name_with_path = (char *) heap_caps_malloc(256, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (NULL != file_name_with_path) {
/* Get full file name with mount point and folder path */
strcpy(file_name_with_path, "S:/spiffs/");
strcat(file_name_with_path, file_name);
/* Set src of image with file name */
lv_img_set_src(img, file_name_with_path);
/* Align object */
lv_obj_align(img, LV_ALIGN_CENTER, 80, 0);
/* Only for debug */
ESP_LOGI(TAG, "Display image file : %s", file_name_with_path);
/* Don't forget to free allocated memory */
free(file_name_with_path);
}
http_test_task();
//语音
size_t bytes_write=0;
esp_tts_voice_t *voice = (esp_tts_voice_t *)&esp_tts_voice_xiaole; // 配置tts的声音配置文件,来自libvoice_set_xiaole
esp_tts_handle_t *tts_handle = esp_tts_create(voice); // 创建tts对象
// char *prompt1 = "今 天 天 气 情 况 多 云 大 雨 冰 雹"; // 需要转换的文字
// char test_tts[100] = {"今 天 天 气 情 况 多 云 大 雨 冰 雹"};
if (esp_tts_parse_chinese(tts_handle, tts_buffer)) // 文字解析成拼音
{
int len[1] = {0};
do
{
short *pcm_data = esp_tts_stream_play(tts_handle, len, 0); // 拼音转换成pcm音频
// esp_audio_play(pcm_data, len[0] * 2, portMAX_DELAY); //播放音频
i2s_write(I2S_NUM_0, (const char *)pcm_data, len[0] * 2, &bytes_write, portMAX_DELAY);
} while (len[0] > 0);
}
esp_tts_stream_reset(tts_handle); // 重置 tts 流并清除 TTS 实例的所有缓存
}
代码完成后使用 idf.py 命令编译并烧录就可以听到播报的声音了。
活动心得体会:乐鑫芯片性能强劲,性价比高,同时乐鑫芯片资料比较多,越来越多的开发者使用乐鑫芯片,遇到问题也很容易找到答案。所以乐鑫也越来越受到国内外的物联网开发者欢迎。
意见:希望Funpack 活动搞一些性能稍强些的芯片,基础芯片一两款玩玩,就可以上手难度稍高的了。