基于HTTPS长连接的ESP32+VS1053网络电台收音机Arduino代码

基于HTTPS长连接的ESP32+VS1053网络电台收音机Arduino代码

代码下载地址:ESP32+VS1053网络电台收音机Arduino完整源代码。-硬件开发文档类资源-CSDN下载

  • 硬件搭建
  1. 本文使用的控制板两块:ESP32最小系统板,带USB转串行接口,电源模块,可向外部供电。VS1053+SD播放模块,板载耳机功放、耳机插头、线路输入插头及麦克风。如下图:

           

  1. 引脚连接如下:

*   ESP32              VS1053_SD             功能说明

*  

*  D5 ---------------------- CS                    SD卡片选

* D18 ----------------------- SCK                SPI总线时钟

* D19 ----------------------- MISO              SPI总线输出

* D23 ----------------------- MOSI              SPI总线输入

* D22 ----------------------- X_CS               VS1053控制总线片选

* D21 ----------------------- X_DCS             VS1053数据总线片选

* D15 ----------------------- DREQ               VS1053总线忙信号

* EN ----------------------- X_RESET           VS1053复位(实际EN脚IO号为34,即D34)

* VIN ----------------------- 5V                      5V电源

* GND --------------------- GND                  接地线

*

  • 软件环境

代码编辑使用的是Arduino,并配置好ESP32编译环境。具体软件下载和配置方法网上很多,不再赘述。

  • 程序说明

程序总体思路是:

void setup() {
  uint32_t TimerOut;
  Serial.begin(115200);
  Player.begin();//Player.SetVolume(90);
  WiFi.mode(WIFI_STA);
  WiFi.begin(SSIDNAME,PASSWORD);

  Serial.print("Waiting for WiFi to connect...");
  TimerOut=0;while ((WiFi.status()!=WL_CONNECTED) && TimerOut++<60){delay(500);Serial.print(".");}
  if(TimerOut>=60){Serial.println("Connection failed");return;}else{Serial.println(" connected");}
  setClock();//同步NTP服务器时间,加密传输运算需要。
  httpsconnection();//连接网站以获取电台播放数据。
}

1.先连接WIFI(注意SSIDNAME和PASSWORD须与自己使用的WIFI信息一致),

2.系统时间与NTP服务器时间同步(HTTPS连接须要用到)。void setClock()

3.连接电台。void httpsconnection(void)

过程中client -> setCACert(rootCACertificate);语句设置使用网站根证书验证,必须将根证书信息保存在rootCACertificate变量中。本程序中保存的是蜻蜓网络电台的网站根证书,如果连接其他网络电台,则必须进行相应修改。如果不想使用网站根证书进行验证,则可以将此句注释掉,然后使用client -> setInsecure();通知服务器不使用安全验证,明码传输虽然不安全,但对于网络电台收音机来说,这个并不重要。

需要注意的是,如果使用根证书验证,则此过程必须在setClock()过程更新时间后进行,否则连接会不成功。

4.连接成功后不断接收服务器下发的数据块,进行相应处理后存入缓存中,并通过SPI串行总线将数据发送给VS1053解码播放。此过程在loop()主循环中执行。

  • 完整代码如下:编译前请先修改SSIDNAME和PASSWORD。其中class VS_1053是一个VS1053操作对象的完整封装,里面有很多用于MP3播放的函数,如果用不上,可以删去多余的函数。完整代码可以到以下地址下载:https://download.csdn.net/download/liyong_sbcel/78272687
#include <SPI.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
class VS_1053 {/*VS1053操作对象*/
    private:
      //SPI引脚设置.
      uint8_t sck_pin=18;     //D18
      uint8_t mosi_pin=23;    //D23
      uint8_t miso_pin=19;    //D19
      //以下为硬件连接设置.
      uint8_t cs_pin=22;      //D22
      uint8_t dcs_pin=21;     //D21
      uint8_t dreq_pin=15;    //D15
      uint8_t reset_pin=34;   //EN
      const uint8_t vs1053_chunk_size=32;//最少可以一次写入32字节.
      //寄存器地址.
      const uint8_t SCI_MODE=0x0;
      const uint8_t SCI_STATUS=0x1;
      const uint8_t SCI_BASS=0x2;
      const uint8_t SCI_CLOCKF=0x3;
      const uint8_t SCI_DECODE_TIME=0x4;
      const uint8_t SCI_AUDATA=0x5;
      const uint8_t SCI_WRAM=0x6;
      const uint8_t SCI_WRAMADDR=0x7;
      const uint8_t SCI_WHDAT0=0x8;
      const uint8_t SCI_WHDAT1=0x9;
      const uint8_t SCI_AIADDR=0xA;
      const uint8_t SCI_VOL=0xB;
      const uint8_t SCI_AICTRL0=0xC;
      const uint8_t SCI_AICTRL1=0xD;
      const uint8_t SCI_AICTRL2=0xE;
      const uint8_t SCI_AICTRL3=0xF;
      //SCI_MODE 寄存器各位功能.
      const uint8_t SM_DIFF=0;
      const uint8_t SM_LAYER12=1;
      const uint8_t SM_RESET=2;
      const uint8_t SM_CANCEL=3;
      const uint8_t SM_EARSPEAKER_LO=4;
      const uint8_t SM_TESTS=5;
      const uint8_t SM_STREAM=6;
      const uint8_t SM_EARSPEAKER_HI=7;
      const uint8_t SM_DACT=8;
      const uint8_t SM_SDIORD=9;
      const uint8_t SM_SDISHARE=10;
      const uint8_t SM_SDINEW=11;
      const uint8_t SM_ADPCM=12;
      const uint8_t SM_LINE1=14;
      const uint8_t SM_CLK_RANGE=15;
 

