|
|
ESP32 dev kit board |
x 1 | |
|
|
ILI9341 TFT Display 2.8 inch |
x 1 | |
|
|
TEA5767 Radio module |
x 1 | |
|
|
Rotary encoder with push button |
x 1 | |
|
|
Small D-class audio amplifier board |
x 1 | |
|
|
and Speaker |
x 1 |
|
arduino IDEArduino
|
|
|
Soldering Iron Kit |
Linear Scale ТЕА5767 FM Radio on ili9341 TFT Display
This time I will present you how to make a simple FM Radio with a beautiful retro look linear scale. The idea for the project was taken from the Volos Projects channel, where is presented the code that was specially made for the LilyGO T-Embed device, which has a TFT screen with a resolution of 320 x 170 pixels.
I just adapted the code for the ILI9341 TFT Display which has a resolution of 320×240 pixels powered by an ESP32 dev kit module. The radio interface is the same, and in the remaining part of the screen I added a small clock that shows the time. Actually, as a beginner in programming, I did a little practice drawing figures and placing text on the Display. The way the scale moves is especially effective for me as a big fan of retro radios. For the FM radio part, the TEA5767 radio module is used, which is characterized by good features and a relatively low price.

I was currently using a board without an audio amplifier, although there is also a board with a built-in stereo amplifier. In that part I use a PAM8304 amplifier module and a custom made small sound box.

If you want to make a PCB for this project, or for any other electronic project, PCBway is a great choice for you. PCBway is one of the most experienced PCB manufacturing company in China in field of PCB prototype and fabrication. They have a large online community where you can find a Open Source projects, and you can also share your project there. From my personal experience I can tell you that on this community you can find many useful projects
Well, as you can see, the device is very simple to build and consists of several components:
- ESP32 dev kit board
- ILI9341 TFT Display 2.8 inch
- TEA5767 Radio module
- Rotary encoder with push button
- Small D-class audio amplifier board
- and Speaker

A few notes about compiling and uploading the code. In order for the code to run without errors, you should use the libraries provided with the code, as they have been modified (TFT_eSPI) specifically for this project.
Аnd now let's see how the device works in reality:
The most striking part of the display is the linear scale that moves with the rotation of the rotary encoder. Then in the middle of the display is the selected frequency, the signal strength icon, as well as the stereo reception and "mute" mark. On the upper left part are the frequencies with the names of favorite stations, which for now are just information, and could be memorized in a future version of the software. Also the battery capacity icon is currently not working. In the lower part under the radio there is a clock that synchronizes with the PC clock when uploading the code.
Finally, the device is installed in a suitable housing made of PVC plastic with a thickness of 5 and 3 mm, and covered with colored self-adhesive wallpaper.

