logo elektroda
logo elektroda
X
logo elektroda

Internet radio and audio file player on ESP32-S3

MAJSTER XXL 299991 2813

TL;DR LABEL_AI_GENERATED

  • Built an ESP32-S3 internet radio with an audio file player and OLED desk display, evolving through v1, v2, and v3.
  • Uses an ESP32-S3-WROOM-1, PCM5102A DAC, rotary encoders, and GitHub-hosted station lists split into banks of up to 100 stations.
  • The prototype uses a 2.42" 128x64px white OLED display, with a PCB made in Poland costing 130zl.
  • v3 adds a colour screen, weather, a calendar, RSS news from Polsat News Polska, and full remote control.
  • The first PCB had a pad-spacing mistake for the ESP32-S3-WROOM-1, so the module pins had to be bent slightly to fit.
AI summary based on the discussion. May contain errors.
ADVERTISEMENT
This content has been translated flag-pl » flag-en View the original version here
Listen:
  • Question about the source of the cassette animation code

    #2791 21942222
    DJCheester
    Level 28  
    Posts: 2090
    Help: 75
    Rate: 960
    It’s a cool animation – I’m all for it – but this design for a tapeless tape recorder is a travesty of retro kit, a bit like fitting Chinese radios into the cabinets of old wooden valve radio sets. Real gear should be preserved, not modified like that – that’s my view. When I was younger, I once converted a ZK147 reel-to-reel tape recorder by fitting electronics from a Technics deck into it. After replacing the head with a new one (the tape recorder was a four-track model), I was able to turn the mono ZK147 into a stereo unit. It sounded better than the original – the dynamics were improved and, on top of that, it gained Dolby. Admittedly, it wasn’t quite the same anymore, but the soul of that kit still lay in the tape playback. I posted this ‘invention’ on Elektroda and got a bit of flak for that kind of remake. Just a little off-topic.

    Getting back to the cassette and the project – it isn’t open-source (I’m talking about this tape recorder); ejcona, did you write the cassette animation code yourself?

    Best regards...
  • ADVERTISEMENT
  • #2792 21942797
    ejcon
    Level 17  
    Posts: 301
    Help: 3
    Rate: 68
    >>21938600
    Metal audio front panel with screen and two knobs on a workshop bench
    This is how it looks at the moment – I still need to change the glass to tinted glass


  • #2793 21942812
    robgold
    Level 23  
    Posts: 726
    Help: 10
    Rate: 290
    >>21942797 I’d go for bigger knobs. Although, for me, I think I’ll always associate this layout with Safari 5, which is why I’m so keen on sticking with a single encoder all the time ;)
  • #2794 21942876
    DJCheester
    Level 28  
    Posts: 2090
    Help: 75
    Rate: 960
    >>21942797

    Well, that’s a lovely little gadget – I think I’ll sit down with my PCB and make one myself, especially as the weather isn’t exactly brilliant.

    Congratulations and best regards...
  • ADVERTISEMENT
  • #2795 21943681
    DJCheester
    Level 28  
    Posts: 2090
    Help: 75
    Rate: 960
    >>21942797

    Ejcon, have you shared this project anywhere on GitHub (I’m talking about the one with 4xESP and 3xLCD from the last video)?

    Best regards....
  • #2796 21943684
    ArturAVS
    Moderator
    Posts: 26461
    Help: 2304
    Rate: 7866
    @ejcon, change the labels on the analysers. Not 250k, but 250Hz. :D
  • ADVERTISEMENT
  • #2797 21943689
    ejcon
    Level 17  
    Posts: 301
    Help: 3
    Rate: 68
    >>21943684
    Thanks, I didn’t realise – I suppose I need to have a bit of a rest if I’m making mistakes like this

    Added after 13 [minutes]:

    >>21943681
    No, I haven’t shared anything
  • #2798 21943704
    ArturAVS
    Moderator
    Posts: 26461
    Help: 2304
    Rate: 7866
    Internet radio and audio file player on ESP32-S3

    Not that I’m criticising your work in any way. You’ve put a lot of effort into it, and a ‘mistake’ like this does spoil the effect, unfortunately. Why do you want to change the front to a tinted/smoked finish? How did you cut out that ‘piece of glass’?
  • #2799 21943709
    ejcon
    Level 17  
    Posts: 301
    Help: 3
    Rate: 68
    >>21943704
    This is milled polycarbonate with a film applied to it.
  • ADVERTISEMENT
  • #2800 21943714
    ArturAVS
    Moderator
    Posts: 26461
    Help: 2304
    Rate: 7866
    If it weren’t for those jagged outer edges… It would be brilliant! I’d also change the font on the cassette to one that’s 2–4 pixels larger and set it to Bold. That would improve the readability.
  • Suggesting tinted glass and rear-mounted panel window

    #2801 21943809
    efi222
    Level 22  
    Posts: 779
    Help: 12
    Rate: 1221
    If the ‘window’ in the front panel is larger than the displays, the purpose of the tinted glass is to conceal the interior of the case. In such cases, I also use additional black masking with cut-outs for the active area of the display.
    In the photo of the panel, light is reflected off the glass. However, from a different angle, the contents of the case will be visible.

    Unless, of course, this is intentional :)

    @ejcon, how thick is the front panel? If it’s reasonably thin, might it be easier to fit a piece of glass larger than the window behind the cut-out?
  • #2802 21943827
    ejcon
    Level 17  
    Posts: 301
    Help: 3
    Rate: 68
    Bottom view of a green PCB with a connected multicolored wire bundle inside a metal enclosure
    Screenshot of a 3D model of a rectangular panel with several rectangular cutouts
    >>21943809
    The panels will be printed on a printer, but I need to send the files to a mate as I haven’t bought a printer yet.
  • Display recess modified with 0.4 mm frame cover

    #2803 21943855
    efi222
    Level 22  
    Posts: 779
    Help: 12
    Rate: 1221
    I would suggest modifying these recesses to accommodate the displays. An additional 0.4 mm thick frame
    The deeper recess accommodates the entire display. An additional thin cover conceals the display outside the active part of the screen.
    It works well for me.

    Internet radio and audio file player on ESP32-S3

    Internet radio and audio file player on ESP32-S3

    Internet radio and audio file player on ESP32-S3

    Black rectangular plastic bezel with an inserted display on a light background
  • #2804 21943863
    ejcon
    Level 17  
    Posts: 301
    Help: 3
    Rate: 68
    >>21943855
    There are some faults underneath – what you saw was the front in the photo

    Added 2 [hours] 7 [minutes] later:

    >>21943863
    Internet radio and audio file player on ESP32-S3
    I’ve corrected the subtitles on the tape; these should now be
    The display panel will look like this
    Internet radio and audio file player on ESP32-S3
  • #2805 21943934
    efi222
    Level 22  
    Posts: 779
    Help: 12
    Rate: 1221
    @ejcon. Also, the direction in which the tape moves inside the cassette is the wrong way round.
    In other words, the movement is fine, it’s just that the spools are spinning the wrong way round.
  • #2806 21944145
    DJCheester
    Level 28  
    Posts: 2090
    Help: 75
    Rate: 960
    >>21943863

    Are you planning to share the code?
    I’d love to see the whole thing, but what I’d find most useful is the code for this cassette. It looks brilliant. Best regards...
  • Cartridge code licensing and speed reduction

    #2807 21944156
    ejcon
    Level 17  
    Posts: 301
    Help: 3
    Rate: 68
    The cartridge code – so these codes will be a compiled version for the ‘Elektroda’ group; those who’ve earned it will get it for free, whilst others, unfortunately, won’t. For you, it’ll be the full version with a licence; after installation and sending certain parameters, you’ll need to enter the licence details. It might not be fair, but I didn’t go to all that trouble just for someone to sell my work.

    Added after 7 [minutes]:

    >>21943934
    Already corrected and speed reduced.
  • Cassette UI drawing helpers for ESP32-S3

    #2808 21945261
    ejcon
    Level 17  
    Posts: 301
    Help: 3
    Rate: 68
    CASSETTE CODE
    // ============================================================================
    // MODE 16 - ANIMOWANA KASETA MAGNETOFONOWA
    // ============================================================================
    static void v50DrawCassetteReel(int cx, int cy, int tapeRadius, float angle)
    {
      gfx.tftFillCircle(cx, cy, tapeRadius + 3, 0x2104);
      gfx.tftFillCircle(cx, cy, 25, 0xEF7D);
      gfx.tftFillCircle(cx, cy, 15, 0xBDF7);
      for (uint8_t tooth = 0; tooth < 8; ++tooth)
      {
        float a = angle + tooth * 0.7853982f;
        int x1 = cx + lroundf(cosf(a) * 9.0f);
        int y1 = cy + lroundf(sinf(a) * 9.0f);
        int x2 = cx + lroundf(cosf(a) * 22.0f);
        int y2 = cy + lroundf(sinf(a) * 22.0f);
        gfx.tftDrawLine(x1, y1, x2, y2, 0x4208);
        gfx.tftFillCircle(x2, y2, 2, 0x4208);
      }
      gfx.tftFillCircle(cx, cy, 6, 0x1082);
      gfx.tftFillCircle(cx, cy, 3, 0xFFFF);
    }
    
    static void v50DrawCassetteLevel(int x, int y, int pct, const char *label)
    {
      gfx.tftText(x, y - 1, label, 1, 0xFFFF);
      pct = constrain(pct, 0, 100);
      for (uint8_t i = 0; i < 14; ++i)
      {
        int threshold = (i + 1) * 100 / 14;
        uint16_t on = i < 9 ? 0x07E0 : (i < 12 ? 0xFFE0 : 0xF800);
        gfx.tftFillRect(x + 12 + i * 9, y, 7, 7, threshold <= pct ? on : 0x2945);
      }
    }
    
    // Czytelniejszy tekst na etykiecie kasety: pogrubienie 2 x 2 piksele.
    static void v50DrawBoldText(int x, int y, const char *text, uint8_t size, uint16_t color)
    {
      if (text == nullptr || text[0] == '\0') return;
      gfx.tftText(x,     y,     text, size, color);
      gfx.tftText(x + 1, y,     text, size, color);
      gfx.tftText(x,     y + 1, text, size, color);
      gfx.tftText(x + 1, y + 1, text, size, color);
    }
    
    void v50PanelCassette()
    {
      static float reelAngle = 0.0f;
      static float tapeTransfer = 0.0f;
      static int8_t tapeDirection = 1;
      static uint32_t lastCassetteMoveMs = 0;
      static uint32_t playSeconds = 0;
      static uint32_t lastSecondMs = 0;
      static float meterL = 0.0f, meterR = 0.0f;
      static float meterVelL = 0.0f, meterVelR = 0.0f;
      static float previousAudioL = 0.0f, previousAudioR = 0.0f;
      uint32_t now = millis();
      bool playing = !volumeMute && SampleRate > 0;
    
      if (playing && now - lastCassetteMoveMs >= 60UL)
      {
        lastCassetteMoveMs = now;
        reelAngle -= 0.08f; // dodatni kąt = obrót przeciwnie z ruchem wskazówek
        if (reelAngle < 0.0f) reelAngle += 6.2831853f;
        tapeTransfer += tapeDirection * 0.00045f;
        if (tapeTransfer >= 1.0f) { tapeTransfer = 1.0f; tapeDirection = -1; }
        if (tapeTransfer <= 0.0f) { tapeTransfer = 0.0f; tapeDirection = 1; }
      }
      if (playing && now - lastSecondMs >= 1000UL)
      {
        lastSecondMs = now;
        playSeconds = (playSeconds + 1) % 6000UL;
      }
    
      gfx.tftFillScreen(0x0000);
      // Czerwono-czarna obudowa inspirowana klasyczną kasetą 90 min.
      gfx.tftFillRoundRect(5, 5, 310, 230, 12, 0xA800);
      gfx.tftDrawRoundRect(7, 7, 306, 226, 10, 0xFBAE);
      gfx.tftFillRoundRect(12, 10, 296, 221, 8, 0x1082);
      gfx.tftFillRect(16, 62, 288, 101, 0xB800);
    
      // Biała etykieta: stała czcionka Unicode i przewijanie po pełnych znakach UTF-8.
      // Font nie zmienia się w trakcie przewijania, a polskie litery są czytelne.
      gfx.tftFillRoundRect(18, 10, 284, 50, 4, 0xFFFF);
      gfx.tftDrawFastHLine(24, 34, 272, 0xE38E);
      String radioLine = tftScrollWindowUtf8(tftStationName(), 34, 520);
      String songLine = tftScrollWindowUtf8(tftNowPlaying(), 34, 360);
      gfx.tftTextUtf8(25, 14, radioLine.c_str(), 1, 0x0000);
      gfx.tftTextUtf8(25, 37, songLine.c_str(), 1, 0x0000);
    
      // Przydymione okno kasety oraz zmieniająca się ilość taśmy na rolkach.
      gfx.tftFillRoundRect(25, 70, 270, 85, 8, 0x0841);
      gfx.tftDrawRoundRect(27, 72, 266, 81, 7, 0x7BEF);
      int leftTapeRadius = 37 - lroundf(tapeTransfer * 13.0f);
      int rightTapeRadius = 24 + lroundf(tapeTransfer * 13.0f);
      v50DrawCassetteReel(92, 111, leftTapeRadius, reelAngle);
      v50DrawCassetteReel(228, 111, rightTapeRadius, reelAngle);
      gfx.tftDrawLine(92, 139, 228, 139, 0x8A22);
      gfx.tftDrawLine(92, 140, 228, 140, 0xC326);
    
      // Środkowa szybka z przesuwającą się taśmą.
      gfx.tftFillRoundRect(126, 83, 68, 55, 4, 0x2104);
      gfx.tftDrawRoundRect(128, 85, 64, 51, 3, 0x8410);
      for (uint8_t i = 0; i < 7; ++i)
      {
        int px = 132 + ((i * 13 + (int)(reelAngle * 9.0f)) % 56);
        gfx.tftFillRect(px, 108, 5, 3, 0xFDC8);
      }
    
      // Wyższy czerwony pasek i większa czcionka. Usunięcie podwójnych spacji
      // pozwala zmieścić cały napis w szerokości 272 px przy rozmiarze 2.
      gfx.tftFillRoundRect(24, 154, 272, 32, 3, 0xE920);
      gfx.tftText(22, 160, "BASF90 EVO4 MULTI STEREO", 2, 0xFFFF);
    
      float rawL = playing ? (float)tftVuPct(displayVuL) : 0.0f;
      float rawR = playing ? (float)tftVuPct(displayVuR) : 0.0f;
      float targetL = v50MusicBeatTarget(rawL, previousAudioL, 0.66f);
      float targetR = v50MusicBeatTarget(rawR, previousAudioR, 0.66f);
      meterVelL = (meterVelL + (targetL - meterL) * 0.64f) * 0.67f;
      meterVelR = (meterVelR + (targetR - meterR) * 0.64f) * 0.67f;
      meterL = constrain(meterL + meterVelL, 0.0f, 100.0f);
      meterR = constrain(meterR + meterVelR, 0.0f, 100.0f);
      int levelL = lroundf(meterL);
      int levelR = lroundf(meterR);
      v50DrawCassetteLevel(25, 190, levelL, "L");
      v50DrawCassetteLevel(25, 203, levelR, "R");
    
      char counter[8];
      snprintf(counter, sizeof(counter), "%02lu:%02lu",
               (unsigned long)(playSeconds / 60UL), (unsigned long)(playSeconds % 60UL));
      gfx.tftText(250, 190, playing ? "PLAY" : "STOP", 1, playing ? 0x07FF : 0xF800);
      gfx.tftText(250, 204, counter, 1, 0xFFE0);
      gfx.tftText(278, 219, "M16", 1, 0xFFFF);
    }
    As I mentioned, I’m sharing the cassette code.
  • #2809 21945311
    DJCheester
    Level 28  
    Posts: 2090
    Help: 75
    Rate: 960
    Thanks, I’ll have a think about it when I’ve a spare moment.

    Best regards...
  • #2810 21947012
    bevo1001
    Level 2  
    Posts: 2
    Internet radio and audio file player on ESP32-S3
    hello! need help to connect this board to esp32-s3, to run internet radio. currently when power is turned on the toshlink does not light up (i don't know if this is normal).
    Maybe someone has a finished project, send it to the mail bevo1001(_at_)gmail.com
    thank you in advance.
  • #2811 21947229
    ejcon
    Level 17  
    Posts: 301
    Help: 3
    Rate: 68
    >>21947012
    On the back of the board, there’s a diagram showing how to set the valves and which ones to leave open if it’s that model
    Connect the LARCK CLOC and DATA, then set the jumpers
    Internet radio and audio file player on ESP32-S3
    as per the diagram on the bottom of the board – it SHOULD start beeping
  • #2812 21947615
    bevo1001
    Level 2  
    Posts: 2
    >>21947229 >>21947229 Thank you very much for your response. According to the jumpers, my board is set to i2s 24 bit... but it barely lights up, there is no response....esp 32-s3 says in the com port that it is sending sound.
  • PAM8406 pins require re-soldering upward for proper installation

    #2813 21948855
    grzes_dst
    Level 12  
    Posts: 80
    Rate: 7
    I’ve been away for a while.
    At the request of my friend @DJCheester, I’m posting a photo of the finished circuit board with the PAM 8406 amplifier installed. The board is my friend’s latest creation, featuring an additional keyboard output.
    Internet radio and audio file player on ESP32-S3
    A note for anyone assembling this amplifier who is having problems with the sound it produces.
    The amplifier is designed to be mounted with the pins facing upwards, so you need to re-solder the gold pins and reposition them so that the pins are facing upwards.
    They are soldered in place as shown in the photo below.

    Internet radio and audio file player on ESP32-S3
  • Version 7.3 uses PAM8406 with resoldered pins

    #2814 21948923
    DJCheester
    Level 28  
    Posts: 2090
    Help: 75
    Rate: 960
    Hello, thanks for the photo. To clarify, this is version 7.3, which features two encoders, a PAM8406 amplifier, and outputs for a switch keypad and LEDs. You need to re-solder the pins on the PAM8406; I haven’t seen a so-called ‘NOT SOLDERED’ version online.

    So we have version 7.3 up and running, and it is indeed the latest version at present, However, further versions are planned for the autumn-winter season, featuring more powerful PAM8610 or TPA3110 amplifiers, with the entire circuit powered by 12V.

    And once Ejcon has fine-tuned the software and made the schematic available, it’s possible that a PCB will also be produced for his version.

    Best regards...
Listen:

Topic summary

LABEL_AI_GENERATED
Discussion of a DIY internet radio and SD-card audio player built on an ESP32-S3 with an OLED display, rotary encoders, buttons, and a front-panel interface. The project uses Wi‑Fi to download station lists from GitHub/public stream databases, supports MP3 and later AAC/FLAC playback, displays metadata including Polish characters via a modified font table, and stores settings such as station/bank selection and volume. The thread covers PCB design in EasyEDA, ESP32-S3 module pin spacing issues, PSRAM and Arduino IDE setup, library compatibility problems, SPI/I2S wiring, SD card reader boot issues, encoder behavior, WiFiManager-based network setup, and troubleshooting of crashes, HTTP download failures, and audio initialization errors. Several improvements were added over time, including station selection by encoder, time sync, browser update support, and saving settings to card memory.
AI summary based on the discussion. May contain errors.
ADVERTISEMENT