      SPIClass * vspi = NULL;                 //SPI总线操作对象.
      SPISettings VS1053_SPI;                 //SPI总线设置.
      uint8_t curvol;                         //音量。0~100
      int8_t curbalance;                      //音量平衡-100~+100
      uint8_t endFillByte;                    //填充字,用于取消和停止播放时填充缓冲区.
      uint16_t speedNum;                      //倍速播放设置.
    protected:
      inline void await_data_request()const{while(!digitalRead(dreq_pin)){yield();}}//等待DREQ引脚变为高电平(模块空闲).
      inline void control_mode_on()const{vspi->beginTransaction(VS1053_SPI);digitalWrite(dcs_pin, HIGH);digitalWrite(cs_pin, LOW);}//开始指令传送.
      inline void control_mode_off()const{digitalWrite(cs_pin, HIGH);vspi->endTransaction();}//结束指令传送.
      inline void data_mode_on()const{vspi->beginTransaction(VS1053_SPI);digitalWrite(cs_pin, HIGH);digitalWrite(dcs_pin, LOW);}//开始数据传送.
      inline void data_mode_off()const{digitalWrite(dcs_pin, HIGH);vspi->endTransaction();}//结束数据传送.
      uint16_t read_register(uint8_t _reg)const{uint16_t result;control_mode_on();vspi->write(3);vspi->write(_reg);result=(vspi->transfer(0xFF)<<8)|(vspi->transfer(0xFF));await_data_request();control_mode_off();return result;}//读寄存器.
      void write_register(uint8_t _reg,uint16_t _value)const{control_mode_on();vspi->write(2);vspi->write(_reg);vspi->write16(_value);await_data_request();control_mode_off();}//写寄存器.
      void sdi_send_buffer(uint8_t *data,size_t BufferSize,size_t Start,size_t Count){size_t chunk_length;uint8_t *WritIndex;if(Start>=BufferSize || Count>BufferSize){return;}WritIndex=data+Start;data_mode_on();while(Count){await_data_request();chunk_length=Count;if(Start+chunk_length>BufferSize){chunk_length=BufferSize-Start;}if(chunk_length>vs1053_chunk_size){chunk_length = vs1053_chunk_size;}Count -= chunk_length;vspi->writeBytes(WritIndex, chunk_length);WritIndex += chunk_length;Start+=chunk_length;if(Start>=BufferSize){Start=Start%BufferSize;WritIndex=data+Start;}}data_mode_off();}//发送指定缓冲区指定数量的数据(循环写入).
      void sdi_send_buffer(uint8_t *data,size_t len){size_t chunk_length;data_mode_on();while(len){await_data_request();chunk_length=len;if(len>vs1053_chunk_size){chunk_length = vs1053_chunk_size;}len -= chunk_length;vspi->writeBytes(data, chunk_length);data += chunk_length;}data_mode_off();}//发送指定缓冲区指定数量的数据(全部写入).
      void sdi_send_fillers(size_t len){size_t chunk_length;data_mode_on();while(len){await_data_request();chunk_length=len;if(len>vs1053_chunk_size){chunk_length = vs1053_chunk_size;}len -= chunk_length;while (chunk_length--){vspi->write(endFillByte);}}data_mode_off();}//发送指定数量的相同数据(填充).
      void wram_write(uint16_t address,uint16_t data){write_register(SCI_WRAMADDR, address); write_register(SCI_WRAM, data);}//写RAM.
      uint16_t wram_read(uint16_t address){write_register(SCI_WRAMADDR, address);return read_register(SCI_WRAM);}//读RAM.
      void LoadPatch(){const uint16_t patch[40]={0x0007, 0x0001, 0x8010, 0x0006, 0x001c, 0x3e12, 0xb817, 0x3e14,0xf812, 0x3e01, 0xb811, 0x0007, 0x9717, 0x0020, 0xffd2, 0x0030,0x11d1, 0x3111, 0x8024, 0x3704, 0xc024, 0x3b81, 0x8024, 0x3101,0x8024, 0x3b81, 0x8024, 0x3f04, 0xc024, 0x2808, 0x4800, 0x36f1,0x9811, 0x0007, 0x0001, 0x8028, 0x0006, 0x0002, 0x2a00, 0x040e};uint16_t i,addr,n,val;for(i=0;i<40;){addr=patch[i++];n=patch[i++];if(n&0x8000){n&=0x7FFF;val=patch[i++];while(n--){write_register(addr,val);}}else{while(n--){val=patch[i++];write_register(addr,val);}}}}//装载录音补丁.
      bool hdReset(void){uint8_t retry=0;digitalWrite(reset_pin, LOW);delay(100);digitalWrite(reset_pin, HIGH);digitalWrite(cs_pin, HIGH);digitalWrite(dcs_pin, HIGH);while(retry<20){retry++;delay(50);if(digitalRead(dreq_pin)!=0){return 0;}}Serial.print("\r\n硬复位失败!");return 1;}//硬复位.
      bool softReset(){uint8_t retry=0;write_register(SCI_MODE, _BV(SM_SDINEW) | _BV(SM_RESET));digitalWrite(cs_pin, HIGH);digitalWrite(dcs_pin, HIGH);while(retry<20){retry++;delay(50);if(digitalRead(dreq_pin)!=0){return 0;}}Serial.print("\r\n软复位失败!");return 1;}//软复位.
    public:
      VS_1053(void){cs_pin=22;dcs_pin=21;dreq_pin=15;reset_pin=34;}//默认接线.
      VS_1053(uint8_t _cs_pin,uint8_t _dcs_pin,uint8_t _dreq_pin,uint8_t _reset_pin){cs_pin=_cs_pin;dcs_pin=_dcs_pin;dreq_pin=_dreq_pin;reset_pin=_reset_pin;}//指定接线.
      uint8_t begin(){uint32_t index;vspi = new SPIClass(VSPI);vspi->begin(sck_pin,miso_pin,mosi_pin,cs_pin);VS1053_SPI = SPISettings(1000000,MSBFIRST,SPI_MODE0);pinMode(dreq_pin, INPUT);pinMode(cs_pin, OUTPUT);pinMode(dcs_pin, OUTPUT);pinMode(reset_pin, OUTPUT);hdReset();softReset();play_enter_mode();testComm();}
      void SPI_SET(void){uint32_t index;uint8_t cnt;cnt=0;for(index=6000000;index>200000;index=index-10000){VS1053_SPI = SPISettings(index,MSBFIRST,SPI_MODE0);if(!testComm()){if(!testComm()){if(!testComm()){if(!testComm()){if(!testComm()){break;}}}}}}Serial.printf("SPI总线速度设置为:%d。\n",index);}//根据测试结果设置最合适的SPI总线速度.
      void play_enter_mode(void){softReset(); VS1053_SPI = SPISettings(200000,MSBFIRST,SPI_MODE0);write_register(SCI_CLOCKF,6<<12);delay(50);VS1053_SPI = SPISettings(4000000,MSBFIRST,SPI_MODE0);write_register(SCI_MODE,_BV(SM_SDINEW)|_BV(SM_LINE1));delay(5);write_register(SCI_AUDATA,44101);endFillByte = wram_read(0x1E06)&0xFF;switchToMp3Mode();SetVolume(80);Serial.printf("VS1053设置为播放器模式!\n");}//设置为放音机模式(MP3播放器).
      void recoder_enter_mode(void){softReset();VS1053_SPI = SPISettings(200000,MSBFIRST,SPI_MODE0);write_register(SCI_CLOCKF,6<<12);delay(50);VS1053_SPI = SPISettings(4000000,MSBFIRST,SPI_MODE0);write_register(SCI_AICTRL0,8000);write_register(SCI_AICTRL1,0);write_register(SCI_AICTRL2,4096U);write_register(SCI_AICTRL3,6);write_register(SCI_MODE,_BV(SM_SDINEW)|_BV(SM_RESET)|_BV(SM_ADPCM)); delay(5);LoadPatch();Serial.printf("VS1053设置为录音机模式!\n");}//设置为录音机模式(单声道(MIC)16位8KHZ采样).
      void VS_Sine_Test(void){uint8_t index;uint16_t OldMode;uint16_t OldVol;uint16_t OldBass;uint8_t TestData[16]={0x53,0xef,0x6e,0x24,0x00,0x00,0x00,0x00};uint8_t TestStop[16]={0x45,0x78,0x69,0x74,0x00,0x00,0x00,0x00};TestData[3]=0x24;SPISettings Old_VS1053_SPI;Old_VS1053_SPI=VS1053_SPI;OldBass=read_register(SCI_BASS);OldMode=read_register(SCI_MODE);OldVol=read_register(SCI_VOL);VS1053_SPI = SPISettings(200000, MSBFIRST, SPI_MODE0);softReset();write_register(SCI_VOL,0X5050);write_register(SCI_MODE,0x0820);for(index=0;index<4;index++){TestData[3]=0x24;sdi_send_buffer(TestData,sizeof(TestData));delay(1000);sdi_send_buffer(TestStop,sizeof(TestStop));TestData[3]=0x44;sdi_send_buffer(TestData,sizeof(TestData));delay(1000);sdi_send_buffer(TestStop,sizeof(TestStop));}softReset();write_register(SCI_MODE,OldMode);write_register(SCI_VOL,OldVol); write_register(SCI_BASS,OldBass);VS1053_SPI=Old_VS1053_SPI;}//正弦测试
      uint16_t ReadHDAT(bool Num){if(Num==0){return read_register(SCI_WHDAT0);}else{return read_register(SCI_WHDAT1);}}//录音时读取数据量或数据.
      void SetSamplingRate(uint16_t Num){write_register(SCI_AICTRL0,Num);}//设置采样率.
      uint16_t GetSamplingRate(void){return read_register(SCI_AICTRL0);}//读取采样率设置.
      uint16_t GetVocalTract(void){uint16_t TemNum;TemNum=read_register(SCI_AICTRL3);TemNum=(TemNum>>1)&0x01;if(TemNum==0){return 2;}else{return 1;}}//读取声道数.返回:1---单声道;2---双声道.
      void Set_Rec_AGC(uint8_t agc){if(agc>59){agc=59;}write_register(SCI_AICTRL1,agc*1024);write_register(SCI_AICTRL2,(agc+4)*1024);}//录音时AGC设置:只支持0~59级,0表示自动.
      uint16_t GetSpeed(){return speedNum;}//获取当前播放速度.
      void Speed(uint16_t Num){wram_write(0x1e04,Num);speedNum=Num;}//倍速播放设置.
      bool LineSelect(bool Mode){uint16_t OldData;OldData=read_register(SCI_MODE);OldData=OldData&0xbfff|((uint16_t)Mode)<<14;write_register(SCI_MODE,OldData);return (bool)(OldData&0x4000);}//线路选择(录音):Mode:0---MIC;1---LINE1.
      uint8_t EarSpeaker(uint8_t Mode){uint16_t OldData;OldData=read_register(SCI_MODE);switch(Mode){case 0:OldData=OldData&0xff6f;write_register(SCI_MODE,OldData);return 0;break;case 1:OldData=OldData|0x0010;write_register(SCI_MODE,OldData);return 1;break;case 2:OldData=OldData&0xffef|0x0080;write_register(SCI_MODE,OldData);return 2;break;case 3:OldData=OldData|0x0090;write_register(SCI_MODE,OldData);return 3;break;case 254:return (uint8_t)(((OldData&0x0080)>>6)|((OldData&0x0010)>>4));break;default:switch(OldData&0x0090){case 0x0000:OldData=OldData|0x0010;write_register(SCI_MODE,OldData);return 1;break;case 0x0010:OldData=OldData&0xffef|0x0080;write_register(SCI_MODE,OldData);return 2;break;case 0x0080:OldData=OldData|0x0090;write_register(SCI_MODE,OldData);return 3;break;case 0x0090:OldData=OldData&0xff6f;write_register(SCI_MODE,OldData);return 0;break;}break;}}//空间效果0~3---设置值.254---返回当前值.255---循环递增处理.
      uint8_t Trebleadjustment(uint8_t Mode){uint16_t OldData;OldData=read_register(SCI_BASS);if(Mode==254){return (int8_t)((OldData&0xf000)>>12);}if(Mode==255){Mode=(uint8_t)((OldData&0xf000)>>12);if(Mode<8){Mode++;if(Mode>7){Mode=15;}}else{Mode--;if(Mode<=8){Mode=0;}}}if(Mode>15){Mode=0;}OldData=OldData&0x0fff|0x0A00|(uint16_t)(Mode<<12);write_register(SCI_BASS, OldData);return Mode;}//高音调节Mode:-7~7---设置值.254---返回当前值.255---循环递增处理.
      uint8_t Bass(uint8_t Mode){uint16_t OldData;OldData=read_register(SCI_BASS);if(Mode==254){return (uint8_t)((OldData&0x00F0)>>4);}if(Mode==255){Mode=(uint8_t)((OldData&0x00F0)>>4);Mode++;if(Mode>15){Mode=0;}}if(Mode>15){Mode=15;}OldData=OldData&0xff0f|0x000A|(uint16_t)(Mode<<4);write_register(SCI_BASS, OldData);return Mode;}//低音效果.Mode:0~15设置值;254返回当前值;255循环递增处理.
      void playChunk(uint8_t *data,size_t BufferSize,size_t Start,size_t Count){sdi_send_buffer(data,BufferSize,Start,Count);}//播放指定数据的MP3音乐。可以一次播放较长的数据。
      void playChunk(uint8_t *data,size_t len){sdi_send_buffer(data,len);}//播放指定数据的MP3音乐。可以一次播放较长的数据。
      void startSong(){sdi_send_fillers(10);}
      void cancelSong(){uint16_t modereg;int i;write_register(SCI_MODE,_BV(SM_SDINEW)|_BV(SM_CANCEL));for (i=0;i<64;i++){sdi_send_fillers(32);modereg=read_register(SCI_MODE);if((modereg&_BV(SM_CANCEL))==0){sdi_send_fillers(2052);return;}}softReset();}//取消当前缓冲区数据解码.
      void stopSong(){uint16_t modereg;int i;sdi_send_fillers(2052);write_register(SCI_MODE, _BV(SM_SDINEW) | _BV(SM_CANCEL));for (i=0;i<64;i++){sdi_send_fillers(32);modereg = read_register(SCI_MODE);if((modereg&_BV(SM_CANCEL))==0){sdi_send_fillers(2052);return;}}softReset();}//停止解码,缓冲区数据会继续,直到全部数据被解码为止.
      void SetVolume(uint8_t vol){uint8_t valueL,valueR;curvol=vol;valueL=vol;valueR=vol;if(curbalance<0){valueR=max(0,vol+curbalance);}else if(curbalance>0){valueL=max(0,vol-curbalance);}valueL=map(valueL,0,100,0xFE,0x00);valueR=map(valueR,0,100,0xFE,0x00);write_register(SCI_VOL,(valueL<<8)|valueR);}
      void SetBalance(int8_t balance){if(balance>100){curbalance=100;}else if(balance<-100){curbalance=-100;}else{curbalance = balance;}}
      void setTone(uint8_t *rtone){uint16_t value=0;int i;for(i=0;i<4;i++){value=(value<<4)|rtone[i];}write_register(SCI_BASS,value);}
      uint8_t GetVolume(){return curvol;}
      int8_t GetBalance(){return curbalance;}
      bool testComm(void){uint16_t OldData;uint16_t i,r1,r2,cnt=0;cnt=0;while(!digitalRead(dreq_pin)){delay(10);cnt++;if(cnt>10){Serial.printf("VS1053测试:硬件错误!\n");return 1;break;}}delay(100);OldData=read_register(SCI_BASS);cnt=0;for(i=0;(i<1000)&&(cnt<20);i++){write_register(SCI_BASS,i);r1=read_register(SCI_BASS);r2=read_register(SCI_BASS);if((i!=r1)||(i!=r2)){cnt++;}}if(cnt>0){Serial.printf("VS1053测试:寄存器读写错误!在%d次读写测试中错误超过%d次.\n",i,cnt);}write_register(SCI_BASS,OldData);return cnt;}//测试成功返回0;失败返回1.
      inline bool data_request() const {return (digitalRead(dreq_pin) == HIGH);}//直接返回系统空闲状态.
      void switchToMp3Mode(){wram_write(0xC017, 3);wram_write(0xC019, 0);delay(100);softReset();}//设置为MP3解码模式.
      bool isChipConnected(){uint16_t status = read_register(SCI_STATUS);return !(status == 0 || status == 0xFFFF);}
      uint16_t getDecodedTime(){return read_register(SCI_DECODE_TIME);}
      void clearDecodedTime(){write_register(SCI_DECODE_TIME, 0x00);write_register(SCI_DECODE_TIME, 0x00);}
  };

