基于MStickCPlus开发板制作一个用于接收网络电台的在线收音机
本次项目是使用m5stick公司的MStickCPlus开发板完成了暑假一起练活动,制作了一个可以接收网络电台MP3音频格式的在线收音机
标签
Arduino
2022暑假在家练
MStickCPlus
ESP8266Audio library
web_radio
会把你变丑的黑魔仙
更新2022-09-07
西安邮电大学
885

项目介绍:

本项目计划使用M5StickC Plus开发板来制作出一款可以播放网络电台的收音机。

我们在这个项目中需要完成的目标为以下三点:

  • 使用M5StickC Plus通过WiFi模块连接网络
  • 在M5StickC Plus上进行解码,并通过提供的扬声器模块播放音乐
  • 在M5StickC Plus上能够切换电台,并将电台的信息显示在LCD屏幕

因此针对上面需要解决的问题我们来在该文档中逐个给大家进行解决

硬件介绍设计思路

我们手头的硬件设备为:

  • M5StickC Plus一套
  • 扬声器板一块
  • 连接模块之间的杜邦线

其中M5StickC PLUS M5StickC的大屏幕版本,主控采用ESP32-PICO-D4模组,具备蓝牙4.2与WIFI功能,小巧的机身内部集成了丰富的硬件资源,如红外、RTC、麦克风、LED、IMU、按键、蜂鸣器、PMU等,同时屏幕尺寸升级到1.14寸、135*240分辨率的TFT屏幕,电池容量达到120mAh。

FlLdruegCAiz5t8FqtQki-J8Dx70

电源结构图:

FsV9B38oSgPYoyEzFtmTpWioWfyh

规格参数(用蓝色标出的为本次项目需要着重注意的点):

主控资源 参数
ESP32 240MHz dual core, 600 DMIPS, 520KB SRAM, Wi-Fi, dual mode Bluetooth
Flash闪存 4MB Flash
输入电压 5V @ 500mA
接口 TypeC x 1, GROVE(I2C+I/0+UART) x 1
LCD屏幕 1.14 inch, 135*240 Colorful TFT LCD, ST7789v2
麦克风 SPM1423
按键 自定义按键 x 2
LED 红色 LED x 1
RTC BM8563
PMU AXP192
蜂鸣器 板载蜂鸣器
IR Infrared transmission
MEMS MPU6886
天线 2.4G 3D天线
外接引脚 G0, G25/G26, G36, G32, G33
电池 120 mAh @ 3.7V, inside vb
工作温度 32°F to 104°F ( 0°C to 40°C )
净重 16g
毛重 21g
产品尺寸 48.2*25.5*13.7mm
包装尺寸 65*25*15mm
外壳材质 Plastic ( PC )

 

环境搭建步骤:

  • 安装Arduino IDE 1.8.18+
  • 添加 ESP32, ESP8266,和 M5Stack JSON 至首选项下的附加开发板管理器网址并用逗号隔开
    https://dl.espressif.com/dl/package_esp32_index.json
    http://arduino.esp8266.com/stable/package_esp8266com_index.json
    https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
  • 安装开发板 M5Stack 2.0.4 
  • 安装库M5StickCPlus 0.0.8 和 ESP8266Audio 1.9.7

实现的功能及图片展示

Ftdi3eBAGpLUICWW-VKSOwYq0fG6FrdmF5pDYR01wpCxcMsRQxOIoownFtJ_jKxZeyO9a6lHESAIjCajJHomFvrJy857ifUkiYdJGfbFo0H3iXRN

p1:初始化界面,连接wifi

p2:彩屏过渡界面

p3:播放广播音频

p4:停止播放进入选台模式

软件运行流程图:

Fq_svqkq9FeD8PjNw_pCEbpJ7xJM

主要代码片段及说明(主要看总体代码我已把重要地方全部使用中文注释)

**根据个人情况需要调试的代码段:**

1.输入自己的wifi信息

const char *SSID = "XXXXXXXX";
const char *PASSWORD = "XXXXXXXX";

2.设置字符缓冲区的大小, 默认为 16 * 1024 = 16kb,理论这个值越大我们播放出来的音频就越流畅

const int bufferSize = 32 * 1024; 

3. 设置播放/暂停的按键和选台/音量大小的按键

