[作品提交] 2024 DigiKey 创意大赛】之智联音响

rertet   2024-11-1 08:55 楼主

一、作品简介

本环境项目模块目的是将有线音响赋值成无线wifi音响

  1. 处于同一局域网下即可实现音频传输
  2. 手机端使用酷狗音进行音乐推送

     

二、系统框图

image.png

image.png

三、功能说明

  1. ESP32 评估板:负责数据处理和传输
  2. LCD 触摸屏显示器:显示信息
  3. 手机app酷狗客户端

 

四、作品源码

#include "esp_log.h"
#include "nvs_flash.h"
#include "board.h"
#include "esp_peripherals.h"
#include "periph_wifi.h"

#include "audio_mem.h"
#include "esp_wifi.h"
#include "esp_ssdp.h"
#include "esp_dlna.h"

#include "esp_audio.h"
#include "esp_decoder.h"
#include "http_stream.h"
#include "i2s_stream.h"
#include "media_lib_adapter.h"
#include "audio_idf_version.h"

#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0))
#include "esp_netif.h"
#else
#include "tcpip_adapter.h"
#endif

static const char *TAG = "DLNA_EXAMPLE";

#define DLNA_UNIQUE_DEVICE_NAME "ESP32_DMR_8db0797a"
#define DLNA_DEVICE_UUID "8db0797a-f01a-4949-8f59-51188b181809"
#define DLNA_ROOT_PATH "/rootDesc.xml"

static esp_audio_handle_t player = NULL;
static esp_dlna_handle_t  dlna_handle = NULL;

static int vol, mute;
static char *track_uri = NULL;
static const char *trans_state = "STOPPED";

static void player_event_handler(esp_audio_state_t *state, void *ctx)
{
    if (dlna_handle == NULL) {
        return ;
    }

    switch (state->status) {
        case AUDIO_STATUS_RUNNING:
            trans_state = "PLAYING";
            esp_dlna_notify_avt_by_action(dlna_handle, "TransportState");
            break;
        case AUDIO_STATUS_PAUSED:
            trans_state = "PAUSED_PLAYBACK";
            esp_dlna_notify_avt_by_action(dlna_handle, "TransportState");
            break;
        case AUDIO_STATUS_STOPPED:
        case AUDIO_STATUS_FINISHED:
            trans_state = "STOPPED";
            esp_dlna_notify_avt_by_action(dlna_handle, "TransportState");
            break;
        default:
            break;
    }
}

