# esp32_a2dp_source_demo **Repository Path**: ming_home/esp32_a2dp_source_demo ## Basic Information - **Project Name**: esp32_a2dp_source_demo - **Description**: ESP32 经典蓝牙 A2DP source 演示:扫描周边音频设备(音箱/耳机),通过串口命令选择并连接,解码 SPIFFS 中的 MP3 并推送到音箱播放 - **Primary Language**: C - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-22 - **Last Updated**: 2026-08-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # esp32-A2DP-source ESP32 经典蓝牙 **A2DP source** 演示:扫描周边音频设备(音箱/耳机),通过串口命令选择并连接,解码 SPIFFS 中的 MP3 并推送到音箱播放。 基于 [esp-idf v5.2.6 官方 a2dp_source 例程](https://github.com/espressif/esp-idf/tree/v5.2.6/examples/bluetooth/bluedroid/classic_bt/a2dp_source) 改造: - 蓝牙名固定为 `esp32-A2DP-source` - 去掉"自动发现指定名称设备 + 自动连接",改为**串口命令交互** - MP3 文件(`audio/on-the-way.mp3`)构建期打包进 SPIFFS,Helix 定点解码后经 A2DP 输出 - 音频数据链路:SPIFFS → Helix 解码 → PCM StreamBuffer → A2DP 回调 → SBC 编码 → 音箱 > 系统架构、数据流、线程模型与设计决策详见 [`docs/system-design.md`](docs/system-design.md) ## 硬件与环境 | 项目 | 说明 | |------|------| | 目标芯片 | ESP32 原版(经典蓝牙 BR/EDR;**C3/S3 无经典蓝牙不可用**) | | ESP-IDF | v5.2.6 | | Flash | **8MB** / DIO | | 分区表 | `partitions.csv`:factory app 1.5MB + storage(SPIFFS) 6.44MB,无 OTA | | 串口 | UART0(GPIO1=TX / GPIO3=RX),115200 | ## 构建 / 烧录 ```powershell # IDF v5.2.6 环境导出后: Copy-Item sdkconfig-debug-logOn sdkconfig # 或 sdkconfig-release idf.py set-target esp32 # 仅首次 idf.py build idf.py -p COM flash monitor # 音频镜像随 flash 一并写入 ``` > 注意:`Copy-Item` 会保留源文件时间戳,切换变体后若构建产物没变化, > 手动刷新时间戳或先 `idf.py reconfigure`。 换歌方法:把新 MP3 放入 `audio/` 目录(可多个,播放时取第一个 .mp3),重新 `idf.py build flash`。 ## 串口命令 以 `\r\n` 结尾(串口助手发送勾选"发送新行",monitor 中直接回车即可)。 上电启动后串口会自动打印一次用法说明(同 `HELP` 命令输出): | 命令 | 功能 | |------|------| | `BT SCAN` | 开始搜索(约 13 秒),实时打印 `[序号] 名称 地址` | | `BT CONNECT ` | 连接扫描列表中的第 idx 个设备 | | `BT PLAY` | 开始播放 MP3(需已连接音箱) | | `BT STOP` | 停止播放 | | `BT DISCONNECT` | 断开当前 A2DP 链路 | | `HELP` | 打印全部命令用法 | > 除命令响应外,固件每 3s 自动输出一行 `CPU usage: xx%`(双核合并占用率,基于 FreeRTOS 运行时统计)。 ### 典型操作流程 ``` BT SCAN ← 搜索 [0] 小米音箱-XXXX xx:xx:xx:xx:xx:xx Scan done, 1 device(s). BT CONNECT 0 ← 连接音箱 a2dp connected BT PLAY ← 开始放歌 playback started BT STOP ← 停止 ``` ## 目录结构 ``` ├── main/ │ ├── main.c # A2DP source 状态机 + 串口命令处理 │ ├── bt_app_core.c/h # 官方蓝牙任务分发框架(未改动) │ ├── mp3_player.c/h # SPIFFS 挂载 + Helix 解码任务 + PCM 流缓冲 │ ├── CMakeLists.txt # 含 spiffs_create_partition_image(音频打包) │ └── idf_component.yml# 引入 esp-libhelix-mp3 托管组件 ├── audio/ │ └── on-the-way.mp3 # 构建期打包进 SPIFFS 的音频 ├── components/ │ ├── cmd_handler/ # UART 命令拆包与分发(自研库) │ ├── uart_mgr/ # UART 驱动封装(自研库) │ └── esp32_misc/ # CPU 占用率测量等杂项(自研库) ├── partitions.csv # factory 1.5MB + storage 6.44MB ├── sdkconfig-release # 日志全关 └── sdkconfig-debug-logOn# 日志 INFO ``` 两份 sdkconfig 仅日志级别不同;均已启用 Bluedroid + Classic BT + A2DP(BR/EDR only,BLE 内存已释放)+ FreeRTOS 运行时统计(CPU 占用率测量依赖)。 MP3 解码使用托管组件 [`chmorgan/esp-libhelix-mp3`](https://components.espressif.com/components/chmorgan/esp-libhelix-mp3)(首次 reconfigure 时自动下载到 `managed_components/`)。 ## 已知限制 - 固件约 0.97~1.0MB(app 槽 1.5MB,余量约 35%) - 播放固定取 SPIFFS 中第一个 .mp3,播完即静音,需再次 `BT PLAY` 重播;不支持暂停续播、不支持多曲目切换 - 日志与命令响应共用 UART0,输出可能交错 - 对端需为 A2DP sink(音箱/耳机);部分设备配对时需在手机端确认