Funpack2-5-基于ESP32-S3-BOX-LITE的B站用户信息播报器
基于ESP32-S3-BOX-LITE实现网络请求B站的用户信息,并显示在屏幕上,然后通过TTS语音播报信息。
标签
Funpack活动
ESP32-S3
ESP32-S3-BOX-LITE
launcher
更新2023-08-01
737

1 项目介绍

本项目使用ESP32-S3-BOX-LITE,基于其WIFI功能、屏幕显示功能、TTS语音功能,实现网络请求B站用户信息,并将信息显示与屏幕上,然后通过强大的TTS语音播报出来。

2 设计思路

要获取B站用户信息,首先设备需要具备网络连接功能,然后通过相应的API发起请求,然后解析请求结果,并在屏幕上进行GUI显示。最后将解析的粉丝数、视频数、点赞数等用户信息,一一通过ESP32的TTS功能进行播报输出。

3 硬件介绍

3.1 简介

ESP-BOX 是乐鑫发布的新一代 AIoT 开发平台,ESP32-S3-BOX-Lite 开发套件配备了一块2.4寸LCD显示屏、双麦克风、一个扬声器、两个用于硬件拓展的 Pmod™ 兼容接口和3个独立按键,可构建多样的HMI人机交互应用。开发板可实现离线语音唤醒和命令词识别,支持乐鑫自研的高性能声学前端算法构建语音交互系统。

3.2 结构图

FqgbusVGlFhZRDUygz3oaYTLKYa8

4 开发环境搭建

使用ESP32进行开发,最基础的一步是要搭建好开发环境。本人比较喜欢使用C语言和VSCODE进行开发,所以本文开发环境主要介绍基于VSCODE的IDF开发环境搭建。

4.1 安装IDF插件

Fm0pMv2imr6035SdgjsSm4xQLPyY

4.2 打开设置向导

FkTgDCCZ0sb3T1Z3--HX6qCC4Ohn

4.3 安装

FkxT_NPnEEEzgC3uMHvL_8-gQGmI

此处需要重点注意,服务器选择Espressif,不要选择github,否则会很慢,而且容易下载到一半失败。同时选择对应的IDF版本,此处我选的是V4.4.5版本,因为ESP32-S3-BOX-LITE的相关示例目前对V4.4版本具有较好的兼容性。版本选择时,注意选择Release版本,不要选择带branch的版本,否则还是会链接到github进行下载。

5 基础代码仓库

(1)IDF仓库

https://github.com/espressif/esp-idf.git

这个仓库实际上安装IDF环境的时候,代码就会被安装了,它包含了很多ESP32 MCU的SDK库及示例,这些库和示例是针对MCU的,不依赖于板载环境。

(2)box仓库

https://github.com/espressif/esp-box.git

这个仓库是ESP32-S3-BOX(LITE)专有仓库,它实现了在ESP32-S3-BOX(LITE)硬件环境下的板级支持包,如本项目将会使用的LCD屏幕、I2S音频播放、按键检测等。

6 软件开发

6.1 新建工程

本项目主要用到https request、LVGL显示、TTS功能,所以比较便捷的方法是,基于IDF的https request工程模板,在BOX仓库的example下建立工程,结构如下:

6.2 任务创建

    xTaskCreate(&https_request_task, "https_get_task", 10240, NULL, 5, NULL);
    xTaskCreate(&app_gui_task, "app_gui_task", 10240, NULL, 5, NULL);
    xTaskCreate(&app_tts_task, "app_tts_task", 10240, NULL, 4, NULL);

本项目在main task中创建了三个任务,https_get_task用于从B站获取粉丝数并解析;app_gui_task用于GUI显示;app_tts_task任务用于语音播报。

6.3 按键回调
static void btn_next_click_cb(void *arg)
{
    g_tabview_index++;
    if(g_tabview_index >= MAX_TABVIEW_TABS)
    {
        g_tabview_index = 0;
    }
    xSemaphoreGive(xSemaph_UpdateTabView);
}
static void btn_prev_click_cb(void *arg)
{
    g_tabview_index--;
    if(g_tabview_index >= MAX_TABVIEW_TABS)
    {
        g_tabview_index = (MAX_TABVIEW_TABS-1);
    }
    xSemaphoreGive(xSemaph_UpdateTabView);
}
static void btn_enter_click_cb(void *arg)
{
    if(g_tabview_index < 2)
    {
       g_https_req_index = g_tabview_index;
       xSemaphoreGive(xSemaph_HttpsRequest);
       xSemaphoreGive(xSemaph_MessageBoxShow);
    }
}

按键回调函数,主要处理页面切换,并按键触发https请求。

6.4 https请求任务

