尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于ESP32与蓝牙AVRCP协议打造Mac音乐播放物理显示器

基于ESP32与蓝牙AVRCP协议打造Mac音乐播放物理显示器 1. 项目缘起当Mac的“正在播放”需要一个物理窗口如果你和我一样是个重度音乐爱好者同时桌面又摆满了各种开发板那你可能也动过这个念头能不能让Mac屏幕角落那个小小的“正在播放”控件变成一个独立的、酷炫的物理设备摆在桌面上每次切歌、调音量都得把鼠标挪过去点一下或者用键盘快捷键总觉得少了点仪式感。尤其是当你在全屏写代码或者看文档时突然想看一眼现在播的是什么歌还得切出窗口体验就被打断了。这个想法其实并不新鲜网上早就有大神用树莓派加个小屏幕做过类似的东西。但树莓派对于这个简单的任务来说有点“杀鸡用牛刀”了功耗、体积和成本都偏高。直到我手边闲置的ESP32开发板和一块SPI接口的OLED屏幕进入了我的视线。ESP32这块集成了Wi-Fi和蓝牙的双核芯片性能对于处理音频信息流和驱动屏幕绰绰有余关键是它功耗极低可以一直插着USB供电几乎不发热。于是一个清晰的目标就形成了用ESP32和一块小显示屏打造一个专属于Mac的“Now Playing”物理显示设备。它需要能实时显示来自Mac的音乐播放信息包括歌曲名、艺术家、专辑封面当然还有播放状态和进度条。这不仅仅是把软件功能硬件化更是为数字生活增添一点实体的趣味和便捷。整个项目的核心就在于如何让ESP32与macOS系统建立起稳定、低延迟的通信桥梁并优雅地将信息呈现出来。2. 技术选型与核心组件解析要实现这个目标我们需要拆解几个关键部分硬件核心、与Mac通信的协议、以及信息显示的方案。每一环的选择都直接影响到最终项目的稳定性、易用性和美观度。2.1 为什么是ESP32不仅仅是性价比首先说说主控芯片ESP32。选择它绝非仅仅因为便宜或手头有存货。对于这个项目ESP32有几个不可替代的优势双模蓝牙是关键ESP32内置了经典的蓝牙Bluetooth Classic和低功耗蓝牙BLE。与Mac通信我们有两种主流协议可选AirPlay和蓝牙音频A2DP。虽然AirPlay音质更好但协议复杂在ESP32上实现完整的接收解码器如Shairport Sync资源开销大且延迟较高。而蓝牙A2DP是更通用、更轻量的选择。ESP32可以作为一个蓝牙音频接收器A2DP Sink直接从Mac接收音频流。更重要的是在A2DP协议中有一个叫做“AVRCP”音频/视频远程控制配置文件的子协议它专门用于传输元数据歌曲名、艺术家等和控制命令播放/暂停。这正是我们获取“Now Playing”信息最直接的通道。充足的性能与内存ESP32的双核处理器和几百KB的RAM足以同时运行蓝牙协议栈、解析AVRCP元数据、并通过SPI或I2C驱动显示屏。特别是当我们需要解码并显示专辑封面可能需要先下载再解码JPEG或PNG时一定的内存和处理器性能是必要的。完善的开发生态无论是乐鑫官方的ESP-IDF框架还是基于Arduino的核心对蓝牙A2DP/AVRCP的支持都已经非常成熟。有现成的库如ESP32-A2DP库可以让我们快速搭建起音频接收和元数据获取的骨架省去了从零实现协议栈的噩梦。2.2 显示屏选型清晰度、接口与刷新率的权衡显示屏是项目的“脸面”选择时需要考虑以下几点类型OLED有机发光二极管是我的首选。原因很简单它自发光黑色纯粹对比度极高显示深色背景下的白色文字非常清晰锐利而且功耗低。这对于主要显示文本信息的设备来说视觉体验远胜于需要背光的LCD。尺寸与分辨率常见的0.96寸或1.3寸OLED屏分辨率128x64或128x32足够显示歌曲名和艺术家。但如果想显示专辑封面哪怕是小缩略图也至少需要128x128或更高的分辨率如240x240。我最终选择了一块1.54寸的IPS LCD屏240x240因为它色彩更好适合展示封面虽然功耗比OLED高但在这个常供电的项目里可以接受。接口SPI接口是首选。相比I2CSPI的通信速率快得多在刷新整张专辑封面图片时速度优势非常明显能避免刷图时的明显延迟感。大部分SPI屏幕都支持“硬件SPI”可以占用更少的CPU资源。驱动库确保屏幕有成熟的驱动库支持例如TFT_eSPIArduino或lvglESP-IDF。TFT_eSPI库兼容性极广通过配置文件可以适配上百种屏幕是快速上手的好选择。2.3 通信协议抉择为什么是蓝牙AVRCP而非其他获取Mac上的播放信息理论上还有几种方法Apple官方API如AppleScript或Music App API这需要在本机运行一个脚本或服务然后通过网络如WebSocket、MQTT把信息发送给ESP32。这种方式最灵活能获取最丰富的元数据但依赖一个常驻的Mac后台程序增加了系统复杂性且跨用户或锁屏时可能失效。监听系统音频接口通过拦截系统音频流来分析频谱或识别歌曲技术难度大且无法获取元数据。蓝牙AVRCP这是最直接、最系统的方式。当你的Mac通过蓝牙将音频输出到ESP32即使你实际听歌用的是Mac自带扬声器或AirPods也可以单独设置元数据输出到蓝牙设备AVRCP协议就会自动将当前播放器的元数据推送给ESP32。它不依赖任何第三方软件是操作系统级别的支持稳定性最高。因此我的方案是将ESP32配置为一个蓝牙音频接收器A2DP Sink但不真正播放音频静音只专注于接收并解析AVRCP通道传来的元数据和控制事件。这样Mac上任何支持AVRCP的播放器Apple Music, Spotify, VLC等的信息都能被捕获。3. 硬件连接与基础环境搭建确定了核心方案我们就可以开始动手了。这部分会从最基础的硬件连线到软件开发环境的配置一步步搭建起项目的骨架。3.1 硬件接线清单与示意图你需要准备以下组件ESP32开发板一块如ESP32 DevKitC, NodeMCU-32S等。SPI显示屏一块以常见的ST7789驱动1.54寸屏为例。杜邦线若干母对母。Micro-USB数据线一根用于供电和编程。接线是项目的第一步务必仔细。SPI接口通常需要连接以下几条线ESP32引脚显示屏引脚作用3.3VVCC电源正极GNDGND电源地GPIO 18SCKSPI时钟线GPIO 23MOSI (SDA)SPI数据线主设备输出GPIO 5DC (AO)数据/命令选择线GPIO 4CS片选线低电平有效GPIO 2RST复位线可选可由软件控制注意GPIO编号是示例具体使用哪个引脚需要根据你选择的ESP32板型和屏幕库的配置来调整。VCC一定要接3.3V接5V会烧毁屏幕RST和CS引脚如果屏幕不提供可以在代码中用软件模拟但硬件连接更稳定。3.2 软件开发环境配置ESP-IDF vs Arduino你有两个主要的开发框架选择乐鑫官方的ESP-IDF和更易上手的Arduino for ESP32。ESP-IDF功能强大、控制精细、对蓝牙协议栈的支持最底层也最完整。如果你想深度定制蓝牙行为或追求极致的性能ESP-IDF是专业之选。但它的学习曲线较陡配置环境稍复杂。Arduino生态丰富库管理方便特别是对于驱动显示屏TFT_eSPI和处理蓝牙A2DPESP32-A2DP库有非常成熟的社区支持。它能让你快速实现功能把精力集中在应用逻辑而非底层驱动上。对于这个项目我强烈推荐使用Arduino框架。它的快速原型开发能力能让我们在几个小时内就看到效果避免在环境配置上耗费过多时间。以下是基于Arduino IDE的设置步骤安装Arduino IDE从官网下载并安装最新版。添加ESP32板支持打开文件-首选项在“附加开发板管理器网址”中输入https://espressif.github.io/arduino-esp32/package_esp32_index.json打开工具-开发板-开发板管理器搜索“esp32”安装“Espressif Systems”提供的包。安装必要的库ESP32-A2DP库用于蓝牙音频接收和AVRCP。在项目-加载库-管理库中搜索并安装。TFT_eSPI库用于驱动SPI显示屏。同样在库管理中安装。可选ArduinoJson库如果后续需要处理复杂的元数据或进行网络请求如下载封面这个库会非常有用。3.3 TFT_eSPI库的屏幕驱动配置这是让屏幕点亮的关键一步。TFT_eSPI库通过一个用户配置文件来适配不同屏幕。在Arduino的库安装目录下找到TFT_eSPI文件夹。进入该文件夹找到User_Setup.h这个文件。不要直接修改它。在同级目录下找一个与你屏幕驱动芯片最接近的例程配置文件如User_Setups/Setup24_ST7789.h将其复制到TFT_eSPI根目录并重命名为User_Setup_Select.h。打开User_Setup.h找到并取消注释删除行首的//这一行#include User_Setup_Select.h。现在打开你刚复制的User_Setup_Select.h文件。在这个文件里你需要取消注释启用你屏幕对应的驱动芯片定义比如找到#define ST7789_DRIVER这一行并取消注释。最关键的一步在同一文件中找到定义引脚的部分通常以// Define the pins used for the screen开头根据你实际的硬件接线修改TFT_CS,TFT_DC,TFT_RST,TFT_MOSI,TFT_SCLK等宏定义的GPIO编号使其与你的接线一致。完成这些后你可以运行一个简单的测试草图如TFT_eSPI库示例中的Hello_World来验证屏幕和接线是否正确。4. 核心实现蓝牙元数据获取与解析硬件和基础环境就绪后我们进入最核心的软件部分让ESP32“听懂”Mac通过蓝牙发来的音乐信息。4.1 初始化ESP32为蓝牙音频接收器我们将使用ESP32-A2DP库来完成大部分繁重的工作。首先在Arduino草图中包含必要的头文件并创建A2DP实例。#include BluetoothA2DPSink.h #include TFT_eSPI.h TFT_eSPI tft TFT_eSPI(); // 创建显示屏对象 BluetoothA2DPSink a2dp_sink; // 创建A2DP接收器对象 // 定义一个结构体来保存当前播放信息 struct NowPlayingInfo { String title; String artist; String album; uint8_t volume; bool is_playing; int32_t song_duration_ms; int32_t song_position_ms; } current_track; void setup() { Serial.begin(115200); tft.init(); tft.setRotation(1); // 根据屏幕方向调整0-3 tft.fillScreen(TFT_BLACK); tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(2); tft.setCursor(0, 0); tft.println(BT Ready...); // 配置A2DP接收器 i2s_pin_config_t pin_config { .bck_io_num 26, // I2S位时钟可任意未用GPIO .ws_io_num 25, // I2S字选择线 .data_out_num 22, // I2S数据输出我们不用但库需要 .data_in_num I2S_PIN_NO_CHANGE // 我们不需要音频输入 }; a2dp_sink.set_pin_config(pin_config); // 设置设备名称 a2dp_sink.set_device_name(ESP32 Now Playing); // 设置AVRCP元数据回调函数 a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback); // 设置播放状态回调函数 a2dp_sink.set_play_status_callback(play_status_callback); // 设置音量变化回调函数 a2dp_sink.set_volume_callback(volume_callback); // 启动A2DP接收器 a2dp_sink.start(ESP32 Now Playing); // 蓝牙设备可见名称 } void loop() { // 主循环处理显示刷新等任务 updateDisplay(); delay(100); // 降低刷新频率避免屏幕闪烁 }在setup()函数中我们初始化了屏幕并配置了A2DP接收器。关键点在于i2s_pin_config_t即使我们不播放音频库也需要一个有效的I2S引脚配置来初始化内部音频流。我们随便指定几个空闲的GPIO即可。set_device_name和start中的参数将作为蓝牙设备名被Mac搜索到。4.2 解码AVRCP回调获取歌曲信息与状态ESP32-A2DP库通过回调函数机制将蓝牙协议栈接收到的元数据和控制事件传递给我们。我们需要实现几个关键的回调函数。// AVRCP元数据回调函数 void avrc_metadata_callback(uint8_t data1, const uint8_t* data2) { Serial.printf(AVRC metadata rsp: attribute id 0x%x, %s\n, data1, data2); switch (data1) { case ESP_AVRC_MD_ATTR_TITLE: current_track.title String((char*)data2); break; case ESP_AVRC_MD_ATTR_ARTIST: current_track.artist String((char*)data2); break; case ESP_AVRC_MD_ATTR_ALBUM: current_track.album String((char*)data2); break; // 可以处理更多属性如流派(ESP_AVRC_MD_ATTR_GENRE)、音轨号等 } } // 播放状态回调函数 void play_status_callback(esp_avrc_playback_stat_t playback_stat) { Serial.printf(Playback status: %d\n, playback_stat); current_track.is_playing (playback_stat ESP_AVRC_PLAYBACK_PLAYING); } // 音量变化回调函数 void volume_callback(int32_t volume) { Serial.printf(Volume changed: %d\n, volume); current_track.volume volume; // 音量范围通常是0-127 } // 歌曲进度与时长回调需要额外设置 void avrc_psth_callback(uint8_t* data, uint32_t len) { // 这个回调用于接收更详细的控制信息如绝对位置 // 解析相对复杂初期可先不实现用轮询或简单估算进度 }avrc_metadata_callback是核心。当Mac端的播放器状态变化如切歌时它会多次被调用每次传递一个元数据属性IDdata1和对应的字符串值data2。我们根据ID将信息存储到current_track结构体中。重要提示AVRCP元数据的推送不是实时的也不是所有播放器都支持所有属性。例如Spotify可能只推送标题和艺术家而Apple Music可能推送更全。此外当歌曲暂停再播放时元数据可能不会重新推送。因此我们的代码需要能处理信息缺失和状态不一致的情况。4.3 处理数据不一致与状态同步问题在实际测试中你会发现元数据回调可能在某些情况下不触发或者播放状态回调滞后。为了构建一个健壮的系统我们需要一些策略状态缓存与超时在current_track结构体中增加时间戳字段记录每条信息的最后更新时间。在显示时如果某个信息如艺术家超过一定时间如10秒未更新则将其清空或显示为“Unknown”避免显示过时信息。轮询补充除了被动接收回调我们还可以在loop()中定期如每5秒主动向AVRCP目标即Mac请求当前状态和元数据。ESP32-A2DP库提供了avrc_ct_send_get_play_status_cmd()等函数但主动请求需要更深入的AVRCP命令知识实现起来较复杂。对于初版依赖回调并做好UI上的降级处理显示“--”是更简单的方案。连接状态管理监听蓝牙连接状态变化当断开连接时清空屏幕显示并提示“未连接”。5. 信息显示与用户界面设计获取到数据后如何在小屏幕上优雅地呈现出来是影响用户体验的最后一步。我们需要考虑布局、刷新效率和视觉反馈。5.1 屏幕布局规划与绘图函数对于一块240x240的方形屏幕我们可以这样规划布局顶部区域40像素高显示状态图标播放/暂停、音量条和设备名称/连接状态。中部区域120像素高显示专辑封面缩略图如果有。如果没有封面则放大显示歌曲标题。中下部区域60像素高显示歌曲标题和艺术家过长文本需要滚动或截断。底部区域20像素高显示播放进度条和时间。在TFT_eSPI中绘制这些元素非常直观。下面是一个更新显示的函数框架void updateDisplay() { static uint32_t lastUpdate 0; if (millis() - lastUpdate 500) { // 限制刷新频率每500ms更新一次 return; } lastUpdate millis(); tft.fillScreen(TFT_BLACK); // 清屏或局部刷新以优化速度 // 1. 绘制顶部状态栏 tft.setTextSize(1); tft.setCursor(5, 5); tft.print(current_track.is_playing ? ▶ : ⏸); // 绘制音量条 int volBarWidth map(current_track.volume, 0, 127, 0, 100); tft.fillRect(30, 8, volBarWidth, 6, TFT_GREEN); // 2. 绘制中部区域 int centerY 70; if (albumCoverAvailable) { // 假设有这个标志位 // 这里调用显示位图函数例如 drawJpeg() 或 drawBmp() // tft.drawJpeg(albumCoverData, albumCoverSize, 60, 40); } else { tft.setTextSize(3); tft.setTextColor(TFT_CYAN); tft.setTextDatum(TC_DATUM); // 文本对齐方式顶部居中 tft.drawString(current_track.title.length() 0 ? current_track.title.c_str() : No Track, 120, centerY - 20, 2); // 使用字体2 } // 3. 绘制歌曲信息 tft.setTextSize(2); tft.setTextColor(TFT_WHITE); tft.setTextDatum(TL_DATUM); // 左上对齐 // 处理长文本滚动或截断 String displayTitle current_track.title; if (displayTitle.length() 20) { displayTitle displayTitle.substring(0, 17) ...; } tft.drawString(displayTitle, 10, 150, 2); // 使用字体2 tft.setTextSize(1); tft.setTextColor(TFT_YELLOW); String displayArtist current_track.artist; if (displayArtist.length() 25) { displayArtist displayArtist.substring(0, 22) ...; } tft.drawString(displayArtist, 10, 180, 2); // 4. 绘制底部进度条示例需要获取时长和当前位置 // int progressWidth map(current_track.song_position_ms, 0, current_track.song_duration_ms, 0, 220); // tft.fillRect(10, 220, progressWidth, 8, TFT_BLUE); }5.2 文本滚动与长信息处理策略歌曲名或艺术家名可能很长直接显示会超出屏幕。除了简单的截断加“...”更友好的方式是水平滚动。实现一个简单的文本滚动器并不复杂String scrollText(const String text, int maxWidth, int offset, uint32_t lastScrollTime) { if (text.length() * 12 maxWidth) { // 12是假设的字符平均像素宽度 return text; // 文本不长无需滚动 } if (millis() - lastScrollTime 200) { // 每200ms滚动一次 lastScrollTime millis(); offset; if (offset text.length() 5) { // 滚完一遍后留白然后重置 offset -maxWidth / 12; } } String displayText text ; // 添加一些空格作为滚动间隔 int startIdx max(0, offset); int endIdx min(startIdx maxWidth / 12, displayText.length()); return displayText.substring(startIdx, endIdx); }在updateDisplay()中为需要滚动的文本维护一个offset和lastScrollTime变量然后调用scrollText函数获取当前应显示的字符串片段进行绘制。5.3 专辑封面的获取与显示优化进阶通过AVRCP获取专辑封面URL或二进制数据在标准协议中支持有限且ESP32的ESP32-A2DP库默认不包含此功能。一个更可行的进阶方案是在Mac端架设一个元数据服务器写一个简单的脚本如Python Flask应用监听Mac的音乐播放状态通过AppleScript或Music/Spotify API获取当前歌曲的完整元数据和高清封面URL。ESP32通过Wi-Fi请求当ESP32通过蓝牙收到新的歌曲标题和艺术家后通过Wi-Fi连接到家庭网络向Mac上的这个服务器发送HTTP请求查询对应的专辑封面图片URL或直接获取缩略图数据。下载并解码图片ESP32使用HTTPClient下载图片最好是小型JPEG或BMP然后用TJpgDec库或TFT_eSPI内置的JPEG解码功能在屏幕上显示。这个方案将蓝牙的稳定性和Wi-Fi的丰富数据能力结合了起来但复杂度也显著增加涉及到双网络蓝牙Wi-Fi协同、HTTP通信和图片解码对ESP32的内存和编程能力都是挑战。作为初版可以暂不实现封面显示专注于把文本信息做稳定。6. 系统集成、调试与优化当各个模块都能独立工作后我们需要将它们整合成一个稳定、可用的系统并解决实际运行中会遇到的问题。6.1 电源管理与自动重连逻辑我们希望这个设备能7x24小时稳定运行。这就需要考虑异常处理。蓝牙自动重连蓝牙连接可能因距离、干扰或Mac睡眠而断开。我们需要在loop()中检测连接状态并在断开后尝试重连。void checkAndReconnectBluetooth() { static uint32_t lastCheck 0; if (millis() - lastCheck 10000) { // 每10秒检查一次 lastCheck millis(); if (!a2dp_sink.is_connected()) { Serial.println(Bluetooth disconnected. Attempting to reconnect...); tft.fillScreen(TFT_BLACK); tft.drawString(BT Disconnected, 60, 100, 2); // 注意ESP32-A2DP库的start()函数在设备已初始化后再次调用可能有问题。 // 更稳妥的做法是重启蓝牙或进入可发现模式。 // 一种简单实现ESP.restart(); // 直接重启粗暴但有效 // 更好的做法实现一个状态机先停止蓝牙再重新初始化。 } } }在生产环境中更优雅的方式是使用非阻塞的状态机来管理蓝牙的连接、断开、重播广告等流程避免使用阻塞的delay()和重启。看门狗定时器为了防止程序跑飞启用ESP32的硬件看门狗。#include esp_task_wdt.h void setup() { // ... 其他初始化 esp_task_wdt_init(10, true); // 10秒看门狗超时 esp_task_wdt_add(NULL); // 将当前任务添加到看门狗监控 } void loop() { esp_task_wdt_reset(); // 在主循环中定期喂狗 // ... 其他逻辑 }6.2 实际测试中的常见问题与解决方案在将设备与Mac配对并使用的过程中我遇到了几个典型问题Mac无法发现或连接ESP32设备检查确保ESP32的蓝牙已正确初始化并开始广播a2dp_sink.start()被调用。解决在Mac的蓝牙设置中有时需要忽略旧设备再重新搜索。确保ESP32的设备名是唯一的。深入某些ESP32开发板的蓝牙天线性能较弱。尝试将设备靠近Mac或使用带有外部天线的ESP32模块。连接后Mac的“声音输出”中没有“ESP32 Now Playing”选项原因这是因为ESP32被识别为“音频设备”但Mac可能没有自动将其设为输出设备。解决在Mac上打开系统设置-声音-输出手动选择“ESP32 Now Playing”。或者在蓝牙设置中点击设备右侧的“选项”勾选“用作音频设备”。元数据不更新或更新延迟原因这是AVRCP协议和播放器实现的固有限制。有些播放器如网页版Spotify通过蓝牙传输的元数据支持很差。排查打开Arduino串口监视器波特率115200观察当切歌时是否有AVRC metadata rsp日志输出。如果没有说明播放器没发送。变通尝试使用不同的播放器如Apple Music、本地iTunes库测试。这是项目最大的不确定性来源。屏幕闪烁或内容残影原因tft.fillScreen(TFT_BLACK)全屏刷新频率过高。优化改为局部刷新。只重绘发生变化的部分区域。例如只有播放状态图标变化时只清除并重绘图标区域。这需要更精细的UI状态管理。6.3 外壳设计与最终部署建议一个裸露的开发板放在桌面上并不美观。你可以为它设计一个3D打印外壳。设计要点外壳需要为ESP32的USB口、屏幕、以及可能的复位按钮留出开口。考虑散热如果屏幕是IPS屏长时间工作会有微热。可以在外壳背面或侧面设计一些散热孔。固定使用螺丝柱或卡扣将ESP32和屏幕固定在外壳内部避免晃动。供电可以使用一个旧的手机充电器和一个美观的USB线进行供电。如果想更简洁可以考虑使用电池供电但需要增加充电管理电路并且要接受定期充电的维护成本。部署时找一个合适的位置摆放确保蓝牙信号稳定。你可以将它放在显示器旁边、书架一角或者任何你觉得方便瞥一眼就能看到播放信息的地方。7. 项目总结与扩展思路回顾整个项目我们利用ESP32的双模蓝牙能力巧妙地通过AVRCP协议捕获了macOS系统级的播放信息并将其显示在一块便携的屏幕上实现了一个低成本、高定制性的“Now Playing”硬件设备。它的价值不在于替代软件控件而在于提供了一种独特的、脱离屏幕的交互体验是数字生活与物理世界的一次有趣结合。这个项目的基础框架已经搭建完成但它还有巨大的扩展潜力添加物理控件为ESP32连接几个按钮或一个旋转编码器实现物理的播放/暂停、切歌、音量调节功能。通过AVRCP协议向Mac发送控制命令a2dp_sink.play(),a2dp_sink.next()等让交互闭环。支持多平台虽然项目聚焦Mac但AVRCP是蓝牙通用协议。理论上任何支持蓝牙音频并实现了AVRCP的设备如Windows PC、Android手机、iPhone都可以作为信号源。你可以在代码中增加识别不同源设备并适配显示逻辑的功能。网络电台与天气显示在ESP32连接Wi-Fi的基础上可以增加网络功能。当没有蓝牙音频播放时屏幕可以显示网络时间、天气信息甚至播放网络电台ESP32-A2DP也支持作为音频源播放网络流。使用更高级的图形库如果你对UI有更高要求可以移植LVGL这个嵌入式图形库到ESP32上。它能提供更流畅的动画、更丰富的控件列表、按钮等打造出媲美智能手表的交互界面。最后关于元数据获取的稳定性这是我踩过最深的坑。目前纯蓝牙AVRCP的方案受制于播放器兼容性。如果你追求极致的稳定性和丰富的元数据尤其是封面那么**“蓝牙接收元数据 Wi-Fi从本地服务器获取补充信息”的混合架构**是更专业、更可靠的方向。这需要你在Mac上常驻一个后台服务但对于一个希望长期稳定使用的桌面小工具来说这份额外的投入是值得的。
返回列表