const int BTNA = 37; 
const int BTNB = 39; 

4.自定义自己的电台信息,这里是我搜集和爬到的电台的链接。

//电台的链接信息
char * arrayURL[6] = {
  "http://lhttp.qingting.fm/live/4915/64k.mp3",
  "http://lhttp.qingting.fm/live/5021912/64k.mp3",
  "http://lhttp.qingting.fm/live/5022405/64k.mp3",
  "http://lhttp.qingting.fm/live/20091/64k.mp3",
  "http://live.wostreaming.net/direct/ppm-jazz24mp3-ibc1",
  "http://lhttp.qingting.fm/live/1609/64k.mp3"
  
};
//电台的对应名称
String arrayStation[6] = {
  "Moring music",
  "ASIA FM classtic",
  "ASIA FM",
  "Chinese university student",
  "Jazz24",
  "SHAN'XI NEWS"
};

*开机的动画部分

void ColorBar() {
    float color_r, color_g, color_b;
    color_r = 0;
    color_g = 0;
    color_b = 255;

    for (int i = 0; i < 384; i=i+4) {
        if (i < 128) {
            color_r = i * 2;
            color_g = 0;
            color_b = 255 - (i * 2);
        }
        else if ((i >= 128) && (i < 256)) {
            color_r = 255 - ((i - 128) * 2);
            color_g = (i - 128) * 2;
            color_b = 0;
        }
        else if ((i >= 256) && (i < 384)) {
            color_r = 0;
            color_g = 255 - ((i - 256) * 2);
            ;
            color_b = (i - 256) * 2;
            ;
        }
        Disbuff.fillRect(0, 0, 240, 135, Disbuff.color565(color_r, color_g, color_b));//屏幕彩色闪烁铺满屏幕
        Displaybuff();
    }

    for (int i = 0; i < 4; i++) {
        switch (i) {
        case 0:
            color_r = 0;
            color_g = 0;
            color_b = 0;
            break;
        case 1:
            color_r = 255;
            color_g = 0;
            color_b = 0;
            break;
        case 2:
            color_r = 0;
            color_g = 255;
            color_b = 0;
            break;
        case 3:
            color_r = 0;
            color_g = 0;
            color_b = 255;
            break;
        }
        for (int n = 0; n < 240; n++) {
            color_r = (color_r < 255) ? color_r + 1.0625 : 255U;
            color_g = (color_g < 255) ? color_g + 1.0625 : 255U;
            color_b = (color_b < 255) ? color_b + 1.0625 : 255U;
            Disbuff.drawLine(n, i * 33, n, (i + 1) * 33, Disbuff.color565(color_r, color_g, color_b));//在屏幕上面画彩色线
        }
    }
    Displaybuff();
    delay(500);

    for (int i = 0; i < 4; i++) {
        switch (i) {
        case 0:
            color_r = 255;
            color_g = 255;
            color_b = 255;
            break;
        case 1:
            color_r = 255;
            color_g = 0;
            color_b = 0;
            break;
        case 2:
            color_r = 0;
            color_g = 255;
            color_b = 0;
            break;
        case 3:
            color_r = 0;
            color_g = 0;
            color_b = 255;
            break;
        }
        for (int n = 0; n < 240; n++) {
            color_r = (color_r > 2) ? color_r - 1.0625 : 0U;
            color_g = (color_g > 2) ? color_g - 1.0625 : 0U;
            color_b = (color_b > 2) ? color_b - 1.0625 : 0U;
            Disbuff.drawLine(239 - n, i * 34, 239 - n, (i + 1) * 34, Disbuff.color565(color_r, color_g, color_b));
        }
    }
    Displaybuff();
    delay(500);
}

*电台的播放

void StartPlaying() {
  file = new AudioFileSourceHTTPStream(URL);//实例化链接
  file->RegisterMetadataCB(MDCallback, (void*)"ICY");
  buff = new AudioFileSourceBuffer(file, bufferSize);//将链接与我们前面定义的字符缓冲区相结合,这个缓冲区的大小决定我们音乐播放的流畅与否
  buff->RegisterStatusCB(StatusCallback, (void*)"buffer");
  out = new AudioOutputI2S(0,1); // 使用内置DAC进行音频的输出
  //out->SetPinout(2, 25, 22);
  out->SetOutputModeMono(true);
  out->SetGain(fgain*0.1);
  M5.Lcd.fillTriangle(109, 20, 109 + (5 * fgain), 20, 109 + (5 * fgain), 20 - (2 * fgain), BLUE);
  mp3 = new AudioGeneratorMP3();
  mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
  mp3->begin(buff, out);
  Serial.printf("STATUS(URL) %s \n", URL);
  Serial.flush();
}

