基于HTTPS长连接的ESP32+VS1053网络电台收音机Arduino代码
基于HTTPS长连接的ESP32+VS1053网络电台收音机Arduino代码
代码下载地址:ESP32+VS1053网络电台收音机Arduino完整源代码。-硬件开发文档类资源-CSDN下载
- 硬件搭建
- 本文使用的控制板两块:ESP32最小系统板,带USB转串行接口,电源模块,可向外部供电。VS1053+SD播放模块,板载耳机功放、耳机插头、线路输入插头及麦克风。如下图:
- 引脚连接如下:
* 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代码相关推荐
- Comet:基于 HTTP 长连接的“服务器推”技术解析
一.背景介绍 传统web请求,是显式的向服务器发送http Request,拿到Response后显示在浏览器页面上.这种被动的交互方式不能满足对信息实时性要求高的应用,譬如聊天室.股票交易行情.在线 ...
- [转载] Comet:基于 HTTP 长连接的“服务器推”技术
转载自http://www.ibm.com/developerworks/cn/web/wa-lo-comet/ "服务器推"技术的应用 传统模式的 Web 系统以客户端发出请求. ...
- Comet:基于 HTTP 长连接的“服务器推”技术 (实例)
"服务器推"技术的应用 传统模式的 Web 系统以客户端发出请求.服务器端响应的方式工作.这种方式并不能满足很多现实应用的需求,譬如: 1. 监控系统:后台硬件热插拔.LED.温度 ...
- Comet:基于HTTP长连接的“服务器推”技术
作者:周 婷 (zhouting@cn.ibm.com), 软件工程师, IBM 中国软件开发技术实验室 来源:http://www.ibm.com/developerworks/cn/web/wa- ...
- java comet_用java实现comet,基于 HTTP长连接的实现,用于从服务端实时发送信息到客户端...
http://homelink.javaeye.com/blog/293328#comments 参考文档 http://www.ibm.com/developerworks/cn/web/wa-lo ...
- C#中HttpClient使用注意:预热与长连接
最近在测试一个第三方API,准备集成在我们的网站应用中.API的调用使用的是.NET中的HttpClient,由于这个API会在关键业务中用到,对调用API的整体响应速度有严格要求,所以对HttpCl ...
- Nginx中保持长连接的配置
哎,老转载王了,原博客地址:https://www.cnblogs.com/kevingrace/p/9364404.html 在Nginx中,对于http1.0与http1.1是支持长连接的.htt ...
- 关于HTTP长连接、短连接、持久连接介绍
转自:微点阅读 https://www.weidianyuedu.com 什么是HTTP长连接 长连接定义: client方与server方先建立连接,连接建立后不断开,然后再进行报文发送和接收.这 ...
- HTTP学习:关于长连接、短连接、持久连接介绍
转自:微点阅读 https://www.weidianyuedu.com 什么是Http长连接 长连接定义: client方与server方先建立连接,连接建立后不断开,然后再进行报文发送和接收.这 ...
最新文章
- lambda表达式树
- c实现面向对象编程(3)
- 类的加载顺序和对象的实例化
- 陈顺志 php,芦芽山之情
- 工作流入门比较经典的文献
- web扫描工具-Nikto介绍与使用
- java私塾(java私塾初级模拟银源代码)
- PHP7.2安装vld扩展
- 手机查看云服务器文件夹,手机查看云服务器文件夹
- 使注册用户名时不能包含特殊符号
- linux cli运行脚本,AWS学习笔记(四)--CLI创建EC2时执行脚本
- 微信无法显示好友头像
- 使用 K-means 算法进行客户分类
- pos打印机指令java,如何从Delphi向POS打印机发送控制命令
- 送给计算机老师的话,送给老师的话(精选60句)
- kdb代码分析(一)
- Flexe2.0 学习笔记三(得用STATE实现一个组件登录窗体)
- 外卖跑腿平台是靠什么赚钱的
- 区块链阴谋与骗局 贴吧
- Jira的详细安装方法