int renderer_request(esp_dlna_handle_t dlna, const upnp_attr_t *attr, int attr_num, char *buffer, int max_buffer_len)
{
    int req_type;
    int tmp_data = 0, buffer_len = 0;
    int hour = 0, min = 0, sec = 0;

    if (attr_num != 1) {
        return 0;
    }

    req_type = attr->type & 0xFF;
    switch (req_type) {
        case RCS_GET_MUTE:
            ESP_LOGD(TAG, "get mute = %d", mute);
            return snprintf(buffer, max_buffer_len, "%d", mute);
        case RCS_SET_MUTE:
            mute = atoi(buffer);
            ESP_LOGD(TAG, "set mute = %d", mute);
            if (mute) {
                esp_audio_vol_set(player, 0);
            } else {
                esp_audio_vol_set(player, vol);
            }
            esp_dlna_notify(dlna, "RenderingControl");
            return 0;
        case RCS_GET_VOL:
            esp_audio_vol_get(player, &vol);
            ESP_LOGI(TAG, "get vol = %d", vol);
            return snprintf(buffer, max_buffer_len, "%d", vol);
        case RCS_SET_VOL:
            vol = atoi(buffer);
            ESP_LOGI(TAG, "set vol = %d", vol);
            esp_audio_vol_set(player, vol);
            esp_dlna_notify(dlna, "RenderingControl");
            return 0;
        case AVT_PLAY:
            ESP_LOGI(TAG, "Play with speed=%s trans_state %s", buffer, trans_state);
            esp_audio_state_t state = { 0 };
            esp_audio_state_get(player, &state);
            if (state.status == AUDIO_STATUS_PAUSED) {
                esp_audio_resume(player);
                esp_dlna_notify(dlna, "AVTransport");
                break;
            } else if (track_uri != NULL) {
                esp_audio_play(player, AUDIO_CODEC_TYPE_DECODER, track_uri, 0);
                esp_dlna_notify(dlna, "AVTransport");
            }
            return 0;
        case AVT_STOP:
            ESP_LOGI(TAG, "Stop instance=%s", buffer);
            esp_audio_stop(player, TERMINATION_TYPE_NOW);
            esp_dlna_notify_avt_by_action(dlna_handle, "TransportState");
            return 0;
        case AVT_PAUSE:
            ESP_LOGI(TAG, "Pause instance=%s", buffer);
            esp_audio_pause(player);
            esp_dlna_notify_avt_by_action(dlna_handle, "TransportState");
            return 0;
        case AVT_NEXT:
        case AVT_PREV:
            esp_audio_stop(player, TERMINATION_TYPE_NOW);
            return 0;
        case AVT_SEEK:
            sscanf(buffer, "%d:%d:%d", &hour, &min, &sec);
            tmp_data = hour * 3600 + min * 60 + sec;
            ESP_LOGI(TAG, "Seekto %d s", tmp_data);
            esp_audio_seek(player, tmp_data);
            return 0;
        case AVT_SET_TRACK_URI:
            ESP_LOGI(TAG, "SetAVTransportURI=%s", buffer);
            esp_audio_state_t state_t = { 0 };
            esp_audio_state_get(player, &state_t);
            if ((track_uri != NULL) && (state_t.status == AUDIO_STATUS_RUNNING) && strcasecmp(track_uri, buffer)) {
                esp_audio_stop(player, TERMINATION_TYPE_NOW);
                esp_dlna_notify(dlna, "AVTransport");
            }
            free(track_uri);
            track_uri = NULL;
            if (track_uri == NULL) {
                asprintf(&track_uri, "%s", buffer);
            }
            return 0;
        case AVT_SET_TRACK_METADATA:
            ESP_LOGD(TAG, "CurrentURIMetaData=%s", buffer);
            return 0;
        case AVT_GET_TRACK_URI:
            if (track_uri != NULL) {
                return snprintf(buffer, max_buffer_len, "%s", track_uri);
            } else {
                return 0;
            }
        case AVT_GET_PLAY_SPEED:    /* ["1"] */
            return snprintf(buffer, max_buffer_len, "%d", 1);
        case AVT_GET_PLAY_MODE:
            return snprintf(buffer, max_buffer_len, "NORMAL");
        case AVT_GET_TRANS_STATUS:  /* ["ERROR_OCCURRED", "OK"] */
            return snprintf(buffer, max_buffer_len, "OK");
        case AVT_GET_TRANS_STATE:   /* ["STOPPED", "PAUSED_PLAYBACK", "TRANSITIONING", "NO_MEDIA_PRESENT"] */
            ESP_LOGI(TAG, "_avt_get_trans_state %s", trans_state);
            return snprintf(buffer, max_buffer_len, trans_state);
        case AVT_GET_TRACK_DURATION:
        case AVT_GET_MEDIA_DURATION:
            esp_audio_duration_get(player, &tmp_data);
            tmp_data /= 1000;
            buffer_len = snprintf(buffer, max_buffer_len, "%02d:%02d:%02d", tmp_data / 3600, tmp_data / 60, tmp_data % 60);
            ESP_LOGD(TAG, "_avt_get_duration %s", buffer);
            return buffer_len;
        case AVT_GET_TRACK_NO:
            return snprintf(buffer, max_buffer_len, "%d", 1);
        case AVT_GET_TRACK_METADATA:
            return 0;
        // case AVT_GET_POS_ABSTIME:
        case AVT_GET_POS_RELTIME:
            esp_audio_time_get(player, &tmp_data);
            tmp_data /= 1000;
            buffer_len = snprintf(buffer, max_buffer_len, "%02d:%02d:%02d", tmp_data / 3600, tmp_data / 60, tmp_data % 60);
            ESP_LOGD(TAG, "_avt_get_time %s", buffer);
            return buffer_len;
        // case AVT_GET_POS_ABSCOUNT:
        case AVT_GET_POS_RELCOUNT:
            esp_audio_pos_get(player, &tmp_data);
            buffer_len = snprintf(buffer, max_buffer_len, "%d", tmp_data);
            ESP_LOGD(TAG, "_avt_get_pos %s", buffer);
            return buffer_len;
    }
    return 0;
}