// 蜻蜓网络电台网站根证书:https://lhttp.qingting.fm
//如果连接其他网络电台,则必须修改为相应网站根证书信息。
const char* rootCACertificate = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIEsTCCA5mgAwIBAgIQCKWiRs1LXIyD1wK0u6tTSTANBgkqhkiG9w0BAQsFADBh\n" \
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \
"QTAeFw0xNzExMDYxMjIzMzNaFw0yNzExMDYxMjIzMzNaMF4xCzAJBgNVBAYTAlVT\n" \
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \
"b20xHTAbBgNVBAMTFFJhcGlkU1NMIFJTQSBDQSAyMDE4MIIBIjANBgkqhkiG9w0B\n" \
"AQEFAAOCAQ8AMIIBCgKCAQEA5S2oihEo9nnpezoziDtx4WWLLCll/e0t1EYemE5n\n" \
"+MgP5viaHLy+VpHP+ndX5D18INIuuAV8wFq26KF5U0WNIZiQp6mLtIWjUeWDPA28\n" \
"OeyhTlj9TLk2beytbtFU6ypbpWUltmvY5V8ngspC7nFRNCjpfnDED2kRyJzO8yoK\n" \
"MFz4J4JE8N7NA1uJwUEFMUvHLs0scLoPZkKcewIRm1RV2AxmFQxJkdf7YN9Pckki\n" \
"f2Xgm3b48BZn0zf0qXsSeGu84ua9gwzjzI7tbTBjayTpT+/XpWuBVv6fvarI6bik\n" \
"KB859OSGQuw73XXgeuFwEPHTIRoUtkzu3/EQ+LtwznkkdQIDAQABo4IBZjCCAWIw\n" \
"HQYDVR0OBBYEFFPKF1n8a8ADIS8aruSqqByCVtp1MB8GA1UdIwQYMBaAFAPeUDVW\n" \
"0Uy7ZvCj4hsbw5eyPdFVMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEF\n" \
"BQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADA0BggrBgEFBQcBAQQo\n" \
"MCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBCBgNVHR8E\n" \
"OzA5MDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9i\n" \
"YWxSb290Q0EuY3JsMGMGA1UdIARcMFowNwYJYIZIAYb9bAECMCowKAYIKwYBBQUH\n" \
"AgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCwYJYIZIAYb9bAEBMAgG\n" \
"BmeBDAECATAIBgZngQwBAgIwDQYJKoZIhvcNAQELBQADggEBAH4jx/LKNW5ZklFc\n" \
"YWs8Ejbm0nyzKeZC2KOVYR7P8gevKyslWm4Xo4BSzKr235FsJ4aFt6yAiv1eY0tZ\n" \
"/ZN18bOGSGStoEc/JE4ocIzr8P5Mg11kRYHbmgYnr1Rxeki5mSeb39DGxTpJD4kG\n" \
"hs5lXNoo4conUiiJwKaqH7vh2baryd8pMISag83JUqyVGc2tWPpO0329/CWq2kry\n" \
"qv66OSMjwulUz0dXf4OHQasR7CNfIr+4KScc6ABlQ5RDF86PGeE6kdwSQkFiB/cQ\n" \
"ysNyq0jEDQTkfa2pjmuWtMCNbBnhFXBYejfubIhaUbEv2FOQB3dCav+FPg5eEveX\n" \
"TVyMnGo=\n" \
"-----END CERTIFICATE-----\n";
//中国之声:     https://lhttp.qingting.fm/live/386/64k.mp3
//经济之声:     https://lhttp.qingting.fm/live/387/64k.mp3
//音乐之声:     https://lhttp.qingting.fm/live/388/64k.mp3
//经典音乐:     https://lhttp.qingting.fm/live/389/64k.mp3
//香港之声:     https://lhttp.qingting.fm/live/4986/64k.mp3
//文艺之声:     https://lhttp.qingting.fm/live/395/64k.mp3
//华夏之声:     https://lhttp.qingting.fm/live/392/64k.mp3
//都市之声:     https://lhttp.qingting.fm/live/389/64k.mp3
//中华之声:     https://lhttp.qingting.fm/live/390/64k.mp3
//阅读之声:     https://lhttp.qingting.fm/live/398/64k.mp3
//大湾区之声:   https://lhttp.qingting.fm/live/392/64k.mp3


