Navigation

    M5Stack Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    1. Home
    2. homeuser33
    H
    • Continue chat with homeuser33
    • Start new chat with homeuser33
    • Flag Profile
    • Profile
    • Following
    • Followers
    • Blocks
    • Topics
    • Posts
    • Best
    • Groups
    Save
    Saving

    homeuser33

    @homeuser33

    2
    Reputation
    9
    Posts
    1364
    Profile views
    0
    Followers
    0
    Following
    Joined Last Online

    homeuser33 Follow

    Posts made by homeuser33

    • RE: Updated version, working Core2 web radio player, M5Stack

      @raychmond
      Thanks for the update, It's been awhile sine I added the Core2 board manager so I don't remember which source I used. If I find it I'll update here. Thank you.

      posted in Core 2
      H
      homeuser33
    • RE: Updated version, working Core2 web radio player, M5Stack

      @world101
      I'm not sure what to say about that, PSRAM is enabled on my Linux setup and so far seems to be working.

      posted in Core 2
      H
      homeuser33
    • RE: Updated version, working Core2 web radio player, M5Stack

      @world101

      Okay, that seemed easier than I thought.
      Link: https://github.com/bwbguard/M5Stack-Core2-MediaPlayer

      posted in Core 2
      H
      homeuser33
    • RE: Updated version, working Core2 web radio player, M5Stack

      @world101
      No, It's a good point. Generally I have not shared much code since I'm still fairly new to the M5Stack hardware. I was thinking this would be a one off since I found the answer for the media player. But its a good point. I'll check into that.
      Thank you.

      posted in Core 2
      H
      homeuser33
    • RE: Updated version, working Core2 web radio player, M5Stack

      @raychmond
      Thank you, I've added the include now in the program, not sure why it worked for me without it.

      I'll make the changes in the program listing above. Thank you.

      Yes, I like Charlie FM : https://worldradiomap.com/us-or/play/kych_live
      If you click the 'Listen with your player' button it downloads a .PLS file that has links to their station. I have not found many good stations with this option.

      https://www.radio.com/charliefm

      posted in Core 2
      H
      homeuser33
    • RE: CORE2 as Web radio, without extra hardware ?

      Sorry, I obviously don't know what I'm doing the the 'Reply' option and I guess I made another topic. I put new code here instead of a reply to this thread.
      (oops).
      https://forum.m5stack.com/topic/3128/updated-version-working-core2-web-radio-player-m5stack

      posted in Core 2
      H
      homeuser33
    • Updated version, working Core2 web radio player, M5Stack

      Re: [CORE2 as Web radio](without extra hardware ?)

      I made a few GUI changes, and checked into the library used for the decoding.

      I think it's worth noting that the actual library for the functions came from:
      Earle Philhower III
      Who credits his library in part to : StellaPlayer and libMAD
      https://github.com/earlephilhower/ESP8266Audio

      Looking through the library so far I can not tell if there is extra information extracted from the stream that we can make use of.

      Here is the latest code:

      /*
         March 28, 2021
         Made changes to the Example Core2 Web Radio Player from:
         https://www.hackster.io/tommyho/arduino-web-radio-player-c4cb23
      
          Wish List: 
          slightly organized the code into better sections (Done)
          Nicer Fonts (Done)
          Use 2nd CPU Core to handle GUI and button's so we keep it responsive
          Add bluetooth output to Bluetooth speakers / headest
          Get more information from the stream,  Album art, etc.
            --  Checked into what comes into the stream So far I don't see extra data
          Get track length and have a progress bar for the track
            -- Again I don't see if this information comes in from the library
          Have an interface to select WiFi network and enter password
          -- Store password in EEPROM
          Better mehod of selecting stations
          ? Maybe web interface for setting up station lists?
      
          I see that the actual library for the functions came from:
          Earle Philhower III
          Who credits his library to :  StellaPlayer and libMAD
          https://github.com/earlephilhower/ESP8266Audio
      
      
      */
      
      //
      //  m5StreamTest Version 2020.12b (Source/Buffer Tester)
      //  Board: M5StackCore2 (esp32)
      //  Author: tommyho510@gmail.com
      //  Required: Arduino library ESP8266Audio 1.60
      //
      
      #include <M5Core2.h>
      #include <driver/i2s.h>
      #include <WiFi.h>
      #include <AudioFileSourceICYStream.h>
      #include <AudioFileSource.h>
      #include <AudioFileSourceBuffer.h>
      #include <AudioFileSourceSPIRAMBuffer.h>
      #include <AudioGeneratorMP3.h>
      #include <AudioOutputI2S.h>
      #include "Free_Fonts.h"
      //#include <spiram-fast.h>
      
      const int bufferSize = 128 * 1024; // buffer size in byte
      
      
      // Enter your WiFi, Station, button settings here:
      
      const char *SSID = "ENTER_SSID_HERE";
      const char *PASSWORD = "ENTER_WIFI_PASSWORD_HERE";
      
      
      // Added Charlie FM in Portland Oregon
      //http://24083.live.streamtheworld.com:80/KYCHFM_SC
      //
      //Removed these from the list:
      //  {"Mega Shuffle", "http://jenny.torontocast.com:8134/stream"},
      //  {"Way Up Radio", "http://188.165.212.154:8478/stream"},
      //  {"Asia Dream", "https://igor.torontocast.com:1025/;.-mp3"},
      //  {"KPop Way Radio", "http://streamer.radio.co/s06b196587/listen"},
      //  {"SomaFM", "http://ice2.somafm.com/christmas-128-mp3"}
      
      const int stations = 7;// Change Number here if you add feeds!
      char * stationList[stations][2] = {
        {"Charlie FM", "http://24083.live.streamtheworld.com:80/KYCHFM_SC"},
        {"MAXXED Out", "http://149.56.195.94:8015/steam"},
        {"Orig. Top 40", "http://ais-edge09-live365-dal02.cdnstream.com/a25710"},
        {"Smooth Jazz", "http://sj32.hnux.com/stream?type=http&nocache=3104"},
        {"Smooth Lounge", "http://sl32.hnux.com/stream?type=http&nocache=1257"},
        {"Classic FM", "http://media-ice.musicradio.com:80/ClassicFMMP3"},
        {"Lite Favorites", "http://naxos.cdnstream.com:80/1255_128"}
      };
      
      float audioGain = 0.0;
      float gainfactor = 0.08;
      int currentStationNumber = 0;
      unsigned long disUpdate = millis();
      
      AudioGeneratorMP3 *mp3;
      AudioFileSourceICYStream *filemp3;
      AudioFileSourceBuffer *buffmp3;
      AudioOutputI2S *out, *outmp3;
      
      
      // Draw a + mark centred on x,y
      void drawDatumMarker(int x, int y)
      {
        M5.Lcd.drawLine(x - 5, y, x + 5, y, TFT_GREEN);
        M5.Lcd.drawLine(x, y - 5, x, y + 5, TFT_GREEN);
      }
      
      /// WIFI Routines *********************
      
      void initwifi() {
      
        M5.Lcd.setTextColor(TFT_BLUE, TFT_BLACK);
        
        M5.Lcd.setTextSize(2);
        M5.Lcd.setTextDatum(BC_DATUM);
        M5.Lcd.setFreeFont(FSB12);   
        M5.Lcd.drawString("Connecting..", M5.Lcd.width()/2, 200, GFXFF);
      
        WiFi.disconnect();
        WiFi.softAPdisconnect(true);
        WiFi.mode(WIFI_STA);
        WiFi.begin(SSID, PASSWORD);
        // Try forever
        int i = 0;
        while (WiFi.status() != WL_CONNECTED) {
          Serial.print("STATUS(Connecting to WiFi) ");
          delay(1500);
          i = i + 1;
          if (i > 15) {
            ESP.restart();
          }
        }
        Serial.println("\nWiFi Connected!\n");
      }
      
      
      // Display network information on the LCD
      void displayWiFiInformation() {
        M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
        M5.Lcd.setTextSize(1);
        M5.Lcd.setFreeFont(FSS9);    
        M5.Lcd.setTextDatum(BL_DATUM); 
        M5.Lcd.drawString("Network: ", 10, 165, GFXFF);
        M5.Lcd.drawString("IP: " , 10, 190, GFXFF);
        M5.Lcd.drawString(SSID, 90, 165, GFXFF);
        M5.Lcd.drawString(WiFi.localIP().toString(),40,190,GFXFF);
      }
      
      
      // Update WiFi Signal Strength
      void updateWiFiSignal() {
        // Display the WiFi Signal Strength
        M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
        M5.Lcd.setTextSize(1);
        M5.Lcd.setFreeFont(FSS9);    
        M5.Lcd.setTextDatum(BL_DATUM); 
        M5.Lcd.drawString("WiFi Signal: ", 10, 215, GFXFF);
        M5.Lcd.fillRect(112,195,30,20,BLACK);
        uint16_t clr = GREEN;
        clr = (WiFi.RSSI() < -70) ? TFT_RED : TFT_GREEN;
        M5.Lcd.setTextColor(clr, TFT_BLACK);  
        M5.Lcd.drawString(String(WiFi.RSSI()),115, 215, GFXFF);
      }
      
      
      /// Battery ***************************
      
      // Calculate Battery Useable range  (3.2 to 4.1 Volts)
      void displayBattery() {
        M5.Lcd.setTextSize(1);
        M5.Lcd.setFreeFont(FSS9);  
        int maxVolts = 410;  // Battery Max volts * 100
        int minVolts = 320;  // Battery Min Volts * 100
        M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
        
        M5.Lcd.setTextSize(1);
        char battInfo[5];
        dtostrf(M5.Axp.GetBatVoltage(),1,2,battInfo); 
        String btInfo = "Batt: " + String(battInfo);
        M5.Lcd.setTextDatum(BL_DATUM);  
        M5.Lcd.drawString(btInfo, 230, 215, GFXFF);
      //  drawDatumMarker(230,215);
          
        int batt = map(M5.Axp.GetBatVoltage() * 100, minVolts, maxVolts, 0 , 10000) / 100.0;
      
        // Draw Battery bar(s) on the right side of the screen
        uint16_t clr = GREEN;
        for (int x = 9; x >= 0; x--) {
          if (x < 3) clr = RED;
          else if (x < 6) clr = YELLOW;   
          M5.Lcd.fillRoundRect(314, (216 - (x * 24)), 6, 21, 2, (batt > (x * 10)) ? clr : BLACK);
          M5.Lcd.drawRoundRect(314, (216 - (x * 24)), 6, 21, 2, TFT_LIGHTGREY);
        }
      }
      
      
      // MISC  ****************************
      
      
      // Remove the Track information (While changing stations)
      void clearTrack() {
        M5.Lcd.fillRect(10, 55, 300, 70, TFT_DARKGREY); // Clear the area of old data
        M5.Lcd.drawRect(10, 55, 300, 70, BLUE); // Draw a box around the Track Information
      }
      
      
      // Identify buttons at the bottom of screen
      void drawButtons() {
        M5.Lcd.fillRect(10,220,300,25,YELLOW);
        M5.Lcd.setTextColor(TFT_BLACK);  
        M5.Lcd.setTextSize(1);
        M5.Lcd.setTextDatum(TC_DATUM);
        M5.Lcd.setFreeFont(FSB12);   
        M5.Lcd.drawString("Volume", 55,220, GFXFF);
        M5.Lcd.drawString("Station", M5.Lcd.width()/2,220, GFXFF);
        M5.Lcd.drawString("Mute", 270 ,220, GFXFF);  
      }
      
      
      // Get the Split String Value Used for Band or Track
      String getValue(String data, char separator, int index) {
        int found = 0;
        int strIndex[] = {0, -1};
        int maxIndex = data.length() - 1;
      
        for (int i = 0; i <= maxIndex && found <= index; i++) {
          if (data.charAt(i) == separator || i == maxIndex) {
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i + 1 : i;
          }
        }
        return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
      }
      
      
      // MP3, Audio etc.  ****************************
      
      // 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;
      
        String band  = getValue(s2, '-', 0);
        band.trim();
        String track = getValue(s2, '-', 1);
        track.trim();
      
        if(band.length() > 30) band = band.substring(0, 30);
        if(track.length() > 30) track = track.substring(0, 30);
      
      //  Serial.printf("Band: %s   Track:  %s  \n", band.c_str(), track.c_str());
        Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2);
        M5.Lcd.setTextSize(1);
      
        M5.Lcd.setTextColor(TFT_BLACK, TFT_DARKGREY );
        
        clearTrack();
        M5.Lcd.setTextDatum(MC_DATUM);
        M5.Lcd.setFreeFont(FSS9); 
        if(band.length() < 20) M5.Lcd.setFreeFont(FSS12);                              // Select the font
        M5.Lcd.drawString(band, M5.Lcd.width()/2, 72, GFXFF);
        M5.Lcd.setFreeFont(FSS9);
        if(track.length() < 20) M5.Lcd.setFreeFont(FSS12);
      //  M5.Lcd.setTextDatum(MC_DATUM);
        M5.Lcd.drawString(track, M5.Lcd.width()/2, 107, GFXFF);
        Serial.flush();
        // Make sure the new song information does not overwrite the battery
        displayBattery();
      }
      
      
      // 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
        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();
      }
      
      
      void stopPlaying() {
        Serial.printf("Stopping MP3...\n");
        if (mp3) {
          mp3->stop();
          delete mp3;
          mp3 = NULL;
        }
        Serial.printf("MP3 Stopped, Stopping Buffer...\n");
        if (buffmp3) {
          buffmp3->close();
          delete buffmp3;
          buffmp3 = NULL;
        }
        Serial.printf("Buffer stopped... Stopping File ...\n");
        if (filemp3) {
          filemp3->close();
          delete filemp3;
          filemp3 = NULL;
        }
        if (outmp3) {
          //    filemp3->close();
          delete outmp3;
          outmp3 = NULL;
        }
      
        Serial.printf("STATUS(Stopped)\n");
        Serial.flush();
      }
      
      
      // Update the Station Label
      void updateStation(String message) {
        M5.Lcd.fillRect(10, 10, 300, 35, BLACK); // Clear out other information on the line
        M5.Lcd.setTextColor(TFT_GREEN, TFT_BLACK);
        M5.Lcd.setTextSize(1);
        M5.Lcd.setTextDatum(TC_DATUM);
      
        M5.Lcd.setFreeFont(FSB18);
        M5.Lcd.drawString(message, M5.Lcd.width()/2, 10, GFXFF);
      }
      
      
      // Change to the next station
      void changeStation() {
        updateStation("Changing..Wait..");
        currentStationNumber++;
        if (currentStationNumber >= stations) currentStationNumber = 0;
        Serial.printf("\n******** Changing to channel number: %i\n", currentStationNumber);
      }
      
      
      // Change the volume level
      // Update the volume graphic
      void changeVolume() {
        audioGain += 1.0;
        if (audioGain > 10.0) {
          audioGain = 1.0;
        }
        if (audioGain < 0.0) {
          audioGain = 0.0;
        }
        
        int xtPos = 260; // X Position for the Volume indication
        outmp3->SetGain(audioGain * gainfactor); // Change Volume to new level
      
      //---------New Volume Bar on left side of LCD  *******************
        // Draw Volume bar(s) on the left side of the screen
        uint16_t clr = RED;
        for (int x = 9; x >= 0; x--) {
          if (x < 5) clr = GREEN;
          else if (x < 8) clr = TFT_ORANGE;   
          M5.Lcd.fillRoundRect(0, (216 - (x * 24)), 6, 21, 2, (audioGain > x ) ? clr : BLACK);
          M5.Lcd.drawRoundRect(0, (216 - (x * 24)), 6, 21, 2, TFT_LIGHTGREY);
        }
      
        // Alternate Draw the Volume Indicator (Triangle)
      //  M5.Lcd.fillTriangle(xtPos, 20, xtPos + 50, 20, xtPos + 50, 0, BLACK); // Clear out old Meter
      //  if (audioGain > 9) { // If we are full, draw red, blue and green
      //    M5.Lcd.fillTriangle(xtPos, 20, xtPos + (5 * audioGain), 20, xtPos + (5 * audioGain), 20 - (2 * audioGain), RED);
      //    M5.Lcd.fillTriangle(xtPos, 20, xtPos + (5 * 9), 20, xtPos + (5 * 9), 20 - (2 * 9), BLUE);
      //    M5.Lcd.fillTriangle(xtPos, 20, xtPos + (5 * 6), 20, xtPos + (5 * 6), 20 - (2 * 6), GREEN);
      //  }
      //  else if (audioGain >= 6) { // if above 5, draw blue and green
      //    M5.Lcd.fillTriangle(xtPos, 20, xtPos + (5 * audioGain), 20, xtPos + (5 * audioGain), 20 - (2 * audioGain), BLUE);
      //    M5.Lcd.fillTriangle(xtPos, 20, xtPos + (5 * 6), 20, xtPos + (5 * 6), 20 - (2 * 6), GREEN);
      //  }
      //  else if (audioGain >= 1)
      //    M5.Lcd.fillTriangle(xtPos, 20, xtPos + (5 * audioGain), 20, xtPos + (5 * audioGain), 20 - (2 * audioGain), GREEN);
      }
      
      
      /*
         Setup output to I2S Device
         Set Pins and Gain
         Set FileSource as web radio station
         Join FileSource to get MetaData
         Create Buffer for data
         Register Callback for...?
         Begin the MP3 playback
      */
      void playMP3() {
        outmp3 = new AudioOutputI2S(0, 0); // Output to builtInDAC
        outmp3->SetPinout(12, 0, 2);
        outmp3->SetOutputModeMono(true);
        outmp3->SetGain(audioGain * gainfactor);
        filemp3 = new AudioFileSourceICYStream(stationList[currentStationNumber][1]);
        filemp3->RegisterMetadataCB(MDCallback, (void*)"ICY"); // ID3TAG  // ICY
        // StreamTitle
        buffmp3 = new AudioFileSourceBuffer(filemp3, bufferSize);
        buffmp3->RegisterStatusCB(StatusCallback, (void*)"buffer");
        mp3 = new AudioGeneratorMP3();
        mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
        mp3->begin(buffmp3, outmp3);
        Serial.printf("STATUS(URL) %s %s\n", stationList[currentStationNumber][0], stationList[currentStationNumber][1]);
        Serial.flush();
        updateStation(String(stationList[currentStationNumber][0]));
      }
      
      
      void loopMP3() {
        if (mp3 != NULL) {  // To avoid crash while changing stationsI
          if (mp3->isRunning()) {
            if (!mp3->loop()) mp3->stop();
          } else {
            Serial.printf("Status(Stream) Stopped \n");
            clearTrack();
            changeStation();
            //      stopPlaying();
            delay(1000);
            playMP3();
          }
        }
      }
      
      
      // General Arduino Routines
      
      void setup() {
        Serial.begin(115200);
      
        M5.begin();
        M5.Axp.SetSpkEnable(true);
        //  M5.Lcd.setRotation(3);
        M5.Lcd.fillScreen(BLACK);
        M5.Lcd.setTextWrap(false);
      
        M5.Lcd.setTextColor(TFT_GREEN, TFT_BLACK);
        M5.Lcd.setTextSize(1);
        M5.Lcd.setTextDatum(TC_DATUM);
        M5.Lcd.setFreeFont(FSB18);   
        M5.Lcd.drawString("Core2 Web Radio", M5.Lcd.width()/2, 20, GFXFF);
      
        initwifi();
        delay(500);
        M5.Lcd.clear();
        drawButtons();
        playMP3();
        changeVolume();  // To update Volume setting and graphic
        displayWiFiInformation();
      }
      
      
      void loop() {
        loopMP3();
        M5.update();
      
        if (m5.BtnA.wasPressed()) { //Change Volume(Button A)
          changeVolume();
        }
      
        if (m5.BtnB.wasPressed()) { //Change Station(Button B)
          clearTrack();
          changeStation();
          stopPlaying();
          playMP3();
        }
      
        if (m5.BtnC.wasPressed()) { //Mute  (Button C)
          audioGain = -1.0;
          changeVolume();
        }
      
        // Update the battery voltage, and WiFi Signal every second
        if ((disUpdate + 1000) < millis()) {
          disUpdate = millis();
          displayBattery();
          updateWiFiSignal();
        }
      }
      
      
      
      

      But this is a slightly nicer interface.

      posted in Core 2
      H
      homeuser33
    • RE: CORE2 as Web radio, without extra hardware ?

      @raychmond

      Happy to help, but all credit goes to :

      https://www.hackster.io/tommyho/arduino-web-radio-player-c4cb23

      // m5StreamTest Version 2020.12b (Source/Buffer Tester)
      // Board: M5StackCore2 (esp32)
      // Author: tommyho510@gmail.com
      // Required: Arduino library ESP8266Audio 1.60
      //

      All I did was to work through his examples, and a few other online examples to find the right audio settings for the Core2.

      Then add a few items to the GUI for battery and track information.

      Hopefully I'll add some of my wish list items as time permits but it seems like a fun project.

      posted in Core 2
      H
      homeuser33
    • RE: CORE2 as Web radio, without extra hardware ?

      I have worked on the webRadio for the Core2, I have it working for me but I'm sure there are issues to iron out. If there is allowed space here, I'll Post the code modified from the same M5WebRadio Project you are talking about.


      
      
      //   March 23, 2021
      //   Made changes to the Example Core2 Web Radio Player from:
      //   https://www.hackster.io/tommyho/arduino-web-radio-player-c4cb23
      
      //    Wish List: (NOT DONE YET)
      //    Nicer Fonts
      //    Use 2nd CPU Core to handle GUI and button's so we keep it responsive
      //    Add bluetooth output to Bluetooth speakers / headest
      //    Get more information from the stream,  Album art, etc.
      //    Get track length and have a progress bar for the track
      //    Have an interface to select WiFi network and enter password
      //    -- Store password in EEPROM
      //    Better mehod of selecting stations
      //    ? Maybe web interface for setting up station lists?
      //
      //  m5StreamTest Version 2020.12b (Source/Buffer Tester)
      //  Board: M5StackCore2 (esp32)
      //  Author: tommyho510@gmail.com
      //  Required: Arduino library ESP8266Audio 1.60
      //
      
      #include <M5Core2.h>
      #include <driver/i2s.h>
      #include <WiFi.h>
      #include <AudioFileSourceICYStream.h>
      #include <AudioFileSource.h>
      #include <AudioFileSourceBuffer.h>
      #include <AudioFileSourceSPIRAMBuffer.h>
      #include <AudioGeneratorMP3.h>
      #include <AudioOutputI2S.h>
      #include <spiram-fast.h>
      
      
      // Enter your WiFi, Station, button settings here:
      const char *SSID = "ENTER_SSID_HERE";
      const char *PASSWORD = "ENTER_WIFI_PASSWORD_HERE";
      
      const int bufferSize = 128 * 1024; // buffer size in byte
      
      
      // http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd
      // Charlie FM in Portland Oregon
      //http://24083.live.streamtheworld.com:80/KYCHFM_SC
      //
      //Removed these from the list:
      //  {"Mega Shuffle", "http://jenny.torontocast.com:8134/stream"},
      //  {"Way Up Radio", "http://188.165.212.154:8478/stream"},
      //  {"Asia Dream", "https://igor.torontocast.com:1025/;.-mp3"},
      //  {"KPop Way Radio", "http://streamer.radio.co/s06b196587/listen"},
      //  {"SomaFM", "http://ice2.somafm.com/christmas-128-mp3"}
      //"http://icecast.thisisdax.com/CapitalEdinburghMP3",
      //"https://prod-34-82-94-246.wostreaming.net/ppm-jazz24mp3-ibc1",
      //"http://sj32.hnux.com/stream?type=http&nocache=3104",
      //"http://sl32.hnux.com/stream?type=http&nocache=1257",
      //"http://media-ice.musicradio.com:80/ClassicFMMP3",
      //"http://naxos.cdnstream.com:80/1255_128",
      //"https://v7player.wostreaming.net/854"
      
      const int stations = 7;// Change Number here if you add feeds!
      char * stationList[stations][3] = {
        {"Charlie_FM", "http://24083.live.streamtheworld.com:80/KYCHFM_SC", "good"},
        {"MAXXED Out", "http://149.56.195.94:8015/steam", "good"},
        {"Orig. Top 40", "http://ais-edge09-live365-dal02.cdnstream.com/a25710", "good"},
        {"Smooth Jazz", "http://sj32.hnux.com/stream?type=http&nocache=3104", "good"},
        {"Smooth Lounge", "http://sl32.hnux.com/stream?type=http&nocache=1257", "good"},
        {"Classic FM", "http://media-ice.musicradio.com:80/ClassicFMMP3", "good"},
        {"Lite Favorites", "http://naxos.cdnstream.com:80/1255_128", "good"}
      };
      
      float audioGain = 1.0;
      float gainfactor = 0.08;
      int currentStationNumber = 0;
      unsigned long disUpdate = millis();
      
      AudioGeneratorMP3 *mp3;
      AudioFileSourceICYStream *filemp3;
      AudioFileSourceBuffer *buffmp3;
      AudioOutputI2S *out, *outmp3;
      
      
      void displayWiFiInformation() {
        M5.Lcd.setCursor(0, 172, 2);
        M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
        M5.Lcd.setTextSize(1);
        M5.Lcd.println("WiFi Network:  " + String(SSID));
        M5.Lcd.print(  "IP Address   :  ");
        M5.Lcd.println(WiFi.localIP());
      }
      
      void initwifi() {
        WiFi.disconnect();
        WiFi.softAPdisconnect(true);
        WiFi.mode(WIFI_STA);
        WiFi.begin(SSID, PASSWORD);
        // Try forever
        int i = 0;
        while (WiFi.status() != WL_CONNECTED) {
          Serial.print("STATUS(Connecting to WiFi) ");
          delay(1000);
          i = i + 1;
          if (i > 10) {
            ESP.restart();
          }
        }
        Serial.println("\nWiFi Connected!\n");
      }
      
      
      String getValue(String data, char separator, int index){
        int found = 0;
        int strIndex[] = {0,-1};
        int maxIndex = data.length() -1;
      
        for (int i = 0; i <= maxIndex && found <= index; i++){
          if (data.charAt(i) == separator || i == maxIndex){
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
          }
        }
        return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
      }
      
      
      // Calculate Battery Usable range  (3.2 to 4.0 Volts)
      void displayBattery() {
        int maxVolts = 400;  // Battery Max volts * 100
        int minVolts = 320;  // Battery Min Volts * 100
        M5.Lcd.setCursor(0, 155, 2);
        M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
        M5.Lcd.setTextSize(1);
        M5.Lcd.printf("Battery:  %.2f  ", M5.Axp.GetBatVoltage());
        int batt = map(M5.Axp.GetBatVoltage()*100, minVolts, maxVolts, 0 , 10000) / 100.0;
      
        // Draw Battery bar(s) on the right side of the screen
        uint16_t clr = GREEN;  
        for(int x = 9; x >= 0; x--){
          if (x < 6) clr = YELLOW;
          if (x < 3) clr = RED;
          M5.Lcd.fillRoundRect(315, (216-(x*24)), 5, 21, 2, (batt>(x*10))?clr:BLACK);  
        }
        M5.Lcd.setCursor(180, 172, 2);
        M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
        M5.Lcd.setTextSize(1);
        M5.Lcd.print("WiFi Signal: ");
        M5.Lcd.print(WiFi.RSSI());
      }
      
      
      // Remove the Track information (While changing stations)
      void clearTrack() {
        M5.Lcd.fillRect(0,63,310,40, BLACK); // Clear the area of old data 
        M5.Lcd.drawRect(0,64,310,37, BLUE); // Draw a box around the Track Information
      }
      
      
      // 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;
      
        String band  = getValue(s2, '-', 0);
        band.trim();
        String track = getValue(s2, '-', 1);
        track.trim();
         
        Serial.printf("Band: %s   Track:  %s  \n", band.c_str(), track.c_str());
        Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2);
        M5.Lcd.setTextSize(1);
        M5.Lcd.setCursor(5, 65, 2);
        clearTrack();
        M5.Lcd.print(band);
        M5.Lcd.setCursor(5, 82, 2);
        M5.Lcd.print(track);
        Serial.flush();
        displayBattery(); // Make sure the new song information does not overwrite the battery
      }
      
      
      // 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
        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();
      }
      
      
      void stopPlaying() {
        Serial.printf("Stopping MP3...\n");
        if (mp3) {
          mp3->stop();
          delete mp3;
          mp3 = NULL;
        }
        Serial.printf("MP3 Stopped, Stopping Buffer...\n");
        if (buffmp3) {
          buffmp3->close();
          delete buffmp3;
          buffmp3 = NULL;
        }
        Serial.printf("Buffer stopped... Stopping File ...\n");
        if (filemp3) {
          filemp3->close();
          delete filemp3;
          filemp3 = NULL;
        }
        if (outmp3) {
          //    filemp3->close();
          delete outmp3;
          outmp3 = NULL;
        }
      
        Serial.printf("STATUS(Stopped)\n");
        Serial.flush();
      }
      
      
      // Update the Station Label
      void updateStation(String message) {
        M5.Lcd.fillRect(0,30,315,30,BLACK); // Clear out other information on the line
        M5.Lcd.setCursor(0, 30, 2);
        M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
        M5.Lcd.setTextSize(2);
        M5.Lcd.println(message);
      }
      
      
      // Identify buttons at the bottom of screen
      void drawButtons() {
        M5.Lcd.setCursor(5, 210, 2);
        M5.Lcd.setTextColor(TFT_BLACK,  TFT_YELLOW);
        M5.Lcd.setTextSize(2);
        M5.Lcd.println(" Vol     Station    Mute");
        M5.Lcd.setTextColor(TFT_WHITE,  TFT_BLACK);
        M5.Lcd.setTextSize(1);
      }
      
      
      // Change to the next station
      void changeStation() {
        updateStation("Changing..Wait..");
        currentStationNumber++;
        if ( stationList[currentStationNumber][2] == "bad") currentStationNumber++; // Skip feeds marked as bad
        if (currentStationNumber >= stations) currentStationNumber = 0;
        Serial.printf("\n******** Changing to channel number: %i\n", currentStationNumber);
      }
      
      
      // Change the volume level
      void changeVolume() {
        audioGain += 1.0;
        if (audioGain > 10.0) {
          audioGain = 1.0;
        }
        if (audioGain < 0.0) {
          audioGain = 0.0;
        }
        int xtPos = 260; // X Position for the Volume indication
        outmp3->SetGain(audioGain * gainfactor);
        M5.Lcd.fillRect(xtPos, 0, 320-xtPos-5, 21, BLACK);
        uint16_t clr = GREEN;
        if (audioGain > 9) clr = RED;
        else if (audioGain > 6) clr = BLUE;
        M5.Lcd.fillTriangle(xtPos, 20, xtPos + (5 * audioGain), 20, xtPos + (5 * audioGain), 20 - (2 * audioGain), clr);
      }
      
      
      void playMP3() {
        outmp3 = new AudioOutputI2S(0, 0); // Output to builtInDAC
        outmp3->SetPinout(12, 0, 2);
        outmp3->SetOutputModeMono(true);
        outmp3->SetGain(audioGain * gainfactor);
        filemp3 = new AudioFileSourceICYStream(stationList[currentStationNumber][1]);
        filemp3->RegisterMetadataCB(MDCallback, (void*)"ICY");
        buffmp3 = new AudioFileSourceBuffer(filemp3, bufferSize);
        buffmp3->RegisterStatusCB(StatusCallback, (void*)"buffer");
        mp3 = new AudioGeneratorMP3();
        mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
        mp3->begin(buffmp3, outmp3);
        Serial.printf("STATUS(URL) %s %s\n", stationList[currentStationNumber][0], stationList[currentStationNumber][1]);
        Serial.flush();
        updateStation(String(stationList[currentStationNumber][0]));
      }
      
      
      void loopMP3() {
        if (mp3 != NULL) {  // To avoid crash while changing stations
          if (mp3->isRunning()) {
            if (!mp3->loop()) mp3->stop();
          } else {
            Serial.printf("Status(Stream) Stopped \n");
            stationList[currentStationNumber][2] = "bad"; // Mark feed as bad if it won't play
            clearTrack();
            changeStation();
            //      stopPlaying();
            delay(1000);
            playMP3();
          }
        }
      }
      
      
      void setup() {
        Serial.begin(115200);
      
        M5.begin();
        M5.Axp.SetSpkEnable(true);
        //  M5.Lcd.setRotation(3);
        M5.Lcd.fillScreen(BLACK);
        M5.Lcd.setTextSize(2);
      //  M5.Lcd.setFreeFont(TT1);       
      //  M5.Lcd.setFreeFont(FF33);
        M5.Lcd.setTextColor(TFT_GREEN, TFT_BLACK);
        M5.Lcd.println("Core2 Web Radio");
        M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
        M5.Lcd.setTextWrap(false);
        
        initwifi();
        delay(500);
        drawButtons();
        playMP3();
        changeVolume();  // To update Volume setting and graphic
        displayWiFiInformation();
      //  Serial.printf("Battery Voltage: %f \n", M5.Axp.GetBatVoltage());
      }
      
      
      void loop() {
        loopMP3();
        M5.update();
      
        if (m5.BtnA.wasPressed()) { //Change Volume(Button A)
          changeVolume();
        }
      
        if (m5.BtnB.wasPressed()) { //Change Station(Button B)
          clearTrack();
          changeStation();
          stopPlaying();
          playMP3();
        }
      
        if (m5.BtnC.wasPressed()) { //Mute  (Button C)
          audioGain = -1.0;
          changeVolume();
        }
        
        if (disUpdate + 1000 < millis()) { // Update the battery voltage
          disUpdate = millis();
          displayBattery();
        }
      }
      
      
      posted in Core 2
      H
      homeuser33