ESP32程序:

由官方usb程序修改而来

examples\peripherals\usb\host\msc\main\msc_example_main.c

使用了 arduino 库

[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
board_upload.psram_size = 8MB
board_upload.flash_size = 16MB
board_build.arduino.partitions = default_16MB.csv
board_build.arduino.memory_type = qio_opi
build_flags = -DBOARD_HAS_PSRAM
	-mfix-esp32-psram-cache-issue
monitor_speed = 115200
lib_deps = 
	crankyoldgit/IRremoteESP8266@^2.8.1
	firechip/usb-host-msc@1.1.4
	adafruit/Adafruit SleepyDog Library@^1.7.2
    esphome/ESP32-audioI2S@^2.3.0

这里使用 ESP32-audioI2S 发现不支持 c语言FILE *文件,所以在 Audip.cpp 开始

加了一些 :

uint32_t audiofilesize = 0;
String audiofilename;
FILE *audiofile;
bool audiofileseek1(int m_resumeFilePos)
{
    return fseek(audiofile, m_resumeFilePos, SEEK_SET) == 0;
}
uint8_t audiofileread()
{
    return fgetc(audiofile);
}
size_t audiofilereadBytes(char *buf, size_t len)
{
    return fread(buf, 1, len, audiofile);
}
size_t audiofileread(void *buf, size_t len)
{
    return fread(buf, 1, len, audiofile);
}

long audiofileposition()
{
    if (audiofile == nullptr)
        return -1;
    return ftell(audiofile);
}
size_t get_file_size(FILE *f8)
{
    if (f8 == nullptr)
        return 0;

    // 保存当前指针位置
    long current_pos = ftell(f8);
    if (current_pos == -1)
        return 0;

    // 移动到文件末尾获取大小
    fseek(f8, 0, SEEK_END);
    size_t size = ftell(f8);

    // 恢复指针位置
    fseek(f8, current_pos, SEEK_SET);

    return size;
}

bool Audio::connecttoFS(const char *path, int32_t fileStartPos)
{

    if (!path)
    { // guard
        printProcessLog(AUDIOLOG_PATH_IS_NULL);
        return false;
    }

    xSemaphoreTake(mutex_playAudioData, portMAX_DELAY); // #3

    m_fileStartPos = fileStartPos;
    setDefaults(); // free buffers an set defaults

    char *audioPath = (char *)__malloc_heap_psram(strlen(path) + 2);
    if (!audioPath)
    {
        printProcessLog(AUDIOLOG_OUT_OF_MEMORY);
        xSemaphoreGive(mutex_playAudioData);
        return false;
    }
    if (path[0] == '/')
    {
        strcpy(audioPath, path);
    }
    else
    {
        audioPath[0] = '/';
        strcpy(audioPath + 1, path);
    }

    audiofile = fopen(audioPath, "rb"); // #86

    AUDIO_INFO("Reading file: \"%s\"", audioPath);

    if (!audiofile)
    {
        printProcessLog(AUDIOLOG_FILE_READ_ERR, audioPath);
        free(audioPath);
        xSemaphoreGive(mutex_playAudioData);
        return false;
    }

    setDatamode(AUDIO_LOCALFILE);
    m_fileSize = audiofilesize = get_file_size(audiofile); // TEST loop
    audiofilename = audioPath;
    char *afn = NULL; // audioFileName
    afn = strdup(audioPath);

    uint8_t dotPos = lastIndexOf(afn, ".");
    for (uint8_t i = dotPos + 1; i < strlen(afn); i++)
    {
        afn[i] = toLowerCase(afn[i]);
    }

    if (endsWith(afn, ".mp3"))
        m_codec = CODEC_MP3; // m_codec is by default CODEC_NONE
    if (endsWith(afn, ".m4a"))
        m_codec = CODEC_M4A;
    if (endsWith(afn, ".aac"))
        m_codec = CODEC_AAC;
    if (endsWith(afn, ".wav"))
        m_codec = CODEC_WAV;
    if (endsWith(afn, ".flac"))
        m_codec = CODEC_FLAC;
    if (endsWith(afn, ".opus"))
        m_codec = CODEC_OPUS;
    if (endsWith(afn, ".ogg"))
        m_codec = CODEC_OGG;
    if (endsWith(afn, ".oga"))
        m_codec = CODEC_OGG;

    if (m_codec == CODEC_NONE)
        AUDIO_INFO("The %s format is not supported", afn + dotPos);

    if (afn)
    {
        free(afn);
        afn = NULL;
    }
    free(audioPath);

    bool ret = initializeDecoder();
    if (ret)
        m_f_running = true;
    else
        fclose(audiofile);
    xSemaphoreGive(mutex_playAudioData);
    return ret;
}

凡是 出现FS 的方法 都替换 比如audiofile.size() 替换 为audiofilesize

audiofile.name() 替换为 audiofilename  

audiofile.close() 替换为 fclose(audioeile)

等等等..............................................

大神们,还有什么好办法吗

main.cpp 

#include <Arduino.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include "Audio.h"
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
#include "esp_random.h"
#include "bootloader_random.h"
#include "usb/usb_host.h"
#include "msc_host.h"
#include "msc_host_vfs.h"
#include <Adafruit_SleepyDog.h>
#include "esp_sleep.h"
// ===================== 配置定义 =====================
// 音频核心配置
#define AUDIO_TASK_STACK 16384   // 音频任务堆栈
#define AUDIO_TASK_PRIO 10       // 音频任务优先级
#define AUDIO_TASK_CORE 0        // 音频任务运行核心
#define AUDIO_BUFFER_SIZE 64     // 音频缓存大小
#define PSRAM_BUFFER_SIZE 524288 // PSRAM缓存大小
#define AUDIO_TASK_DELAY 5       // 音频任务延迟

// 硬件引脚定义
#define IR_RX 2     // 红外接收引脚
#define I2S_BCLK 8  // I2S时钟引脚
#define I2S_DOUT 17 // I2S数据输出引脚
#define I2S_LRC 18  // I2S左右声道引脚

// USB相关定义
#define MNT_PATH "/usb"                                 // USB挂载路径
const String LIST_CACHE_FILE = "/usb/audio_list.cache"; // 音频列表缓存文件

// 状态枚举(统一管理,避免重复定义)
enum UsbEvent
{
    USB_EVENT_DISCONNECTED = 0,
    USB_EVENT_CONNECTED = 1,
    USB_EVENT_QUIT = 2
};

enum MscEvent
{
    MSC_EVENT_CONNECTED = 0,
    MSC_EVENT_DISCONNECTED = 1
};

// ===================== 全局变量 =====================
IRrecv IRR(IR_RX);
decode_results results;
Audio audio;

// 音频相关
bool audioisMusicFinished = false;
String currentTrack = "";
int trackIndex = 0;
bool isPlaying = false;
std::vector<String> trackList;
int yl = 2; // 音量

// USB相关
bool usbMounted = false;
uint8_t usbaddress = 0;
uint8_t usb_event_id = 5; // USB事件标识:5=无事件,1=连接,0=断开,2=退出
msc_host_device_handle_t msc_device = NULL;
msc_host_vfs_handle_t vfs_handle = NULL;
bool dev_present = false;
unsigned long shutdown_ms = millis() + 1000 * 60 * 10;
// ===================== USB事件回调 =====================
static void msc_event_cb(const msc_host_event_t *event, void *arg)
{
    if (event->event == MSC_EVENT_CONNECTED)
    {
        Serial.println("MSC device connected");
        usbaddress = event->device.address;
        usb_event_id = USB_EVENT_CONNECTED; // 标记USB连接事件
    }
    else if (event->event == MSC_EVENT_DISCONNECTED)
    {
        Serial.println("MSC device disconnected");
        usb_event_id = USB_EVENT_DISCONNECTED; // 标记USB断开事件
    }
}

// ===================== USB任务 =====================
static void usb_task(void *args)
{
    printf("usb_task running on core: %d\n", xPortGetCoreID());

    // 初始化USB主机
    const usb_host_config_t host_config = {.intr_flags = ESP_INTR_FLAG_LEVEL1};
    ESP_ERROR_CHECK(usb_host_install(&host_config));

    // 初始化MSC主机驱动
    const msc_host_driver_config_t msc_config = {
        .create_backround_task = true,
        .task_priority = 1,
        .stack_size = 4096,
        .core_id = 1,
        .callback = msc_event_cb,
    };
    ESP_ERROR_CHECK(msc_host_install(&msc_config));

    bool has_clients = true;
    while (true)
    {
        uint32_t event_flags;
        usb_host_lib_handle_events(portMAX_DELAY, &event_flags);

        // 释放无客户端的设备
        if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
        {
            has_clients = false;
            if (usb_host_device_free_all() == ESP_OK)
                break;
        }

        // 所有设备释放完成且无客户端时退出
        if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE && !has_clients)
            break;

        vTaskDelay(100);
    }

    vTaskDelay(10); // 给客户端卸载时间
    Serial.println("Deinitializing USB");
    ESP_ERROR_CHECK(usb_host_uninstall());
    vTaskDelete(NULL);
}

