earlephilhower / ESP8266Audio

Arduino library to play MOD, WAV, FLAC, MIDI, RTTTL, MP3, and AAC files on I2S DACs or with a software emulated delta-sigma DAC on the ESP8266 and ESP32
GNU General Public License v3.0
1.99k stars 433 forks source link

Reduce time to change URL on ESP32 #599

Open Totoche95 opened 1 year ago

Totoche95 commented 1 year ago

Hello, I made a kitchen radio with 20 (soon 5 x 20) stations with esp8266audio librairy and an exemple of web radio. I add a potentiometer to select station with analogRead and some code to copy ssid/password and stations in eeprom by UDP. I begin with a esp8266 and because of memory i use now an esp32, more stable because not in limit of memory. It works fine ,and i thank for this librairy . Just having two little problems. 1) On esp32 (not on esp8266) before changing url , i close mp3, buff and file the esp32 reply by UNINSTALL I2S and it takes 4 seconds to hear new station, not on esp8266. Is it possible to reduce this time on esp32. 2) On French public service stations (only) it cut the words, some milliseconds are missing in audio, theses stations are all on the same server and they are knowed to produce problem on some internet radio, not on all and not on PC or Android , i get an esp32 also to increase buffer size but the problem is the same. Is there a solution for theses radios ?

Two of French public service radio that have the problem http://direct.franceinter.fr/live/franceinter-lofi.mp3 http://direct.francemusique.fr/live/francemusique-lofi.mp3 same exist with midfi in place of lofi, same problem.

Cheers

Here is the code of my radio (need a potentiometer on analog port , a button on port 14, a led on port 16) If it is usefull, i can translate comments to English

// WEB RADIO AVEC ESP32 // Programmer l'ESP avec 160 Mhz de freq CPU , 26 Mhz Crystal //https://www.hackster.io/RoboticaDIY/esp8266-how-to-make-wi-fi-radio-5bee14 //WiFi.setOuputPower() permet de regler la puissance emise par l'ESP8266 //https://github.com/G6EJD/ESP32-8266-Adjust-WiFi-RF-Power-Output/blob/main/README.md //Si on sort vers un ampli classique un RC 1 KOhms - 10 nF donne 16 Khz de freq de coupure, devrait convenir.

// https://github.com/earlephilhower/ESP8266Audio/issues/465 // AudioOutputI2SNoDAC() sur esp32 // The default pin for NoDAC on ESP32 is pin 22. // the pin can be selected, default on 22 // change the pin eg out->SetPinout(26, 25, 22); sets it on pin 22 // out->SetPinout(26, 25, 3) sortie audio sur RX (GPIO3) esp32 comme sur esp8266 // out = AudioOutputI2SNoDAC(); // Sound quality is really good with the standard oversampling 32. And even better with oversampling 64.

// Le pb de wifi et usb sur espduino-32 vient de l'absence de mise à niveau 5V/3.3V entre le CH340 et l ESP32 // Solution : resistance de 4.7K entre GND et IOC derniere broche du connecteur alim de l esp32 (3.04V sur sortie au lieu de 4.1V) // + condensateur 10 micro-farads entre masse et reset esp32 // GND ----- 4.7K ------- IOC // GND ----- 10µF ------- RST

include

include

include

ifdef ESP32

include

else

include

endif

//#include "AudioFileSourceICYStream.h"

include "AudioFileSourceICYStream.h"

include "AudioFileSourceBuffer.h"

include "AudioGeneratorMP3.h"

include "AudioOutputI2SNoDAC.h"

WiFiUDP udp;

const uint32_t bufferSize = 16 1024; // buffer reception flux audio pour AudioFileSourceBuffer() const int pas = 200; //valeur du convertisseur pour passer à la station suivante const int led1 = 16; // 16 sur esp32 meme place que 14 sur esp8266 const int bouton1 = 14; //14 sur esp32 = 13 sur esp8266 char packetBuffer[100]; const unsigned int localPort = 9999; char ssid; char *password; int i; int r; int len; int adresse; int sensorValue; // variable to store the value coming from the sensor int old_sens = 50; int sv;int old_sv; int conf = 0; int etat_bouton1; int en_config = 0; int pas_radio = 0; int oldcode; boolean ii; boolean connecte = 0; boolean debug = 0;

// To run, set your ESP8266 build to 160MHz, update the SSID info, and upload.

