Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Place your projects here
User avatar
Fernando Perez
Posts: 378
Joined: Mon Feb 15, 2021 10:09 pm
Location: Santander (Spain)
Has thanked: 195 times
Been thanked: 267 times

Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by Fernando Perez »

I have loaded version 1.47.2 on a LilyGO TTGO T-Display ESP32 and managed to activate the small TFT display.
Very satisfied, because until now I could only use this module with the Arduino libraries.
Two notices about this module:
At first I used the small built-in buttons to change stations. After pressing them 30 or 40 times during testing, they literally blew themselves up.
The module does not have the GPIO1 and GPIO3 pins accessible, so they cannot be linked to recover the PA 192.168.4.1 (RECOVERY MODE) configuration. If you make a mistake in the Wifi configuration, there is no choice but to re-record the system.
I have programmed a new Internet radio which, at least for me, works fine.
image.png
image.png
A first version, minimal, with only one button to change the station:
image.png

Code: [Local Link Removed for Guests]

' Lilygo TTGO T-Display ESP32 with PCM5102 I2S DAC
' Tested with Annex32 CAN 1.47.2
' PCM510    ESP32
' ------   -------
'  BCK      GPIO27
'  DIN      GPIO02
'  LCK      GPIO26
'  GND      GND
'  VIN      +5V
'  SCK      GND (to avoid oscillations) 

background = tft.color(yellow) 
foreground = tft.color(black)
brightness = 128 ' 0 to 255 
gosub TFT_init

separator$ = "="
file$ = "/station.txt"

' numLines = number of stations minus 1: the
' subscript of an array always starts with zero
numLines = word.count(file.read$(file$), separator$) - 1
dim name$(numLines)
dim url$(numLines)
gosub loadArrays

station = 0
title$ = ""
volume = 75
gosub Play_init

onplay infoStation ' When receiving streaming metadata

init = 1
pin.mode 22, input, pullup
interrupt 22, change_Station

goto bucle

WAIT

' ---------------------------------
bucle:
  while 1
    j = 257
    for i = 1 to 18 ' scrolls first 18 characters of title
      line$ = mid$(title$, 1, i)
      display line$, j, 92
      j = j - 12
    next i    

    j = 53
    for i = 2 to len(title$) - 12 ' scrolls remaining title characters
      line$ = mid$(title$, i, 18)
      display line$, j, 92
    next i
  wend

return

END

' --------------------------------------
loadArrays:
  for i = 1 to numLines
    line$ = file.read$(file$, i)
    if line$ = "_EOF_" then EXIT FOR
    line$ = left$(line$, len(line$)-1) ' remove cr from end of line
    separator = instr(line$, separator$)
    name$(i-1) = left$(line$, separator-1)
    url$(i-1) = mid$(line$, separator+1)
  next i  
' uncomments for debug
'  wlog string$(20,"*"); " STATIONS "; string$(20,"*")
' for i = 0 to numLines-1
'   wlog name$(i); " <---> "; url$(i)
' next i
' wlog  
return

' --------------------------------------
TFT_init:
  tft.init 2 ' Orientation Portrait Reversed
  tft.brightness brightness ' 0 to 255
  tft.text.color foreground, background
  tft.text.align 0
return

' --------------------------------------
Play_init:
  play.setup 1, 64
  play.volume volume
  play.stream url$(station), 20000
  
  tft.fill background  
  tft.text.font 4
  tft.text.draw name$(station), 50, 62
  tft.text.font 3

  ' frame for bar level meter
  tft.rect 45, 120, 227, 60, black
  tft.line 46, 150, 271, 150, black
 
return

' --------------------------------------
InfoStation:
  info$ = play.message$
'  wlog info$
  
  title = instr(info$, "StreamTitle=")
  if title <> 0 then
    title$ = mid$(info$, title + 12)
  else
    title$ = "No title information"
  endif  

  select case name$(station)  ' very special cases
    case "The 40's sounds"
      title$ = replace$(title$, "Big Band Swing Jazz Jive 40s 50s", "")
  end select
  
  blanks = 18 - (len(title$) MOD 18)
  title$ = title$ + space$(blanks) 
'  wlog title$
  
return

' --------------------------------------
change_Station:
  if pin(22) = 0 then return
  if init = 1 then  ' trick to avoid triggering interrupt on boot
    init = 0
    return
  endif  
  pause 20          ' adjust for the best button debounce
  if pin(22) = 1 then
    station = station + 1
    if station = numLines then station = 0
    gosub Play_init
  endif
return