static int _http_stream_event_handle(http_stream_event_msg_t *msg)
{
    if (msg->event_id == HTTP_STREAM_RESOLVE_ALL_TRACKS) {
        return ESP_OK;
    }
    if (msg->event_id == HTTP_STREAM_FINISH_TRACK) {
        return http_stream_next_track(msg->el);
    }
    if (msg->event_id == HTTP_STREAM_FINISH_PLAYLIST) {
        return http_stream_restart(msg->el);
    }
    return ESP_OK;
}

static void audio_player_init(void)
{
    audio_board_handle_t board_handle = audio_board_init();
    audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START);

    esp_audio_cfg_t cfg = DEFAULT_ESP_AUDIO_CONFIG();
    cfg.vol_handle = board_handle->audio_hal;
    cfg.vol_set = (audio_volume_set)audio_hal_set_volume;
    cfg.vol_get = (audio_volume_get)audio_hal_get_volume;
    cfg.prefer_type = ESP_AUDIO_PREFER_MEM;
    cfg.resample_rate = 48000;
    player = esp_audio_create(&cfg);

    // Create readers and add to esp_audio
    http_stream_cfg_t http_cfg = HTTP_STREAM_CFG_DEFAULT();
    http_cfg.event_handle = _http_stream_event_handle;
    http_cfg.type = AUDIO_STREAM_READER;
    http_cfg.enable_playlist_parser = true;
    http_cfg.task_prio = 12;
    http_cfg.stack_in_ext = true;
    audio_element_handle_t http_stream_reader = http_stream_init(&http_cfg);
    esp_audio_input_stream_add(player, http_stream_reader);

    audio_decoder_t auto_decode[] = {
        DEFAULT_ESP_MP3_DECODER_CONFIG(),
        DEFAULT_ESP_WAV_DECODER_CONFIG(),
        DEFAULT_ESP_AAC_DECODER_CONFIG(),
        DEFAULT_ESP_M4A_DECODER_CONFIG(),
        DEFAULT_ESP_TS_DECODER_CONFIG(),
    };
    esp_decoder_cfg_t auto_dec_cfg = DEFAULT_ESP_DECODER_CONFIG();
    esp_audio_codec_lib_add(player, AUDIO_CODEC_TYPE_DECODER, esp_decoder_init(&auto_dec_cfg, auto_decode, 5));

    // Create writers and add to esp_audio
    i2s_stream_cfg_t i2s_writer = I2S_STREAM_CFG_DEFAULT();
    i2s_writer.type = AUDIO_STREAM_WRITER;
    i2s_writer.stack_in_ext = true;
    i2s_writer.task_core = 1;
    audio_element_handle_t i2s_stream_writer = i2s_stream_init(&i2s_writer);
    i2s_stream_set_clk(i2s_stream_writer, 48000, 16, 2);
    esp_audio_output_stream_add(player, i2s_stream_writer);

    // Set default volume
    esp_audio_vol_set(player, 35);
}