// ===================== USB初始化 =====================
void usbinit()
{
    printf("usbinit running on core: %d\n", xPortGetCoreID());

    // 创建USB任务
    BaseType_t task_created = xTaskCreatePinnedToCore(
        usb_task, "usb_task", 4096, NULL, 2, NULL, 1);
    assert(task_created);

    // 等待USB设备连接(初始化阶段)
    int js = 0;
    Serial.println("Waiting for USB flash drive to be connected");
    while (1)
    {
        // 处理USB连接事件
        if (usb_event_id == USB_EVENT_CONNECTED)
        {
            usb_event_id = 5; // 重置事件标识
            if (!dev_present)
            {
                dev_present = true;
                // 挂载USB设备
                ESP_ERROR_CHECK(msc_host_install_device(usbaddress, &msc_device));
                const esp_vfs_fat_mount_config_t mount_config = {
                    .format_if_mount_failed = false,
                    .max_files = 3,
                    .allocation_unit_size = 8192,
                };
                ESP_ERROR_CHECK(msc_host_vfs_register(msc_device, MNT_PATH, &mount_config, &vfs_handle));
                usbMounted = true;
                Serial.println("USB flash drive mounted successfully");
                break;
            }
        }

        // 超时退出(避免无限等待)
        if (js++ > 10)
            break;
        vTaskDelay(500);
    }
}