' ============================================
SUB display(t$, x, y)
LOCAL l, r
  
' calculate and draw vumeter bars
  l = convert.map(play.vu_L, 0, 10000, 0, 218)
  r = convert.map(play.vu_R, 0, 10000, 0, 218)
  tft.rect 50, 125, l, 20, red, 1
  tft.rect 50, 155, r, 20, blue, 1
' Scroll song title  
  tft.text.col foreground
  tft.text.draw line$, x, y
  pause 200
' Erase vumeter bars  
  tft.rect 50, 125, l, 20, background, 1  
  tft.rect 50, 155, r, 20, background, 1
' Erase song title
  tft.text.col background
  tft.text.draw line$, x, y
  
END SUB
station.txt

https://youtu.be/RXvKQKpZAR4

A second version, extended, with a rotary encoder to change the station:

Code: [Local Link Removed for Guests]

' File: "/radio2.bas"
' Lilygo TTGO T-Display ESP32 with PCM5102 I2S DAC
' Tested with Annex32 CAN 1.47.2
' PCM510    ESP32
' ------   -------
'  BCK      GPIO27
'  DIN      GPIO02
'  LCK      GPIO26
'  GND      GND
'  VIN      +5V
'  SCK      GND (to avoid oscillations) 

background = tft.color(yellow) 
foreground = tft.color(black)
brightness = 25 ' 0 to 255 
gosub TFT_init

separator$ = "="
file$ = "/station.txt"

' numLines = number of stations minus 1: the
' subscript of an array always starts with zero
numLines = word.count(file.read$(file$), separator$) - 1
dim name$(numLines)
dim url$(numLines)
gosub loadArrays

if file.exists("/last.dat") then  ' remember the last tuned station
  station = val(file.read$("/last.dat"))
else
  last$ = "0"
  file.save "/last.dat", last$
  station = 0
endif

volume = 70
title$ = ""
gosub Play_init

onplay infoStation ' When receiving streaming metadata

init = 1
pin.mode 21, input
pin.mode 22, input
' if you move the rotary encoder:
interrupt 21, loadDial 
interrupt 22, loadDial

goto bucle

WAIT

' ---------------------------------
bucle:
  while 1
    j = 257
    for i = 1 to 18 ' scrolls first 18 characters of title
      line$ = mid$(title$, 1, i)
      display line$, j, 92
      j = j - 12
    next i    

    j = 53
    for i = 2 to len(title$) - 12 ' scrolls remaining title characters
      line$ = mid$(title$, i, 18)
      display line$, j, 92
    next i
  wend
return

END

' --------------------------------------
loadArrays:
  for i = 1 to numLines
    line$ = file.read$(file$, i)
    if line$ = "_EOF_" then EXIT FOR
    line$ = left$(line$, len(line$)-1) ' remove cr from end of line
    separator = instr(line$, separator$)
    name$(i-1) = left$(line$, separator-1)
    url$(i-1) = mid$(line$, separator+1)
  next i  
' uncomments for debug:
'  wlog string$(20,"*"); " STATIONS "; string$(20,"*")
' for i = 0 to numLines-1
'   wlog name$(i); " <---> "; url$(i)
' next i
' wlog  
return

' --------------------------------------
TFT_init:
  tft.init 2 ' Orientation Portrait Reversed
  tft.brightness brightness 
  tft.text.color foreground, background
  tft.text.align 0
return

' --------------------------------------
Play_init:
  play.setup 1, 64
  play.volume volume
  play.stream url$(station), 15000
  
  tft.fill background  
  tft.text.font 4
  tft.text.draw name$(station), 50, 62
  tft.text.font 3

  ' frame for bar level meter
  tft.rect 45, 120, 227, 60, black
  tft.line 46, 150, 271, 150, black
 
return

' --------------------------------------
InfoStation:
  info$ = play.message$
' uncomments for debug:
' wlog info$
  
  title = instr(info$, "StreamTitle=")
  if title <> 0 then
    title$ = mid$(info$, title + 12)
  else
    title$ = "No title information"
  endif  

  select case name$(station)  ' very special cases
    case "The 40's sounds"
      title$ = replace$(title$, "Big Band Swing Jazz Jive 40s 50s", "")
  end select
  
  blanks = 18 - (len(title$) MOD 18)
  title$ = title$ + space$(blanks) 
' uncomments for debug:
' wlog title$  
  
return

' --------------------------
loadDial:
  if pin(21) = 0 then return
  if pin(22) = 0 then return
  if init = 1 then  ' trick to avoid triggering interrupt on boot
    init = 0
    return
  endif
  r = bas.load "/dial.bas"