#include <Wire.h>
#include <TEA5767.h>
#include <TFT_eSPI.h>
#include <RotaryEncoder.h>
#include <SPI.h>
#define TFT_GREY 0x5AEB
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
#define PIN_IN1 16
#define PIN_IN2 17
RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
#define color1 0xC638
#define color2 0xC638
int value=980;
int minimal=880;
int maximal=1080;
int strength=0;
String sta[6]={"Ant.5","92.0","Metro","94.8","Super","97.0"};
float freq=0.00;
TEA5767 radio = TEA5767();
bool muted=0;
int deb=0;
uint32_t targetTime = 0; // for next 1 second timeout
static uint8_t conv2d(const char* p); // Forward declaration needed for IDE 1.6.x
uint8_t hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6); // Get H, M, S from compile time
byte omm = 99, oss = 99;
byte xcolon = 0, xsecs = 0;
unsigned int colour = 0;
void setup() {
tft.begin();
tft.writecommand(0x11);
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextSize(1);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
targetTime = millis() + 1000;
pinMode(0, INPUT_PULLUP);
Wire.begin(21,22);
spr.createSprite(320,170);
spr.setTextDatum(4);
spr.setSwapBytes(true);
spr.setFreeFont(&Orbitron_Light_24);
spr.setTextColor(color1,TFT_BLACK);
drawSprite();
}
void readEncoder() {
static int pos = 0;
encoder.tick();
if(digitalRead(0)==0){
if(deb==0){
deb=1;
muted=!muted;
radio.setMuted(muted);
drawSprite();
delay(200);
}
}else deb=0;
int newPos = encoder.getPosition();
if (pos != newPos) {
if(newPos>pos)
value=value-1;
if(newPos<pos)
value=value+1;
pos = newPos;
drawSprite();
}
}
void drawSprite()
{
freq=value/10.00;
if(muted==false)
radio.setFrequency(freq);
strength=radio.getSignalLevel();
spr.fillSprite(TFT_BLACK);
spr.setTextColor(TFT_WHITE,TFT_BLACK);
spr.drawFloat(freq,1,160,64,7);
spr.setFreeFont(&Orbitron_Light_24);
spr.drawString("FM Radio",160,12);
spr.drawString("STATIONS",38,14,2);
spr.drawRoundRect(1,1,76,110,4,0xAD55);
spr.drawRoundRect(240,20,76,22,4,TFT_WHITE);
spr.drawRect(290,6,20,9,TFT_WHITE);
spr.fillRect(291,7,12,7,0x34CD);
spr.fillRect(310,8,2,5,TFT_WHITE);
spr.setTextFont(0);
spr.setTextColor(0xBEDF,TFT_BLACK);
for(int i=0;i<6;i++){
spr.drawString(sta[i],38,32+(i*12));
spr.fillCircle(16,31+(i*12),2,0xFBAE);
}
spr.setTextColor(TFT_WHITE,TFT_BLACK);
spr.drawString("SIGNAL:",266,54);
spr.drawString("MUTED",260,102,2);
spr.fillRoundRect(288,96,20,20,3,0xCC40);
if(muted==1)
spr.fillCircle(297,105,6,TFT_WHITE);
for(int i=0;i<strength;i++)
spr.fillRect(244+(i*4),80-(i*1),2,4+(i*1),0x3526);
spr.fillTriangle(156,104,160,114,164,104,TFT_RED);
int temp=value-20;
for(int i=0;i<40;i++)
{
if((temp%10)==0){
spr.drawLine(i*8,170,i*8,140,color1);
spr.drawLine((i*8)+1,170,(i*8)+1,140,color1);
spr.drawFloat(temp/10.0,1,i*8,130,2);
}
else if((temp%5)==0 && (temp%10)!=0)
{spr.drawLine(i*8,170,i*8,150,color1);
spr.drawLine((i*8)+1,170,(i*8)+1,150,color1);
//spr.drawFloat(temp/10.0,1,i*8,144);
}
else
{spr.drawLine(i*8,170,i*8,160,color1);}
temp=temp+1;
}
spr.drawString("Stereo: "+String(radio.isStereo()),275,31,2);
spr.drawLine(160,114,160,170,TFT_RED);
spr.pushSprite(0,0);
}
void loop() {
readEncoder();
if (targetTime < millis()) {
// Set next update for 1 second later
targetTime = millis() + 1000;
tft.setFreeFont(&Orbitron_Light_24);
tft.drawString("Time :", 17,186);
tft.drawRoundRect(5,175,310,57,8,TFT_WHITE);
//tft.drawRoundRect(240,20,76,22,4,TFT_WHITE)
// Adjust the time values by adding 1 second
ss++; // Advance second
if (ss == 60) { // Check for roll-over
ss = 0; // Reset seconds to zero
omm = mm; // Save last minute time for display update
mm++; // Advance minute
if (mm > 59) { // Check for roll-over
mm = 0;
hh++; // Advance hour
if (hh > 23) { // Check for 24hr roll-over (could roll-over on 13)
hh = 0; // 0 for 24 hour clock, set to 1 for 12 hour clock
}
}
}
// Update digital time
int xpos = 110;
int ypos = 183; // Top left corner ot clock text, about half way down
int ysecs = ypos; // + 24;
if (omm != mm) { // Redraw hours and minutes time every minute
omm = mm;
// Draw hours and minutes
if (hh < 10) xpos += tft.drawChar('0', xpos, ypos, 6); // Add hours leading zero for 24 hr clock
xpos += tft.drawNumber(hh, xpos, ypos, 6); // Draw hours
xcolon = xpos; // Save colon coord for later to flash on/off later
xpos += tft.drawChar(':', xpos, ypos - 8, 6);
if (mm < 10) xpos += tft.drawChar('0', xpos, ypos, 6); // Add minutes leading zero
xpos += tft.drawNumber(mm, xpos, ypos, 6); // Draw minutes
xsecs = xpos; // Sae seconds 'x' position for later display updates
}
if (oss != ss) { // Redraw seconds time every second
oss = ss;
xpos = xsecs;
if (ss % 2) { // Flash the colons on/off
tft.setTextColor(0x39C4, TFT_BLACK); // Set colour to grey to dim colon
tft.drawChar(':', xcolon, ypos, 6); // Hour:minute colon
xpos += tft.drawChar(':', xsecs, ysecs, 6); // Seconds colon
tft.setTextColor(TFT_WHITE, TFT_BLACK); // Set colour back to yellow
}
else {
tft.drawChar(':', xcolon, ypos, 6); // Hour:minute colon
xpos += tft.drawChar(':', xsecs, ysecs, 6); // Seconds colon
}
//Draw seconds
if (ss < 10) xpos += tft.drawChar('0', xpos, ysecs, 6); // Add leading zero
tft.drawNumber(ss, xpos, ysecs, 6); // Draw seconds
}
}
}
// Function to extract numbers from compile time string
static uint8_t conv2d(const char* p) {
uint8_t v = 0;
if ('0' <= *p && *p <= '9')
v = *p - '0';
return 10 * v + *++p - '0';
}
Linear Scale ТЕА5767 FM Radio on ili9341 TFT Display
Raspberry Pi 5 7 Inch Touch Screen IPS 1024x600 HD LCD HDMI-compatible Display for RPI 4B 3B+ OPI 5 AIDA64 PC Secondary Screen(Without Speaker)
BUY NOW- Comments(6)
- Likes(3)
-
Engineer
Mar 27,2025
-
Eric Bouillet
Mar 02,2025
-
Engineer
Dec 07,2024
- 1 USER VOTES
- YOUR VOTE 0.00 0.00
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
-
10design
-
10usability
-
10creativity
-
10content
More by Mirko Pavleski
-
Arduino 3D Printed self Balancing Cube
Self-balancing devices are electronic devices that use sensors and motors to keep themselves balanc...
-
Retro Analog VU Meter on Round dispalys (ESP32 and GC9A01)
Recently, in one of my previous videos I presented you a Retro VU Meter project on round displays ...
-
Ultimate 2-Player Reaction Timer with WS2812B LED Strips & Arduino
Arcade reaction game is a genre of play designed to test a player's physical response time and hand...
-
Building a Vintage Tube-Style Internet Radio with Raspberry Pi & Rotary Encoder
Internet radio (also known as web radio or net radio) is a digital audio service transmitted via th...
-
DIY Smart Code Lock with CrowPanel 1.28 ESP32 Rotary Display
A code lock is a keyless security device—either mechanical or electronic—that restricts access to d...
-
SDR Panadapter for Vintage Tube Radios – Step-by-Step Tutorial
A radio panadapter (or panoramic adapter) is a device or software tool used in amateur radio and ot...
-
Oscilloscope Clock Simulation on a Round ESP32 Display
An oscilloscope clock is a circuit that turns an old analog oscilloscope into a stylish, retro-them...
-
DIY Simple GU32 Tube Stereo Amplifier (2x3W on 12VDC)
Vacuum tube amplifiers are often favored for their smooth harmonic distortion, especially in the low...
-
DIY 3-Display OLED Clock with Arduino and I2C Multiplexer
In this video I want to present you another unusual clock to add to my large collection of such DIY...
-
Build a 5-Day forecast Raspberry Pi Weather Dashboard (Step-by-Step)
Recently in one of my previous videos,I introduced you to the 7 inch Elecrow Pi Terminal and how to...
-
ESP32 Aneroid Barometer using Squareline Studio and LVGL on CrowPanel Round display
A barometer is a scientific instrument used to measure atmospheric pressure. Rising Pressure genera...
-
LINAMP Project – Winamp-Style Audio Front Panel on Raspberry Pi 5
Winamp is one of the most iconic and historically significant digital media players ever created. I...
-
Retro Style radio with CrowPanel 2.1inch round Display (TEA5767)
Some time ago I presented you a clock project with CrowPanel 2.1inch-HMI ESP32 Rotary Display 480*4...
-
Pi-Pico RX - SDR Radio with New Firmware and Features
A few months ago I presented you a wonderful SDR radio project by DawsonJon 101 Things. In short, i...
-
How to make simple Variable HIGH VOLTAGE Power Supply
High Voltage Power Supply is usually understood as a device that is capable of generating a voltage...
-
DIY 5-Day Rainfall Forecast Device - ESP32 E-Paper Project
In several of my previous projects I have presented ways to make weather stations, but this time I ...
-
Build simple Retro Style VFO (Variable frequency oscillator) with Crowoanel 1.28 inch Round Display
Today I received a shipment with a Small round LCD display from Elecrow. The device is packed in tw...
-
Human vs Robot – Rock Paper Scissors with MyCobot 280 M5Stack
Today I received a package containing the few Elephant Robotics products. The shipment is well pack...
-
-
-
ARPS-2 – Arduino-Compatible Robot Project Shield for Arduino UNO
1097 0 2 -
-
A Compact Charging Breakout Board For Waveshare ESP32-C3
1631 3 7 -
AI-driven LoRa & LLM-enabled Kiosk & Food Delivery System
1577 2 0 -
-
-
-
ESP32-C3 BLE Keyboard - Battery Powered with USB-C Charging
1764 0 1 -