WiFiClientSecure *client;//网络连接对象。
VS_1053 Player;//MP3播放模块VS1053操作对象。
const char *SSIDNAME="ShuFang";//WIFI名称。
const char *PASSWORD="12345678";//WIFI密码。
const char *host="lhttp.qingting.fm";//网站HOST主机。
const char *path="/live/4915/64k.mp3";//电台地址。
//String Url="https://lhttp.qingting.fm/live/4915/64k.mp3";//完整的电台URL地址。
const uint16_t httpsPort=443;//连接端口号。
const uint16_t BufferSize=8192;//缓存大小。缓存足够才能保证在WIFI接收间隔期间不会中断。不会有停顿或跳跃感。
uint8_t Buffer[BufferSize];//播放数据缓存。
uint16_t BufferIndex;//缓存指针。
uint16_t PlayIndex;//播放指针。
//uint16_t dataSize;//数据块大小。
//uint8_t OldData;//暂存字节。
void setClock() {
  //此过程更新ESP32内部时钟。
  //configTime(0, 0, "pool.ntp.org", "time.nist.gov");
  configTime(0, 0, "ntp1.aliyun.com", "ntp2.aliyun.com","ntp3.aliyun.com");
  Serial.print(F("Waiting for NTP time sync: "));
  time_t nowSecs = time(nullptr);
  while (nowSecs < 8 * 3600 * 2) {
    delay(500);
    Serial.print(F("."));
    yield();
    nowSecs = time(nullptr);
  }
  Serial.println();
  struct tm timeinfo;
  gmtime_r(&nowSecs, &timeinfo);
  Serial.print(F("Current time: "));
  Serial.println(&timeinfo, "%F %T %A"); // 格式化输出:2021-10-24 23:00:44 Sunday
}
void httpsconnection(void){
  uint16_t Index;
  String writeString;
  uint8_t writeBuffer[500];
  delete client;client = new WiFiClientSecure;
  client -> setCACert(rootCACertificate);//根证书验证。
  client -> setTimeout(15000);//设置超时。
  //client -> setInsecure();//不进行身份验证(不使用加密传输)。
  Serial.printf("connect https://%s:%d%s...",host,httpsPort,path);
  client -> connect(host,httpsPort,15000);//开始连接。
  Index=100;while(!client -> connected() && Index>0){delay(100);Index--;}if(!client -> connected()){Serial.println("Connection failed");return;}else{Serial.println(" connected");}//超过10秒未建立连接则显示连接失败并退出。
  writeString=String("GET ")+path+" HTTP/1.1\r\nHost: "+host+"\r\n\r\n";
  for(Index=0;Index<writeString.length();Index++){
    writeBuffer[Index]=writeString[Index];
  }
  client -> write(writeBuffer,Index);
  Index=100;BufferIndex=0;writeString="";
  while(Index){
    delay(100);Index--;
    while(client->available()){
      writeString=writeString+char(client->read());BufferIndex++;
      if(BufferIndex>30){
        //等待服务器HTTP回应信息。
        if(writeString[BufferIndex-1]=='\n' && writeString[BufferIndex-2]=='\r' && writeString[BufferIndex-3]=='\n' && writeString[BufferIndex-4]=='\r'){
          Serial.print(writeString);
          Index=0;break;
        }
      }
      if(BufferIndex>BufferSize){Index=0;break;}//非HTTP响应,放弃并跳出。
    }
  }
  //因为第一帧数据表示后续块的大小,为配合数据处理算法,加上一个结束标志.
  Buffer[0]='\r';Buffer[1]='\n';
  BufferIndex=2;PlayIndex=0;
}
void setup() {
  uint32_t TimerOut;
  Serial.begin(115200);
  Player.begin();//Player.SetVolume(90);
  WiFi.mode(WIFI_STA);
  WiFi.begin(SSIDNAME,PASSWORD);
  Serial.print("Waiting for WiFi to connect...");
  TimerOut=0;while ((WiFi.status()!=WL_CONNECTED) && TimerOut++<60){delay(500);Serial.print(".");}
  if(TimerOut>=60){Serial.println("Connection failed");return;}else{Serial.println(" connected");}
  setClock();//同步NTP服务器时间,加密传输运算需要。
  httpsconnection();//连接网站以获取电台播放数据。
}
void loop() {
  int index;
  if(!client->connected()){Serial.printf("重新连接:\n");httpsconnection();}//如连接断开则自动重连。
  //不断读取电台数据,处理后存入缓存中。
  while(client->available()){
    Buffer[BufferIndex]=client->read();BufferIndex++;
    if(Buffer[(BufferIndex+BufferSize-1)%BufferSize]=='\n' && Buffer[(BufferIndex+BufferSize-2)%BufferSize]=='\r' && Buffer[(BufferIndex+BufferSize-6)%BufferSize]=='\n' && Buffer[(BufferIndex+BufferSize-7)%BufferSize]=='\r'){
      //int DataSize=(Buffer[(BufferIndex+BufferSize-3)%BufferSize]-48)+(Buffer[(BufferIndex+BufferSize-4)%BufferSize]-48)*16+(Buffer[(BufferIndex+BufferSize-5)%BufferSize]-48)*16*16;
      //Serial.printf("块大小:%d\n",DataSize);
      BufferIndex=(BufferIndex+BufferSize-7)%BufferSize;//去除无用的块大小指示帧及块结束标志字节(间隔字符7字节"前一帧数据\r\nxxx\r\n后一帧数据")(将各帧播放数据连接起来)
    }else{
      BufferIndex=BufferIndex%BufferSize;
    }
    //保证缓存足量数据用于播放模块。(缓存将满时将数据提供给播放模块)
    if(PlayIndex>BufferIndex && PlayIndex-BufferIndex<1024 || BufferIndex>PlayIndex && BufferIndex-PlayIndex>BufferSize-1024){break;}
    yield();//交出系统控制权。
  }
  //接收数据为空或缓存将满时都会播放缓存数据。
  if(PlayIndex>BufferIndex && PlayIndex-BufferIndex<BufferSize-128 || BufferIndex>PlayIndex && BufferIndex>PlayIndex+128){
    Player.playChunk(Buffer,BufferSize,PlayIndex,64);
    PlayIndex=(PlayIndex+64)%BufferSize;
  }
  yield();
}
void setClock() {
  //此过程更新ESP32内部时钟。
  //configTime(0, 0, "pool.ntp.org", "time.nist.gov");
  configTime(0, 0, "ntp1.aliyun.com", "ntp2.aliyun.com","ntp3.aliyun.com");
  Serial.print(F("Waiting for NTP time sync: "));
  time_t nowSecs = time(nullptr);
  while (nowSecs < 8 * 3600 * 2) {
    delay(500);
    Serial.print(F("."));
    yield();
    nowSecs = time(nullptr);
  }
  Serial.println();
  struct tm timeinfo;
  gmtime_r(&nowSecs, &timeinfo);
  Serial.print(F("Current time: "));
  Serial.println(&timeinfo, "%F %T %A"); // 格式化输出:2021-10-24 23:00:44 Sunday
}
void httpsconnection(void){
  uint16_t Index;
  String writeString;
  uint8_t writeBuffer[500];
  delete client;client = new WiFiClientSecure;
  client -> setCACert(rootCACertificate);//根证书验证。
  client -> setTimeout(15000);//设置超时。
  //client -> setInsecure();//不进行身份验证(不使用加密传输)。
  Serial.printf("connect https://%s:%d%s...",host,httpsPort,path);
  client -> connect(host,httpsPort,15000);//开始连接。
  Index=100;while(!client -> connected() && Index>0){delay(100);Index--;}if(!client -> connected()){Serial.println("Connection failed");return;}else{Serial.println(" connected");}//超过10秒未建立连接则显示连接失败并退出。
  writeString=String("GET ")+path+" HTTP/1.1\r\nHost: "+host+"\r\n\r\n";
  for(Index=0;Index<writeString.length();Index++){
    writeBuffer[Index]=writeString[Index];
  }
  client -> write(writeBuffer,Index);
  Index=100;BufferIndex=0;writeString="";
  while(Index){
    delay(100);Index--;
    while(client->available()){
      writeString=writeString+char(client->read());BufferIndex++;
      if(BufferIndex>30){
        //等待服务器HTTP回应信息。
        if(writeString[BufferIndex-1]=='\n' && writeString[BufferIndex-2]=='\r' && writeString[BufferIndex-3]=='\n' && writeString[BufferIndex-4]=='\r'){
          Serial.print(writeString);
          Index=0;break;
        }
      }
      if(BufferIndex>BufferSize){Index=0;break;}//非HTTP响应,放弃并跳出。
    }
  }
  //因为第一帧数据表示后续块的大小,为配合数据处理算法,加上一个结束标志.
  Buffer[0]='\r';Buffer[1]='\n';
  BufferIndex=2;PlayIndex=0;
}
void setup() {
  uint32_t TimerOut;
  Serial.begin(115200);
  Player.begin();//Player.SetVolume(90);
  WiFi.mode(WIFI_STA);
  WiFi.begin(SSIDNAME,PASSWORD);
  Serial.print("Waiting for WiFi to connect...");
  TimerOut=0;while ((WiFi.status()!=WL_CONNECTED) && TimerOut++<60){delay(500);Serial.print(".");}
  if(TimerOut>=60){Serial.println("Connection failed");return;}else{Serial.println(" connected");}
  setClock();//同步NTP服务器时间,加密传输运算需要。
  httpsconnection();//连接网站以获取电台播放数据。
}
void loop() {
  int index;
  if(!client->connected()){Serial.printf("重新连接:\n");httpsconnection();}//如连接断开则自动重连。
  //不断读取电台数据,处理后存入缓存中。
  while(client->available()){
    Buffer[BufferIndex]=client->read();BufferIndex++;
    if(Buffer[(BufferIndex+BufferSize-1)%BufferSize]=='\n' && Buffer[(BufferIndex+BufferSize-2)%BufferSize]=='\r' && Buffer[(BufferIndex+BufferSize-6)%BufferSize]=='\n' && Buffer[(BufferIndex+BufferSize-7)%BufferSize]=='\r'){
      //int DataSize=(Buffer[(BufferIndex+BufferSize-3)%BufferSize]-48)+(Buffer[(BufferIndex+BufferSize-4)%BufferSize]-48)*16+(Buffer[(BufferIndex+BufferSize-5)%BufferSize]-48)*16*16;
      //Serial.printf("块大小:%d\n",DataSize);
      BufferIndex=(BufferIndex+BufferSize-7)%BufferSize;//去除无用的块大小指示帧及块结束标志字节(间隔字符7字节"前一帧数据\r\nxxx\r\n后一帧数据")(将各帧播放数据连接起来)
    }else{
      BufferIndex=BufferIndex%BufferSize;
    }
    //保证缓存足量数据用于播放模块。(缓存将满时将数据提供给播放模块)
    if(PlayIndex>BufferIndex && PlayIndex-BufferIndex<1024 || BufferIndex>PlayIndex && BufferIndex-PlayIndex>BufferSize-1024){break;}
    yield();//交出系统控制权。
  }
  //接收数据为空或缓存将满时都会播放缓存数据。
  if(PlayIndex>BufferIndex && PlayIndex-BufferIndex<BufferSize-128 || BufferIndex>PlayIndex && BufferIndex>PlayIndex+128){
    Player.playChunk(Buffer,BufferSize,PlayIndex,64);
    PlayIndex=(PlayIndex+64)%BufferSize;
  }
  yield();
}

  • 1
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
VS8053 / VS1053 / VS1063开发板具有全面的硬件接口,用于编写和测试VS8053 / VS1053器件上的各种音频信号处理软件。连接器包括耳机,线路输入,线路输出,USB和UART。用户界面采用1.77“TFT显示屏和八个按键,板上的许多信号都被连接到连接器上,以帮助进行评估测量并与其他板卡连接。FM收音机模块使用耳机线作为天线,并给出了一种可能的信号选项VS8053 / VS1053器件的线路输入。 该板由USB总线供电。该板还具有用于3.7V Li-on可充电电池单元的充电器电路。串口用作调试器接口,用于将代码加载到VS8053 / VS1053。 实物截图: 目前正确的电路板功能: 立体声线路输入。 立体声线路输出和耳机输出。(与两个音频相同) FM收音机模块(PL102BC:TEA5767)。 耳机线天线/外部天线连接器。 1.77“TFT LCD模块。 8个用户界面按钮。 启动选择按钮(用于刷新SPI闪存)。 复位按钮。 扩展连接器用于连接MCU或其他IC。 USB连接器,用于为锂离子电池充电。* 锂离子电池连接器和锂离子电池充电器IC(100mA充电电流)* UART端口用于连接VSIDE USB UART电缆。 USB SD读卡器IC。 特征: 来自ADC(线路输入)的高质量16位,48 kHz立体声音频输入。 一个示例信号处理功能:一个简单的放大器。 高品质模拟立体声音频输出(线路输出/耳机)。 调频收音机模块(PL102BC:TEA5767)无线电频道调谐。 1.77“TFT LCD图形用户界面。 带有可选按钮哔声的按钮读取。 用C语言编写的音频中断处理程序。 信号预处理滤波器:直流阻塞和饱和限幅器。 源代码VSIDE集成开发环境中可用。 带完整源代码的SD卡播放器。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟渔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值