return

' --------------------------
SUB display(t$, x, y)
LOCAL l, r
' calculate and draw vumeter bars
  l = convert.map(play.vu_L, 0, 10000, 0, 218)
  r = convert.map(play.vu_R, 0, 10000, 0, 218)
  tft.rect 50, 125, l, 20, red, 1
  tft.rect 50, 155, r, 20, blue, 1
' Scroll song title  
  tft.text.col foreground
  tft.text.draw line$, x, y
  pause 200
' Erase vumeter bars  
  tft.rect 50, 125, l, 20, background, 1  
  tft.rect 50, 155, r, 20, background, 1
' Erase song title
  tft.text.col background
  tft.text.draw line$, x, y
END SUB  

Code: [Local Link Removed for Guests]

' File "/dial.bas"
 ' Rotary encoder to change stations
' KY-04   ESP32
' -----   ------
'  CLK -> GPIO21
'  DT  -> GPIO22
'  SW  -> N/C
'  +   -> 3V3
'  GND -> GND

dt = 21
clk = 22
pin.mode dt, input
pin.mode clk, input

separator$ = "="
file$ = "/station.txt"
numLines = word.count(file.read$(file$), separator$) - 1

dim name$(numLines)
dim url$(numLines)
gosub loadArrays

background = tft.color(yellow) 
foreground = tft.color(black)
brightness = 25 ' 0 to 255
gosub TFT_init

play.setup 1, 64
play.volume 70

station = val(file.read$("/last.dat"))
gosub menu

new = station
old = station
min = 0
max = numLines-1

inactivity = 8000  ' 15 seconds without moving and returns
t = millis 

' loop waiting for rotary encoder turn:
while 1

  gosub decode  
  if new <> old then
    gosub viewValue
    old = new
    station = new
    gosub menu
    t = millis    
  endif  

  elapsed = millis - t
  if elapsed > inactivity then ' return after idle time
    ' uncomment for debug
    ' wlog time$
    ' t = millis
    ' comment for debug
    play.stop
    file.save "/last.dat", str$(station)
    r = bas.load "/radio2.bas"
  endif  

wend 

END

' --------------------------------------
loadArrays:
  for i = 1 to numLines
    line$ = file.read$(file$, i)
    if line$ = "_EOF_" then EXIT FOR
    line$ = left$(line$, len(line$)-1)
    separator = instr(line$, separator$)
    name$(i-1) = left$(line$, separator-1)
    url$(i-1) = mid$(line$, separator+1)
  next i  
return

' --------------------------------------
TFT_init:
  tft.init 2 ' Orientation Portrait Reversed
  tft.brightness brightness 
  tft.text.color foreground, background
  tft.text.align 0
return

' --------------------------------------
menu:
  tft.fill background  
  tft.text.font 4
  
  last = 3
  offset = station-1
  y = 92
    
  select case station
    case 0           ' first array item
      offset = 0
      y = 62
    case numLines-1  ' last array item
      last = numLines-station
  end select
  
  for row = 0 to last
    tft.text.draw name$(row+offset), 50, (row*30) + 62
  next row
          
  tft.text.color white, blue  
  tft.text.draw name$(station), 50, y
  tft.text.color foreground, background 
  
  play.stream url$(station), 15000

return

' -------------------------------------
decode:
  l = pin(clk)
  r = pin(dt)
  
  if l=0 AND r=1 then new = new + 1
  if new > max then new = max  
  
  if l=1 AND r=0 then new = new - 1
  if new < min then new = min
     
  do
    l = pin(clk)
    r = pin(dt)
    pause 10   ' modify for better debounce
  loop until l=1 AND r=1

return

' ---------------------------------------
viewValue:
  if new > old then wlog "Derecha "; new
  if new < old then wlog "Izquierda "; new
return
 
https://youtu.be/TjqrYGyelxM
You do not have the required permissions to view the files attached to this post.
AndyGadget
Posts: 229
Joined: Mon Feb 15, 2021 1:44 pm
Has thanked: 132 times
Been thanked: 134 times

Re: Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by AndyGadget »

I never did manage to get my other TTGO Display board working after the one I'd successfully got working with the display blew up. I'm pretty sure my reconnection of the TFT I'd removed was clean but I was getting nothing apart from the backlight from the display.
Frustrating really as I was planning on doing the same thing as you, and these boards now cost an arm and a leg.
User avatar
Fernando Perez
Posts: 378
Joined: Mon Feb 15, 2021 10:09 pm
Location: Santander (Spain)
Has thanked: 195 times
Been thanked: 267 times