/*1 http://start-sud.ice.infomaniak.ch:80/start-sud-high\0"; // sud-radio 2 http://direct.franceinter.fr/live/franceinter-lofi.mp3\0"; // >>>>France inter http://direct.franceculture.fr/live/franceculture-lofi.mp3\0"; // France culture 4 http://direct.francemusique.fr/live/francemusique-lofi.mp3\0"; // France musique 6 http://radioclassique.ice.infomaniak.ch/radioclassique-high.mp3\0"; // Radio classique 9 http://stream.radiojar.com/kokkino-ath.mp3\0"; // grece 10 http://radiostreaming.ert.gr/ert-trito\0"; // grece 11 http://s3.onweb.gr:8878\0"; // grece

http://scdn.nrjaudio.fm/adwz2/fr/30601/mp3_128.mp3 nostagie http://nepatriots.streamguys1.com:80/live.mp3 //usa http://maggie.torontocast.com:9012/stream224 //nashville http://noasrv.caster.fm:10099/128mp3_autodj http://88.212.34.18:8050/mp3lowband http://s2.euer.tv:80/audio192 http://live.fresh927.com.au:80/freshmp3

http://s3.onweb.gr:8870 // grece http://s3.onweb.gr:8872 http://s3.onweb.gr:8012 http://s4.onweb.gr:8442 http://s4.onweb.gr:8468 */

char tampon[100]; char *URL = tampon; char buf1[100];

AudioFileSourceICYStream file; AudioFileSourceBuffer buff; AudioGeneratorMP3 mp3; AudioOutputI2SNoDAC out;

// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc. void MDCallback(void cbData, const char type, bool isUnicode, const char string) { const char ptr = reinterpret_cast<const char *>(cbData); (void) isUnicode; // Punt this ball for now // Note that the type and string may be in PROGMEM, so copy them to RAM for printf char s1[32], s2[64]; strncpy_P(s1, type, sizeof(s1)); s1[sizeof(s1)-1]=0; strncpy_P(s2, string, sizeof(s2)); s2[sizeof(s2)-1]=0; Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2); Serial.flush(); if (connecte == 1) { udp.beginPacket(udp.remoteIP(), udp.remotePort()); udp.printf(s2); udp.endPacket();} }

// Called when there's a warning or error (like a buffer underflow or decode hiccup) void StatusCallback(void cbData, int code, const char string) { const char ptr = reinterpret_cast<const char >(cbData); // Note that the string may be in PROGMEM, so copy it to RAM for printf if (code != oldcode) { char s1[64]; strncpy_P(s1, string, sizeof(s1)); s1[sizeof(s1)-1]=0; Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1); Serial.flush();} oldcode = code; }

