基于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;     //D18uint8_t mosi_pin=23;    //D23uint8_t miso_pin=19;    //D19//以下为硬件连接设置.uint8_t cs_pin=22;      //D22uint8_t dcs_pin=21;     //D21uint8_t dreq_pin=15;    //D15uint8_t reset_pin=34;   //ENconst 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~100int8_t curbalance;                      //音量平衡-100~+100uint8_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.mp3WiFiClientSecure *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();
}

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

  1. Comet:基于 HTTP 长连接的“服务器推”技术解析

    一.背景介绍 传统web请求,是显式的向服务器发送http Request,拿到Response后显示在浏览器页面上.这种被动的交互方式不能满足对信息实时性要求高的应用,譬如聊天室.股票交易行情.在线 ...

  2. [转载] Comet:基于 HTTP 长连接的“服务器推”技术

    转载自http://www.ibm.com/developerworks/cn/web/wa-lo-comet/ "服务器推"技术的应用 传统模式的 Web 系统以客户端发出请求. ...

  3. Comet:基于 HTTP 长连接的“服务器推”技术 (实例)

    "服务器推"技术的应用 传统模式的 Web 系统以客户端发出请求.服务器端响应的方式工作.这种方式并不能满足很多现实应用的需求,譬如: 1. 监控系统:后台硬件热插拔.LED.温度 ...

  4. Comet:基于HTTP长连接的“服务器推”技术

    作者:周 婷 (zhouting@cn.ibm.com), 软件工程师, IBM 中国软件开发技术实验室 来源:http://www.ibm.com/developerworks/cn/web/wa- ...

  5. java comet_用java实现comet,基于 HTTP长连接的实现,用于从服务端实时发送信息到客户端...

    http://homelink.javaeye.com/blog/293328#comments 参考文档 http://www.ibm.com/developerworks/cn/web/wa-lo ...

  6. C#中HttpClient使用注意:预热与长连接

    最近在测试一个第三方API,准备集成在我们的网站应用中.API的调用使用的是.NET中的HttpClient,由于这个API会在关键业务中用到,对调用API的整体响应速度有严格要求,所以对HttpCl ...

  7. Nginx中保持长连接的配置

    哎,老转载王了,原博客地址:https://www.cnblogs.com/kevingrace/p/9364404.html 在Nginx中,对于http1.0与http1.1是支持长连接的.htt ...

  8. 关于HTTP长连接、短连接、持久连接介绍

    转自:微点阅读  https://www.weidianyuedu.com 什么是HTTP长连接 长连接定义: client方与server方先建立连接,连接建立后不断开,然后再进行报文发送和接收.这 ...

  9. HTTP学习:关于长连接、短连接、持久连接介绍

    转自:微点阅读  https://www.weidianyuedu.com 什么是Http长连接 长连接定义: client方与server方先建立连接,连接建立后不断开,然后再进行报文发送和接收.这 ...

最新文章

  1. lambda表达式树
  2. c实现面向对象编程(3)
  3. 类的加载顺序和对象的实例化
  4. 陈顺志 php,芦芽山之情
  5. 工作流入门比较经典的文献
  6. web扫描工具-Nikto介绍与使用
  7. java私塾(java私塾初级模拟银源代码)
  8. PHP7.2安装vld扩展
  9. 手机查看云服务器文件夹,手机查看云服务器文件夹
  10. 使注册用户名时不能包含特殊符号
  11. linux cli运行脚本,AWS学习笔记(四)--CLI创建EC2时执行脚本
  12. 微信无法显示好友头像
  13. 使用 K-means 算法进行客户分类
  14. pos打印机指令java,如何从Delphi向POS打印机发送控制命令
  15. 送给计算机老师的话,送给老师的话(精选60句)
  16. kdb代码分析(一)
  17. Flexe2.0 学习笔记三(得用STATE实现一个组件登录窗体)
  18. 外卖跑腿平台是靠什么赚钱的
  19. 区块链阴谋与骗局 贴吧
  20. Jira的详细安装方法

热门文章

  1. 解决 PageInfo 返回的 total 不正确
  2. 软件盗版被查怎么办?
  3. onkeyup,onkeydown和onkeypress区别
  4. 东方博宜OJ题目全解(1006~1010)
  5. android圆形头像:相机相册加载图片到圆形头像
  6. 车辆检测数据集(2)
  7. MBR、EBR、DBR是什么
  8. docker php-fpm 优化,利用docker-compose快速部署php-fpm+nginx环境
  9. 经典ERP(家庭版)
  10. Fed Keeps Its Helping Hand On Economy