Re: Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by Fernando Perez »

Absolutely true, Andy.
I have ordered a 16 MB one for $12.70 on AliExpress, when on Amazon in my country they cost over 30 euros.
And to think that I bought them back in the day for €4.70!
User avatar
cicciocb
Site Admin
Posts: 2195
Joined: Mon Feb 03, 2020 1:15 pm
Location: Toulouse
Has thanked: 470 times
Been thanked: 1461 times
Contact:

Re: Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by cicciocb »

Thanks for sharing your projects, Fernando.

I agree that the cost of the modules increased a lot, but they still remain affordable for our projects.
AndyGadget
Posts: 229
Joined: Mon Feb 15, 2021 1:44 pm
Has thanked: 132 times
Been thanked: 134 times

Re: Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by AndyGadget »

They seem to be coming down daily from the £20 plus of a few weeks ago.
I keep an eye on Banggood where I got my first for under a fiver and occasionaly really good offers come up.
Let's see what Black Friday brings.

These modules are very good in standby too, with a sleep current of under 1mA if you remove the display.
I think the display adds another mA when turned off.
User avatar
Chella Vee
Posts: 6
Joined: Sun Nov 27, 2022 12:35 am
Has thanked: 2 times

Re: Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by Chella Vee »

Dear Fernando

This is amazing project I love it thank you so much for sharing the code.

I was wondering is there any we you could make UI like one blow, without VU meter, and list station as in number, please?



Much Appreciated.
image.png
Kind Regards
Chella
You do not have the required permissions to view the files attached to this post.
AndyGadget
Posts: 229
Joined: Mon Feb 15, 2021 1:44 pm
Has thanked: 132 times
Been thanked: 134 times

Re: Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by AndyGadget »

4MB TTGO-Display boards going for £6:67 on Aliexpress at the moment.
Also MAX98357 decoder modules for 80p.
(Plus 20%, as they add the VAT on at the end :x )


https://www.aliexpress.com/item/3305063 ... ONgogqAG5p

https://www.aliexpress.com/item/1005004 ... svt74kjFCd
AndyGadget
Posts: 229
Joined: Mon Feb 15, 2021 1:44 pm
Has thanked: 132 times
Been thanked: 134 times

Re: Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by AndyGadget »

This may be of interest :-
I've attached a file in .CSV format which lists over 15,000 streaming radio stations from around the world. This is a flattened version of the stations.json file from the Github distro of Jude Pullen's RadioGlobe project : https://www.instructables.com/RadioGlob ... 2000-Stat/

They are a mixture of .mp3, .m3u, .pls and a few other format URLs so a bit of a mish-mash and most need the relevent file downloading and the actual streaming URL to be copied from it into Annex. The list was last updated in July 2021 and from the ones I've tried about half work.
(Jude's project is released under Attribution-NonCommercial-ShareAlike license so OK to post here.)
data.csv
You do not have the required permissions to view the files attached to this post.
AndyGadget
Posts: 229
Joined: Mon Feb 15, 2021 1:44 pm
Has thanked: 132 times
Been thanked: 134 times

Re: Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by AndyGadget »

Just spent way too long doing this, but the attached file is a quick world tour (not complete and in no particular order) in Fernando's format.

Just about all the stations with an https prefix will work better with http. (As Francesco says, secure takes a lot more processing.)
A lot of the .m3u sites will work if you remove the .m3u suffix.
I've set the stream buffering parameters quite high but still getting bad breakup on some stations whereas if I go through a browser they're OK. (With 1.48.22.) All a bit hit and miss, but I'm sure it's harder work than the little ESP32 was designed for :D
Stations.txt
You do not have the required permissions to view the files attached to this post.
User avatar
Fernando Perez
Posts: 378
Joined: Mon Feb 15, 2021 10:09 pm
Location: Santander (Spain)
Has thanked: 195 times
Been thanked: 267 times

Re: Internet Radio: TTGO T-Display ESP32 with PCM5102 and Annex 1.47.2

Post by Fernando Perez »

Andy, I really appreciate your station list. Right now I am testing them. The ones from the txt list, of course, the csv... It would take me years :lol: ! But it comes very well to search for a specific station.
The problem I am encountering is that the stations appear and disappear endlessly. And that many of the addresses that I find seem to be more like servers that capture the streaming of the real station to redirect it to another address in which they embed ads and advertisements.
And, how well you say, that the transmission of some stations is very deficient and good results are not obtained in my program.
Post Reply