Funpack2-5 使用 ESP32s3 天气语音播报系统
由于对智能物联网开发的喜爱,所以入手box lite 学习了 tts 功能以及离线语音示例的功能
标签
嵌入式系统
Funpack活动
Funpack2-5
ESP32S3
liuliuliu
更新2023-08-02
郑州大学
465

根据要求完成如下任务:

任务一
使用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 处理天气数据,并使用扬声器播报出来。

      程序流程图:

FgaDdLZVoIKYVN8lBALKDQ7WS9DV

代码部分:
如下所示:使用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 活动搞一些性能稍强些的芯片,基础芯片一两款玩玩,就可以上手难度稍高的了。

附件下载
tts.zip
tts代码
团队介绍
个人
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号