// ===================== 工具函数 =====================
// 生成指定范围随机数
uint32_t get_random_in_range(uint32_t min, uint32_t max)
{
    return min + (esp_random() % (max - min + 1));
}

// 过滤音频文件
bool isAudioFile(const String &fileName)
{
    int dotIndex = fileName.lastIndexOf('.');
    if (dotIndex == -1)
        return false;

    String ext = fileName.substring(dotIndex + 1);
    ext.toLowerCase();
    return (ext == "mp3" || ext == "wav" || ext == "flac");
}

// 遍历目录
bool traverseDirectory(const String &dirPath, bool (*callback)(const String &, bool))
{
    DIR *dir = opendir(dirPath.c_str());
    if (!dir)
    {
        Serial.printf("打开目录失败:%s\n", dirPath.c_str());
        return false;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL)
    {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        String fullPath = dirPath + "/" + entry->d_name;
        if (fullPath.startsWith("//"))
            fullPath = fullPath.substring(1);

        bool isDir = (entry->d_type == DT_DIR);
        if (!callback(fullPath, isDir))
        {
            closedir(dir);
            return false;
        }
    }

    closedir(dir);
    return true;
}

// 保存音频列表到缓存
bool saveAudioListToCache()
{
    if (!usbMounted)
        return false;

    FILE *cacheFile = fopen(LIST_CACHE_FILE.c_str(), "w");
    if (!cacheFile)
    {
        Serial.printf("创建缓存文件失败:%s\n", LIST_CACHE_FILE.c_str());
        return false;
    }

    fprintf(cacheFile, "%d\n", trackList.size());
    for (size_t i = 0; i < trackList.size(); i++)
    {
        String fileName = trackList[i];
        if (!fileName.startsWith("/"))
            fileName = "/" + fileName;
        fprintf(cacheFile, "%s\n", fileName.c_str());
    }

    fclose(cacheFile);
    Serial.printf("音频列表已缓存到USB,共%d个文件\n", trackList.size());
    return true;
}

// 从缓存加载音频列表
bool loadAudioListFromCache()
{
    if (!usbMounted)
        return false;

    FILE *cacheFile = fopen(LIST_CACHE_FILE.c_str(), "r");
    if (!cacheFile)
    {
        Serial.println("缓存文件不存在,需要扫描USB");
        return false;
    }

    trackList.clear();
    int fileCount = 0;
    if (fscanf(cacheFile, "%d\n", &fileCount) != 1)
    {
        Serial.println("读取缓存文件数量失败");
        fclose(cacheFile);
        return false;
    }

    char buf[256];
    for (int i = 0; i < fileCount; i++)
    {
        if (fgets(buf, sizeof(buf), cacheFile) == NULL)
            break;

        String fileName = String(buf);
        fileName.trim();
        if (fileName.length() > 0)
        {
            if (!fileName.startsWith("/"))
                fileName = "/" + fileName;
            trackList.push_back(fileName);
        }
    }

    fclose(cacheFile);
    Serial.printf("从USB缓存加载音频列表,共%d个文件\n", trackList.size());
    return true;
}