static void start_dlna()
{
    const ssdp_service_t ssdp_service[] = {
        { DLNA_DEVICE_UUID, "upnp:rootdevice",                                  NULL },
        { DLNA_DEVICE_UUID, "urn:schemas-upnp-org:device:MediaRenderer:1",      NULL },
        { DLNA_DEVICE_UUID, "urn:schemas-upnp-org:service:ConnectionManager:1", NULL },
        { DLNA_DEVICE_UUID, "urn:schemas-upnp-org:service:RenderingControl:1",  NULL },
        { DLNA_DEVICE_UUID, "urn:schemas-upnp-org:service:AVTransport:1",       NULL },
        { NULL, NULL, NULL },
    };

    ssdp_config_t ssdp_config = SSDP_DEFAULT_CONFIG();
    ssdp_config.udn = DLNA_UNIQUE_DEVICE_NAME;
    ssdp_config.location = "http://${ip}"DLNA_ROOT_PATH;
    esp_ssdp_start(&ssdp_config, ssdp_service);

    static httpd_handle_t httpd = NULL;
    httpd_config_t httpd_config = HTTPD_DEFAULT_CONFIG();
    httpd_config.max_uri_handlers = 25;
    httpd_config.stack_size = 6 * 1024;
    if (httpd_start(&httpd, &httpd_config) != ESP_OK) {
        ESP_LOGI(TAG, "Error starting httpd");
    }

    extern const uint8_t logo_png_start[] asm("_binary_logo_png_start");
    extern const uint8_t logo_png_end[] asm("_binary_logo_png_end");

    dlna_config_t dlna_config = {
        .friendly_name = "ESP32 MD (ESP32 Renderer)",
        .uuid = (const char *)DLNA_DEVICE_UUID,
        .logo               = {
            .mime_type  = "image/png",
            .data       = (const char *)logo_png_start,
            .size       = logo_png_end - logo_png_start,
        },
        .httpd          = httpd,
        .httpd_port     = httpd_config.server_port,
        .renderer_req   = renderer_request,
        .root_path      = DLNA_ROOT_PATH,
        .device_list    = false
    };

    dlna_handle = esp_dlna_start(&dlna_config);

    ESP_LOGI(TAG, "DLNA Started...");
}

void app_main()
{
    esp_log_level_set("*", ESP_LOG_INFO);
    esp_log_level_set(TAG, ESP_LOG_INFO);
    esp_log_level_set("AUDIO_ELEMENT", ESP_LOG_WARN);
    esp_log_level_set("AUDIO_PIPELINE", ESP_LOG_ERROR);
    esp_log_level_set("ESP_AUDIO_CTRL", ESP_LOG_WARN);
    media_lib_add_default_adapter();
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0))
    ESP_ERROR_CHECK(esp_netif_init());
#else
    tcpip_adapter_init();
#endif

    esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
    esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);
    periph_wifi_cfg_t wifi_cfg = {
        .wifi_config.sta.ssid = "WiFi_8G",
        .wifi_config.sta.password = "IIll1122",
    };
    esp_periph_handle_t wifi_handle = periph_wifi_init(&wifi_cfg);
    esp_periph_start(set, wifi_handle);
    periph_wifi_wait_for_connected(wifi_handle, portMAX_DELAY);

    audio_player_init();
    esp_audio_callback_set(player, player_event_handler, NULL);

    start_dlna();
}

下载地址:我的下载_个人中心- - 电子工程世界EEWORLD

五、作品功能演示视频

因为需要安卓手机的酷狗音乐app,我使用的是苹果的,手上暂时,后续补上

 

六、项目总结

通过本项目,我们构建了一个基于esp32wifi音频传输模块使有线音响赋值无限“意义”。

本帖最后由 rertet 于 2024-11-1 09:02 编辑

回复评论 (1)

使用的是什么协议进行通信啊?  

在爱好的道路上不断前进,在生活的迷雾中播撒光引
点赞  2024-11-1 11:06
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复