void https_request_task(void *pvparameters)
{
    int idx;
    while(1)
    {
        xSemaphoreTake( xSemaph_HttpsRequest, portMAX_DELAY );

        http_request_buf = (char *) heap_caps_malloc(HTTP_REQUEST_BUF_LEN, MALLOC_CAP_SPIRAM);
        if(http_request_buf != NULL)
        {
            https_get_request_using_cacert_buf(g_https_req_index);
            ESP_LOGI(TAG, "%s\n", http_request_buf);
            for (idx = 0; idx < HTTP_REQUEST_BUF_LEN; idx++)
            {
                if(http_request_buf[idx] == '{')
                    break;
            }
            cJSON *json = cJSON_Parse(&http_request_buf[idx]);

            if(json != NULL)
            {
                //ESP_LOGI(TAG, "%s", cJSON_Print(json));
                cJSON *data = cJSON_GetObjectItem(json, "data");
                cJSON *card = cJSON_GetObjectItem(data, "card");
                if(card != NULL)
                {
                    char *name = cJSON_GetStringValue(cJSON_GetObjectItem(card, "name"));
                    strcpy(g_stc_bili_info.name, name);
                    g_stc_bili_info.fans = cJSON_GetNumberValue(cJSON_GetObjectItem(card, "fans"));
                    g_stc_bili_info.friends = cJSON_GetNumberValue(cJSON_GetObjectItem(card, "friend"));
                    g_stc_bili_info.attention = cJSON_GetNumberValue(cJSON_GetObjectItem(card, "attention"));
                    g_stc_bili_info.vides = cJSON_GetNumberValue(cJSON_GetObjectItem(data, "archive_count"));
                    g_stc_bili_info.likes = cJSON_GetNumberValue(cJSON_GetObjectItem(data, "like_num"));
                    xSemaphoreGive(xSemaph_HttpsRequestDone);
                    xSemaphoreGive(xSemaph_TTSRequest);
                }
                else
                {
                    ESP_LOGI(TAG, "Json data failed");
                }
        }
            else
            {
                ESP_LOGI(TAG, "Json failed");
            }
            /* Don't forget to free allocated memory */
            free(http_request_buf);
        }
        else
        {
            ESP_LOGI(TAG, "malloc http request buf failed");
        }
    }

    ESP_LOGI(TAG, "End https_request example");
    vTaskDelete(NULL);
}

该任务在收到按键释放的信号量之后,便进行https请求,通过API向B站请求用户信息,由于返回的是Json格式数据,因此还需要对Json数据进行解析,然后将解析的目标数据存放于g_stc_bili_info结构体中,并释放信号量给TTS任务和GUI任务进行播报和显示。

6.5 GUI显示任务

void app_gui_task(void *pvparameters)
{
    lv_gui_creat();

    do {
        lv_task_handler();

        //update handler
        if( xSemaphoreTake( xSemaph_UpdateTabView, 0 ) == pdTRUE )
        {
            ESP_LOGI(TAG, "btn down");
            lv_tabview_set_act(g_tabview, g_tabview_index, LV_ANIM_ON);
        }
        if( xSemaphoreTake( xSemaph_HttpsRequestDone, 0 ) == pdTRUE )
        {
            update_tabview_user();
            update_tabview_videos();
            lv_msgbox_close(g_msgbox);

        }
        if( xSemaphoreTake( xSemaph_MessageBoxShow, 0 ) == pdTRUE )
        {
            g_msgbox = lv_msgbox_create(lv_scr_act(), NULL, "Waiting for https request...", NULL, false);
            static lv_style_t msgbox_style;
            lv_style_init(&msgbox_style);
            lv_style_set_bg_color(&msgbox_style, lv_color_hex(0x999999));
            lv_obj_add_style(g_msgbox, &msgbox_style, LV_PART_MAIN);

            lv_obj_center(g_msgbox);
        }
    } while (vTaskDelay(1), true);
}

6.6 TTS任务

void app_tts_task(void *pvparameters)
{
    /*** 1. create esp tts handle ***/
    // initial voice set from separate voice data partition
    .........
    while(1)
    {
        xSemaphoreTake( xSemaph_TTSRequest, portMAX_DELAY );

        char *tts_i2s_buffer = (char *) heap_caps_malloc(1024*100, MALLOC_CAP_SPIRAM);
        if (NULL != tts_i2s_buffer) 
        {
            /*** 2. play prompt text ***/
            char prompt[256];
            sprintf(prompt, "粉丝数%d,关注数%d,视频数%d,点赞数%d", g_stc_bili_info.fans,g_stc_bili_info.attention,g_stc_bili_info.vides,g_stc_bili_info.likes);
            //char *prompt1="我是谁从哪来到哪去,你在干什么";  
            printf("%s\n", prompt);
            uint32_t index = 0;
            if (esp_tts_parse_chinese(tts_handle, prompt)) {
                    int len[1]={0};
                    do {
                        short *pcm_data=esp_tts_stream_play(tts_handle, len, 0);
                        memcpy(&tts_i2s_buffer[index], pcm_data, len[0]*2);
                        index += len[0]*2;
                        //esp_audio_play(pcm_data, len[0]*2, portMAX_DELAY);
                        //printf("data:%d \n", len[0]);
                    } while(len[0]>0);
            }
            esp_tts_stream_reset(tts_handle);
            esp_audio_play(tts_i2s_buffer, index, portMAX_DELAY);
            /* Don't forget to free allocated memory */
            free(tts_i2s_buffer);
        }
        else
        {
            printf("malloc buf for tts failed! \n");
        }
    }
}

该任务是接到TTS信号量后,便开始执行。先将文字与数据进行拼接,然后调用TTS解析转换成音频数据。此处,我分配一个大内存,先将转换的音频数据存储到数组中,全部转换完成之后,再调用I2S接口进行播放。这样做的好处是,播放声音会很流畅,但会占用比较大的缓存空间,还好ESP-BOX-LITE板载了8M SPIRAM,足够使用。

7 效果展示

FtHgZMh22TabOVPu78nckoZ0AJZd

8 活动小结

通过本次活动,熟悉了ESP32开发环境的搭建,以及基于ESP-IDF开发应用的流程,同时也被ESP32丰富的外设资源、强大的运算能力、超高的集成度所震撼,使用ESP32系列芯片及配套的SDK可以很快速的开发网络、GUI、语音识别等应用。

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