// 扫描音频文件(优先加载缓存)
void scanAudioFiles()
{
    if (!usbMounted)
    {
        trackList.clear();
        return;
    }

    // 先尝试加载缓存
    if (loadAudioListFromCache())
        return;

    // 缓存加载失败,扫描USB
    trackList.clear();
    traverseDirectory("/usb", [](const String &fullPath, bool isDir) -> bool
                      {
        if (!isDir && isAudioFile(fullPath))
        {
            String fileName = fullPath;
            if (!fileName.startsWith("/")) fileName = "/" + fileName;
            trackList.push_back(fileName);
            Serial.printf("发现音频文件:%s\n", fileName.c_str());
        }
        return true; });

    saveAudioListToCache();
    Serial.printf("扫描USB完成,共%d个音频文件\n", trackList.size());
}

// 检查文件是否存在
bool fileExists(const String &path)
{
    struct stat st;
    return (stat(path.c_str(), &st) == 0);
}

// ===================== 音频播放控制 =====================
bool playAudioFile(const String &filePath)
{
    if (!usbMounted || trackList.empty())
        return false;

    String realFilePath = filePath;
    if (!realFilePath.startsWith("/"))
        realFilePath = "/" + realFilePath;

    if (!fileExists(realFilePath))
    {
        Serial.printf("文件不存在:%s\n", realFilePath.c_str());
        return false;
    }

    // 停止当前播放
    audio.stopSong();
    delay(200);

    // 连接并播放文件
    bool result = audio.connecttoFS(realFilePath.c_str());
    isPlaying = result;
    currentTrack = realFilePath;

    return result;
}

// 随机播放
void sjbf()
{
    if (!trackList.empty())
    {
        int sjs = get_random_in_range(0, trackList.size() - 1);
        playAudioFile(trackList[sjs]);
    }
    else
    {
        isPlaying = false;
        currentTrack = "";
    }
}

// 音量增加
void yljia()
{
    if (yl < 21)
        yl++;
    audio.setVolume(yl);
    Serial.printf("音量:%d\n", yl);
}

// 音量减少
void yljian()
{
    if (yl > 0)
        yl--;
    audio.setVolume(yl);
    Serial.printf("音量:%d\n", yl);
}

// ===================== 红外遥控处理 =====================
void dogogo()
{
    if (IRR.decode(&results))
    {
        serialPrintUint64(results.value, HEX);
        Serial.println();

        switch (results.value)
        {
        case 0xFFB04F: // 重启
            esp_restart();
            break;

        case 0xFF02FD: // 播放指定歌曲
            audio.connecttoFS("/usb/赵静-故乡情.mp3");
            break;

        case 0xFF18E7: // 音量+
            yljia();
            break;

        case 0xFF4AB5: // 音量-
            yljian();
            break;

        case 0xFF5AA5: // 下一曲/随机播放
            sjbf();
            break;

        // 其他未使用的按键,保留但不处理
        case 0xFFE21D:
        case 0xFFA25D:
        case 0xFF6897:
        case 0xFF38C7:
            audio.pauseResume();
            break;
        case 0xFF10EF:
        case 0xFF9867:
            break;
        }

        IRR.resume();
    }
}

// ===================== 音频任务 =====================
void audioTask(void *args)
{
    printf("audioTask running on core: %d\n", xPortGetCoreID());

    // 音频初始化
    audio.setBufsize(AUDIO_BUFFER_SIZE, PSRAM_BUFFER_SIZE);
    audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
    audio.setVolume(yl);
    // audio.setCallback(audio_info, audio_eof_mp3); // 注册回调(修复播放完成检测)

    while (1)
    {
        audio.loop();

        // 播放完成自动切换
        if (isPlaying && audioisMusicFinished)
        {
            audioisMusicFinished = false;
            Serial.println("当前曲目播放完成,自动切换下一曲");
            sjbf();
        }

        Watchdog.reset();
        portYIELD();
    }
}