void setup() { int i0=0; ii = LOW; pinMode(led1, OUTPUT); //pinMode(bouton1,INPUT_PULLUP); EEPROM.begin(4096); //Initialise EEPROM 256 * 16 caracteres Serial.begin(115200); delay(1000); Serial.print(F("Web radio, envoyer connexion en UDP sur le port 9999 pour nom des stations, memoire pour liste des stations en memoire")); //i=digitalRead(bouton1); //if(i == 0) {Serial.print(F("Passage en mode configuration ssid et URL radios en UDP sur 192.168.4.1 ssid:AC-ESP8266 mot de passe:motdepasse"));connexion();} WiFi.disconnect(); WiFi.softAPdisconnect(true); WiFi.mode(WIFI_STA); WiFi.hostname("ESP-Radio"); // nom pour le DNS lit_EEPROM(0); // lit ssid/password en eeprom adr 0 WiFi.begin(ssid, password); // Try forever while (i0 < 12 && (WiFi.status() != WL_CONNECTED)) { Serial.println("...Connecting to WiFi"); i=digitalRead(bouton1); if(i == 0) {Serial.print(F("Passage en mode configuration ssid et URL radios en UDP port 9999 sur 192.168.4.1 ssid:AC-ESP8266 mot de passe:motdepasse"));connexion();} i0++; delay(1000); } if (WiFi.status() == WL_CONNECTED) {Serial.print("Connected :");Serial.println(WiFi.localIP());} else {Serial.print(F("Pas de connexion wifi. Passage en mode configuration ssid et URL radios en UDP sur 192.168.4.1 ssid:AC-ESP8266 mot de passe:motdepasse")); Serial.print(F("Puis appuyer 2 secondes sur bouton pour revenir en radio")); connexion();}

WiFi.setTxPower(WIFI_POWER_17dBm); // Sur esp32 Regle la puissance emise sur +17db soit 50 mW (reglable de 0db à 20 db soit 1mW à 100mW) par defaut le 8266 sort +25db (ecrit dessus) soit 300 mW ! udp.begin(localPort); sensorValue=analogRead(36); if(sensorValue > 3760) sensorValue = 3760; // 4096 max, 12 bits adresse = (sensorValue/pas + 1) * 100; choix_station(); }

void StopPlaying() { if (out) out->stop(); if (mp3) { mp3->stop(); delete mp3; mp3 = NULL; } if (buff) { buff->close(); delete buff; buff = NULL; } if (file) { file->close(); delete file; file = NULL; } }

void connexion(void) { ssid = "AC-ESP8266"; password = "motdepasse"; IPAddress local_IP(192,168,4,1); IPAddress gateway(192,168,4,1); IPAddress subnet(255,255,255,0);
WiFi.softAPConfig({192,168,4,1}, {192,168,4,1}, {255,255,255,0}); // local IP, gateway, subnet de reseau wifi de ESP8266 WiFi.softAP(ssid, password); udp.begin(localPort); while (conf < 1200 && en_config < 5) {ecoute_UDP(); conf++;} // en boucle pour entrer ssid/passe en adr 0 puis stations radios en adr 1 à 25 600 secondes soit 10 minutes (l'ESP chauffe un peu en mode serveur) ESP.restart(); }

void choix_station() { //voir https://registry.platformio.org/libraries/earlephilhower/ESP8266Audio old_sens=sensorValue; Serial.println(sensorValue); digitalWrite(led1, LOW); // pas de reception if (adresse != 99) lit_EEPROM(adresse); Serial.println(URL); if (connecte == 1) { udp.beginPacket(udp.remoteIP(), udp.remotePort()); udp.printf(URL); // Ecrit l URL de la station udp.endPacket();} audioLogger = &Serial; // peripherique d'erreur -> serie StopPlaying(); file = new AudioFileSourceICYStream(URL); if(!file->isOpen()) {Serial.println("Pas de flux pour cette URL, premiere station selectionnee ");lit_EEPROM(100);file = new AudioFileSourceICYStream(URL);} if (debug == 1) file->RegisterStatusCB(StatusCallback, (void)"file"); file->RegisterMetadataCB(MDCallback, (void)"ICY"); buff = new AudioFileSourceBuffer(file, bufferSize); if (debug == 1) buff->RegisterStatusCB(StatusCallback, (void)"buffer"); out = new AudioOutputI2SNoDAC(); out->SetPinout(26, 25, 3); // sortie sur pin 3 (RX0) (x,x,3) au lieu de 22 par defaut sur ESP if (debug == 1) {Serial.print("Memoire libre en milieu de choix_station "); // 35ko Serial.println(ESP.getFreeHeap());} // //out->SetOutputModeMono(true); //out->SetGain(1.0); mp3 = new AudioGeneratorMP3(); if (debug == 1) mp3->RegisterStatusCB(StatusCallback, (void)"mp3"); //file->SetReconnect(3, 500); mp3->begin(buff, out); if (debug == 1) {Serial.print("Memoire libre en fin de choix_station "); // 4ko Serial.println(ESP.getFreeHeap());} }

void lit_EEPROM(int adresse) { i=0;r=0; char Buffer[100]; EEPROM.get(adresse,Buffer); if(adresse == 0) // ssid et mot de passe en adr 0 { while(Buffer[i] != ' ') {buf1[i] = Buffer[i]; i++;} buf1[i] = '\0';i++; while(Buffer[i] != '\0') {tampon[r] = Buffer[i]; i++;r++;} tampon[r] = '\0'; ssid = buf1; password = tampon; } else { while(Buffer[i] != '\0') {tampon[i] = Buffer[i]; i++;} // lit l URL de la station tampon[i] = '\0'; } }

void liste_mem() { adresse = 100; while (adresse < 2000)
{ lit_EEPROM(adresse); if ((strlen(URL) > 10) && (strlen(URL) < 100)) { udp.beginPacket(udp.remoteIP(), udp.remotePort()); sprintf(buf1,"%d",adresse/100); udp.printf(buf1);udp.printf(" "); udp.printf(URL); // Ecrit les URL en memoire udp.endPacket();} adresse = adresse + 100; delay(200); }

}

void ecoute_UDP() { char nb[100]; char a; i=0;r = 0; ii = !ii; digitalWrite(led1, ii); int packetSize = udp.parsePacket(); if (packetSize > 4) { len = udp.read(packetBuffer, 100); if(packetBuffer[0] == 'a') { sprintf(buf1, "%d.%d.%d.%d ", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]); // transforme IP locale en chaine de caracteres udp.beginPacket(udp.remoteIP(), udp.remotePort()); udp.printf("Connexion sur : "); udp.printf(buf1); // Retourne l'adresse de la carte au programme appelant udp.endPacket(); return;} if(packetBuffer[0] == 'm') {liste_mem();return;} if (len > 10) { packetBuffer[len] = '\0'; while (packetBuffer[i] != ' ') {nb[i] = packetBuffer[i];i++;} nb[i]='\0';i++; adresse = atoi(nb) * 100; while (packetBuffer[i] != '\0') { a = packetBuffer[i]; if (a == '\n' || a == '\r') a = '\0'; tampon[r] = a; i++;r++;}
tampon[r] = '\0'; if (adresse == 0) if (nb[0] != '0') return; EEPROM.put(adresse,tampon);
Serial.print(" Ecriture en EEPROM "); Serial.print(adresse);Serial.println(URL); EEPROM.commit(); lit_EEPROM(adresse); udp.beginPacket(udp.remoteIP(), udp.remotePort()); if (adresse != 0) udp.printf(tampon); else udp.printf("SSID et mot de passe enregistres"); udp.endPacket(); } } etat_bouton1=digitalRead(bouton1); if(etat_bouton1 == 0) en_config++; delay(500); }

void config(void) { Serial.println(); Serial.print(F("Passage en mode configuration pour entrer les URL des radios en UDP sur le port 9999 sur : ")); Serial.print(WiFi.localIP()); Serial.print(F(" ou sur ESP-Radio si votre moniteur UDP accepte les noms de domaines")); Serial.print(F("Envoyer le mot adresse en UDP sur l adresse xxx.xxx.xxx.255 port 9999 pour connaitre l'adresse depuis le reseau, envoyer le mot memoire pour lister la memoire stations")); Serial.print(F("Appuyer 2 secondes sur le bouton pour revenir en radio")); while (conf < 2400 && en_config < 5) {ecoute_UDP(); conf++;} // = 1200 secondes soit 20 mn ou 2 secondes sur bouton pour revenir en radio Serial.println(URL); conf = 0;en_config = 0; }

void loop() { char a; static int lastms = 0; if (mp3->isRunning()) { pas_radio = 0; if (millis()-lastms > 200) { i=0; if(!file->isOpen()) {Serial.println("Pas de flux pour cette URL, premiere station selectionnee ");lit_EEPROM(100);file = new AudioFileSourceICYStream(URL);} int packetSize = udp.parsePacket(); if (packetSize > 4) { len = udp.read(packetBuffer, 100); while (packetBuffer[i] != '\0') { a = packetBuffer[i]; if (a == '\n' || a == '\r') a = '\0'; tampon[i] = a; i++;}
tampon[i] = '\0'; if(tampon[0] == 'm' && tampon[2] == 'm') {connecte = 1;liste_mem(); return;} // memoire , liste la memoire stations .connecte, enverra les URL des stations en UDP au PC ou tel connecte if(tampon[0] == 'c' && tampon[1] == 'o') {connecte = 1; return;} // connecte, envrra les URL des stations en UDP au PC ou tel connecte if(tampon[0] == 'd' && tampon[1] == 'e') {debug = 1; return;} // mode debug, affiche les infos sur serial if(tampon[0] == 'p' && tampon[1] == 'r') {config(); return;} // programme les memoires adresse = 99; choix_station(); } etat_bouton1=digitalRead(bouton1); if(etat_bouton1 == 0) conf++; else conf = 0; if(conf > 10) {conf = 0;config();} digitalWrite(led1, HIGH); sensorValue=analogRead(36); if(sensorValue > 3760) sensorValue = 3760; adresse = (sensorValue/pas + 1) 100; sv = (sensorValue/pas) pas; if (old_sens/pas != sensorValue/pas) if (sensorValue > sv + 60 && sensorValue < sv + 140) choix_station(); //if (old_sens/pas != sensorValue/pas) if (sensorValue < old_sv - 20 || sensorValue > old_sv + 20) choix_station(); lastms = millis(); //Serial.printf("Running for %d ms...\n", lastms); //Serial.flush(); } if (!mp3->loop()) { mp3->stop();digitalWrite(led1, LOW);Serial.print("Redemarrage MP3 loop->stop");delay(1000);adresse = 100;ESP.restart();} } else { if(!file->isOpen()) {Serial.println("Pas de flux pour cette URL, premiere station selectionnee ");lit_EEPROM(100);file = new AudioFileSourceICYStream(URL);} digitalWrite(led1, LOW); // pas de reception Serial.println("MP3 done"); if(pas_radio > 6) {Serial.print("Redemarrage MP3 done");delay(1000);ESP.restart();} sensorValue=analogRead(36); if(sensorValue > 3760) sensorValue = 3760; adresse = (sensorValue/pas + 1) 100; //old_sv = (old_sens/pas) pas; sv = (sensorValue/pas) * pas; if (old_sens/pas != sensorValue/pas) if (sensorValue > sv + 60 && sensorValue < sv + 140) choix_station(); pas_radio++; delay(500); } }