*电台的停止播放

void StopPlaying() {
  if (mp3) {
    mp3->stop();
    delete mp3;
    mp3 = NULL;
  }
  if (buff) {
    buff->close();
    delete buff;
    buff = NULL;
  }
  if (file) {
    file->close();
    delete file;
    file = NULL;
  }
  Serial.printf("STATUS(Stopped)\n");
  Serial.flush();
}

遇到的主要难题及解决方法:

这个部分是我在这次总结报告中主要想写的部分。

1.对于这次暑假活动我遇到的困难有许多,绝大部分都通过查阅国内外的资料解决了。最终有一个不完美的点在于通过我的代码来播放音频会有dd的噪音,我试图通过增加缓冲区通过软件层面的手法来进行改善,最终还是失败了,因为esp32-pico-D4的sram只有520k,硬件上的限制导致不可以使用我现有的材料来解决,在github上我看到一个大佬提出的解决方案,我搬运过来来提供给后面的同学参考,大佬提供的方案针对esp8266,我了解过esp32只要改动引脚也是可以的。

类允许使用芯片23lc1024 SPI RAM 作为输入缓冲区。该芯片连接到 ESP8266 HSPI 端口,提供了一个较大的缓冲区,有助于避免网络流回放中的阻塞现象。

目前的版本允许使用标准硬件 CS 或任何其他引脚通过软件在稍微低的性能。下面的示意图显示了一个例子:

FpJsrgjT7KGAnvlTfFViFNvsca8D

通过这个方法大大提高了sram的容量来是我们的音频可以更加流畅的播放。

2.音频播放的问题:

起初做这个项目的时候我对于乐鑫的I2S协议不是很了解,对于DAC也没有一个认识。导致走了很多的弯路,逐渐在查阅资料的过程中,看了AudioOutputI2S.h这一个库文件的源代码,发现esp32有内置的DAC在25,26引脚,其中在这个库中我们默认的DAC引脚就是26引脚,这个刚刚好与我们的开发板对外开放的引脚位置相同,这样我就没有使用杜邦线,直接将5v,gnd,AIN和开发板的5v,gnd,26引脚直插连接播放音频。

FmwusS7xKu2_uWHhrIH0miLG35BC

3.国内电台链接的获取问题:

我在看国外大佬例子时候发现他们的mp3链接都是打开就是一个黑色框框,只有一个播放按键,但是当我打开国内电台时候发现没有办法直接获取我需要的链接,这时我在b站发现一个人分享的蜻蜓的mp3链接,在他分享的链接的基础上我发现了一个规律,就是只用改动链接的数字部分就可以和得到我们任何想要的电台链接,进去就是纯粹的音频播放界面没有下一个节目上一个界面这种花哨的功能。

http://lhttp.qingting.fm/live/1609/64k.mp3:陕广新闻

FovIeww2LdhlxLNRdI1kUmQkmNTz

就是这么一个方案,可以来提供给后面的同学来进行参考

4.汉字显示的问题:

在我进行项目的过程中发现M5.Lcd.print()函数只可以打印英文,我一想这个缺乏中文字库,导致只可以在电台播放界面显示出英文曲目信息,中文不可以,查阅了m5的官方文档后,发现他们只有在micropython中提供了显示汉字的api,arduino中是没有的。这各也就是后续可以改进的地方,可以加入一个汉字字库,来可以使用arduino语言来显示出中文电台播放信息在LCD屏幕上面。

未来的计划或建议:

由于我不是相关专业的学生,所以我没有一定的专业知识,但是我可以去摸索去学习,嵌入式也是我的一个兴趣爱好所在,以后我也会一直走下去,本文的主要代码我已全部中文注释,有不理解的同学,欢迎在评论区来一起讨论,一起学习。

附件下载
m5webradio.ino
团队介绍
你说的都对队
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号