// ===================== 热插拔处理函数 =====================
void handleUsbHotplug()
{
    if (!audio.isRunning())
    {

        if (millis() > shutdown_ms)

            esp_deep_sleep_start();
    }
    else
        shutdown_ms = millis() + 1000 * 60 * 10;

    // 处理USB连接事件
    if (usb_event_id == USB_EVENT_CONNECTED)
    {
        usb_event_id = 5; // 重置事件标识

        if (!dev_present)
        {
            dev_present = true;
            // 挂载新连接的USB设备
            ESP_ERROR_CHECK(msc_host_install_device(usbaddress, &msc_device));
            const esp_vfs_fat_mount_config_t mount_config = {
                .format_if_mount_failed = false,
                .max_files = 3,
                .allocation_unit_size = 8192,
            };
            ESP_ERROR_CHECK(msc_host_vfs_register(msc_device, MNT_PATH, &mount_config, &vfs_handle));
            usbMounted = true;

            Serial.println("USB device mounted (hotplug)");
            // 重新扫描音频文件
            scanAudioFiles();
            // 如果当前没有播放,自动开始播放
            if (!isPlaying && !trackList.empty())
            {
                sjbf();
            }
        }
    }

    // 处理USB断开事件
    else if (usb_event_id == USB_EVENT_DISCONNECTED)
    {
        usb_event_id = 5; // 重置事件标识

        if (dev_present)
        {
            dev_present = false;
            usbMounted = false;

            // 停止当前播放
            if (isPlaying)
            {
                audio.stopSong();
                isPlaying = false;
                currentTrack = "";
            }

            // 卸载USB设备
            if (vfs_handle)
            {
                ESP_ERROR_CHECK(msc_host_vfs_unregister(vfs_handle));
                vfs_handle = NULL;
            }
            if (msc_device)
            {
                ESP_ERROR_CHECK(msc_host_uninstall_device(msc_device));
                msc_device = NULL;
            }

            // 清空曲目列表
            trackList.clear();
            Serial.println("USB device unmounted (hotplug)");
        }
    }

    // 处理退出事件
    else if (usb_event_id == USB_EVENT_QUIT)
    {
        usb_event_id = 5;
        ESP_ERROR_CHECK(msc_host_uninstall());
    }
}

// ===================== 回调函数 =====================
// optional
void audio_info(const char *info)
{
    Serial.print("info        ");
    Serial.println(info);
}
void audio_id3data(const char *info)
{ // id3 metadata
    Serial.print("id3data     ");
    Serial.println(info);
}
void audio_eof_mp3(const char *info)
{ // end of file
    Serial.print("eof_mp3     ");
    Serial.println(info);
    audioisMusicFinished = true;
}
void audio_showstation(const char *info)
{
    Serial.print("station     ");
    Serial.println(info);
}
void audio_showstreamtitle(const char *info)
{
    Serial.print("streamtitle ");
    Serial.println(info);
}
void audio_bitrate(const char *info)
{
    Serial.print("bitrate     ");
    Serial.println(info);
}
void audio_commercial(const char *info)
{ // duration in sec
    Serial.print("commercial  ");
    Serial.println(info);
}
void audio_icyurl(const char *info)
{ // homepage
    Serial.print("icyurl      ");
    Serial.println(info);
}
void audio_lasthost(const char *info)
{ // stream URL played
    Serial.print("lasthost    ");
    Serial.println(info);
}

// ===================== 初始化 =====================
void setup()
{
    delay(500);
    Serial.begin(115200);
    while (!Serial)
        delay(10);

    // 初始化红外接收
    IRR.enableIRIn();
    printf("setup running on core: %d\n", xPortGetCoreID());
    delay(100);

    // 初始化USB
    usbinit();
    Serial.println("\n===== usb启动 =====");
    delay(500);

    // 创建音频任务
    Serial.println("\n===== ESP32 音频播放器启动 =====");
    BaseType_t audio_task = xTaskCreatePinnedToCore(
        audioTask, "AudioTask", AUDIO_TASK_STACK,
        NULL, AUDIO_TASK_PRIO, NULL, AUDIO_TASK_CORE);
    assert(audio_task);
    Serial.printf("音频任务已启动(核心0)\n");

    // 扫描音频文件
    scanAudioFiles();
    Serial.println("\n===== 播放列表加载完毕 =====");

    // 开始播放
    sjbf();

    // 启用看门狗
    int countdownMS = Watchdog.enable(10000);
    Serial.print("Enabled the watchdog with max countdown of ");
    Serial.print(countdownMS, DEC);
    Serial.println(" milliseconds!");
    Watchdog.reset();
    // esp_sleep_enable_ext0_wakeup(GPIO_NUM_2, 0);
}

// ===================== 主循环 =====================
void loop()
{
    Watchdog.reset();

    // 处理红外遥控
    dogogo();

    // 处理USB热插拔(核心修复)
    handleUsbHotplug();

    delay(100);
}

更多推荐