鹤岗市网站建设_网站建设公司_ASP.NET_seo优化
2026/1/10 7:50:39 网站建设 项目流程

偶然发现有一个产品,可以通过蓝牙连接手机,在播放音乐的同时,显示歌词。于是查找了一下资料,发现有很多用ESP32实现的例子,但无一例外都基于Arduino,调用大神提供的库实现歌词获取和显示。

为了节约硬盘空间,前不久Arduino已经被我从硬盘上删掉了,无法利用现成的了,所以只能用IDF实现一下。

一、蓝牙歌词显示原理

如果没接触过传统蓝牙,可能要实现在一个屏幕显示歌词,需要在手机播放歌曲的同时,将歌词字幕通过蓝牙以字符串的形式发送给单片机,然后再由单片机对字符串解析查找字库字模,在LCD上显示。这里面涉及手机上的歌词字幕获取,以及发送字符串的手机蓝牙编程,感觉难度不小。

但实际上,蓝牙标准化组织早已考虑到大众对显示歌词的需求,在传统蓝牙中提供了相关协议A2DP和AVRCP。利用这两个协议,可以实现音频和控制传输,实现蓝牙歌词显示。

A2DP(Advanced Audio Distribution Profile蓝牙音频传输模型协定)和AVRCP(Audio/Video Remote Control Profile音频/视频远程控制协议)是传统蓝牙的两种高层应用协议:

在市面上的应用产品中,支持A2DP的蓝牙产品通常也支持AVRCP。A2DP负责高质量音频数据的传输,而AVRCP则负责对这些音频数据进行远程控制。例如,在使用蓝牙耳机听音乐时,A2DP负责将音乐数据从手机传输到耳机,而AVRCP则允许用户通过耳机上的按钮来控制音乐的播放、暂停和切换等操作。

另外,AVRCP也可以传输歌词、歌名、作者等信息,而且手机上大部分音乐播放软件都已实现AVRCP。所以,只需在单片机端实现AVRCP协议,就可以和手机对话,获取歌词。

二、为什么选择ESP32?

其一,ESP32不是乐鑫最高性能的模块,和ESP32 S3相比真的很难用,但ESP32支持传统蓝牙,而ESP32 S3只支持BLE。

其二,ESP32有A2DP的例程,可以拿来直接利用。

三、实现方案

为了达到目标,需要完成以下工作:

  • 驱动一个屏幕,可以是LED、LCD,这个决定最终的显示效果;

  • 需要一个GBK汉字字模库,用来获取显示汉字;

  • 需要一个UTF转GBK的函数,因为AVRCP是UTF-8格式的,需要转换成GBK格式,才能通过GBK汉字字模库获取字模;

  • 读懂A2DP例程,加入适当的功能。

四、屏幕驱动

篇幅有限,这部分略,大家根据自已手头的屏幕情况驱动就好。

但需要注意的是,ESP32引脚资源有限,并且34-39引脚只能做为输入,00、02、05、12、15不推荐使用,如果还要支持I2S外放音乐,就剩不了太多引脚,可能刚好够一个8位并口屏驱动。

五、GBK汉字字模库

1、汉字库

可以从网上找一个gb2312.bin的标准汉字库,也可以用一些字库工具生成一个,比如高通字库。

2、自定义烧录分区表

为了将gb2312.bin烧录到ESP32中,需要为gb2312.bin分配一个分区,在烧录的时候和固件一起烧录到ESP32中:

idf.py menuconfig

Partition Table选择"Custom partition table CSV"

设置Custom partition table CSV名称:

partitions_singleapp_large.csv内容:

hzk是用于放汉字库的分区,0x50,0x32是自定义的类型和子类型用于和其他分区区别便于查找,300k为分区大小,汉字库200多k,大小足够了。

3、烧录命令和烧录地址

每次idf.py build编译后,都会给出烧录命令和烧录地址,比如:

nvs,data,nvs,0x9000,24K,phy_init,data,phy,0xf000,4K,factory,app,factory,0x10000,3000K,hzk,80,50,0x2fe000,300K,

其中,0x2fe000是烧录hzk的烧录地址。

编译后给出的烧录命令:

C:\Espressif\python_env\idf5.1_py3.11_env\Scripts\python.exe C:\Espressif\frameworks\esp-idf-v5.1.2\components\esptool_py\esptool\esptool.py -p (PORT) -b 460800 --before default_reset --after hard_reset --chip esp32 write_flash --flash_mode dio --flash_size 4MB --flash_freq 40m 0x1000 build\bootloader\bootloader.bin 0x8000 build\partition_table\partition-table.bin 0x10000 build\a2dp-demo.bin

需要修改下,把gb2312.bin也一起烧录。为了方便,可以将下面内容放到一个bat文件中。

C:\Espressif\python_env\idf5.1_py3.11_env\Scripts\python.exe C:\Espressif\frameworks\esp-idf-v5.1.2\components\esptool_py\esptool\esptool.py -p COM10 -b 460800 --before default_reset --after hard_reset --chip esp32 write_flash --flash_mode dio --flash_size 4MB --flash_freq 40m 0x1000 build\bootloader\bootloader.bin 0x8000 build\partition_table\partition-table.bin 0x10000 build\a2dp-demo.bin 0x2fe000 gb2312_80.bin

COM10酌情修改,gb2312_80.bin放到工程根目录。

4、读取汉字库,并在LCD上显示

通过前面操作可以在每次烧录时,将汉字库烧录到0x2fe000的起始位置。要读取它,需要利用2个函数:

  • esp_partition_find_first:Find first partition based on one or more parameters。通过类型、子类型、标识获取分区句柄。

  • esp_partition_read:Read data from the partition。从分区上读取指定字节数据。

实现函数:

获取分区句柄:

void getHzkPartition(void){ hzkPartition= esp_partition_find_first(0x50,0x32,"hzk");}

获取字模数据:

void getMatrix(const unsigned short nmCode){ uint32_t offset; unsigned char GBH,GBL; unsigned short nm=nmCode; GBH=nm>>8; GBL=nm; if(GBH>=0xb0) { offset=((GBH-0xa7)*94+GBL-0xa1)*32; }else { offset=((GBH-0xa1)*94+GBL-0xa1)*32; } esp_partition_read(hzkPartition,offset,MatrixBuff,32);}

通过GB2312汉字编码获取在字库中的偏移量,然后通过esp_partition_read读取字模数据。

将字模在LCD上显示函数:

void GUI_Write16CnCharMatrix(unsigned char x, unsigned char y, char *cn, unsigned short wordColor, unsigned short backColor){ uint8_t i=0, j,mx,my,wordByte; uint16_t zm; uint16_t color; uint8_t wordNum; mx=x; my=y; while (*cn != '\0') { if(mx>170){ mx=x; my+=16; } if(*cn<128){ wordNum = *cn - 32; setXY(mx,my,mx+7, my+15); for (wordByte=0; wordByte<16; wordByte++) { color = Font_Data[wordNum].dat[wordByte]; for (j=0; j<8; j++) { if ((color&0x80) == 0x80) { setPixel(wordColor); } else { setPixel(backColor); } color <<= 1; } } cn++; mx +=8; } else { setXY(mx, my, mx+15, my+15); zm=*cn; zm<<=8; zm|=*(cn+1); getMatrix(zm); for(i=0; i<32; i++) { //MSK的位数 color=MatrixBuff[i]; for(j=0;j<8;j++) { if((color&0x80)==0x80) { setPixel(wordColor); } else { setPixel(backColor); } color<<=1; }//for(j=0;j<8;j++)结束 } cn += 2; mx += 16; } }}

可以显示ASCII和汉字。

六、UTF转GBK的函数

UTF汉字不能直接转换为GBK,需要先将UTF转换为unicode,再将unicode通过查表法转换为GBK。

这部分不用自己实现,网上有现成的:https://gitee.com/zhangkt1995/my-code/tree/master/utf8_gb2312_switch

使用起来很方便!

七、实现蓝牙歌词功能

ESP IDF下有一个传统蓝牙接收设备(sink)例程:esp-idf-v5.1.2\examples\bluetooth\bluedroid\classic_bt\a2dp_sink

可以选择这个做为模板新建一个工程,然后将前面四、五、六步的程序加到这个工程中,再打开其中bt_app_av.c,在430行左右,修改:

memset(gb2312Data,' ',sizeof(gb2312Data)); size_t gb2312DataLen = utf8_to_gb2312((uint8_t *)rc->meta_rsp.attr_text, strlen((char*)rc->meta_rsp.attr_text), (uint8_t *)gb2312Data, sizeof(gb2312Data)); if((rc->meta_rsp.attr_id==0x01)&&(gb2312DataLen>0)){ gb2312Data[gb2312DataLen]=' '; gb2312Data[128]='\0'; printf("%s",gb2312Data); GUI_Write16CnCharMatrix(10,90,gb2312Data,VGA_WHITE,VGA_RED); }

当收到ESP_AVRC_CT_METADATA_RSP_EVT消息后,将收到的AVRCP协议数据显示出来。

为什么要在这里添加?如果想深入了解,还是需要参考AVRCP协议相关内容。rc->meta_rsp.attr_id==0x01,标识接收的是歌曲TITLE信息,同时歌词也复用该字段。

通过utf8_to_gb2312将收到的歌词,由UTF-8转换为gb2312编码。

通过GUI_Write16CnCharMatrix将歌词显示在LCD上。

八、运行效果

手机打开蓝牙,连接ESP_SPEAKER。打开搜狗音乐,找一首歌播放。

这时LCD上歌词会实时显示:

以上就是今天的分享,如果有需要查看原图、代码的小伙伴,请点击底部“阅读原文”进行下载。

END

作者:sujingliang

来源:21ic论坛


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询