/*==============================================================
RETRO RADIO DIAL – Tangential FM Labels via 80x40 bitmap
ESP32-S3 + CrowPanel 2.1" + Arduino_GFX 1.4.4
V1.6: Retro narrow font + 17px mid tick + inner yellow arc
+ Rotary Encoder Control
+ TEA5767 Radio Module
+ AM/FM Horizontal Labels by mircemk November 2025
==============================================================*/
#include <Arduino.h>
#include <Arduino_GFX_Library.h>
#include <Wire.h>
#define DISPLAY_WIDTH 480
#define DISPLAY_HEIGHT 480
#define CX 240
#define CY 240
// Panel pins -------
#define TYPE_SEL 7
#define PCLK_NEG 1
#define BL_PIN 6
#define PANEL_CS 16
#define PANEL_SCK 2
#define PANEL_SDA 1
// ------------------
// Rotary Encoder pins
#define ENCODER_CLK 4
#define ENCODER_DT 42
#define ENCODER_SW 41
// I2C pins for TEA5767
#define I2C_SDA 38
#define I2C_SCL 39
#define TEA5767_I2C_ADDRESS 0x60
Arduino_DataBus *panelBus = nullptr;
Arduino_ESP32RGBPanel *rgbpanel = nullptr;
Arduino_RGB_Display *gfx = nullptr;
uint16_t *fb = nullptr;
/* ----------- COLORS ----------- */
const uint16_t COL_BG = 0x0000;
const uint16_t COL_TICK = 0xFDA0; // warm retro yellow
const uint16_t COL_NUM = 0x07E0; // green
// ---- Button grayscale colors ----
uint16_t BTN_DARK = 0x4208; // darkest
uint16_t BTN_MID = 0x6b48; // medium
uint16_t BTN_LIGHT = 0xb56e; // light
const int R_AM = 160 - 15; // = 145
/* ----------- Label bitmap size ----------- */
#define LABEL_W 80
#define LABEL_H 40
uint8_t labelBuf[LABEL_H][LABEL_W]; // 0 = off, 1 = on
// ---------- ARROW (frequency pointer) ----------
float arrowFreq = 97.9; // starting frequency (example)
const float ARROW_MIN = 88.0;
const float ARROW_MAX = 108.0;
const int ARROW_R_START = 0; // inner starting radius
const int ARROW_R_END = 240; // same as FM ticks outer edge
const int ARROW_THICK = 8; // arrow thickness (px)
const uint16_t ARROW_COL = 0xF800; // bright red
// Rotary Encoder variables (from older flawless version)
volatile int lastEncoded = 0;
volatile long encoderValue = 0;
long lastEncoderValue = 0;
int lastMSB = 0;
int lastLSB = 0;
// Radio state
bool radioInitialized = false;
float fmFreqToAngle(float f)
{
// clamp
if (f < ARROW_MIN) f = ARROW_MIN;
if (f > ARROW_MAX) f = ARROW_MAX;
// linear interpolation from 88..108 MHz → -155..+155 degrees
float t = (f - 88.0f) / (108.0f - 88.0f);
return -155.0f + t * (155.0f - (-155.0f));
}
/*==============================================================
TEA5767 RADIO FUNCTIONS
==============================================================*/
void tea5767_setFrequency(float frequency) {
unsigned int freqB = 4 * (frequency * 1000000 + 225000) / 32768;
byte frequencyH = freqB >> 8;
byte frequencyL = freqB & 0xFF;
byte data[5] = {
frequencyH,
frequencyL,
0xB0, // Normal mode, stereo, port1 high, port2 high
0x10, // 75µs de-emphasis, 32.768kHz crystal, soft mute off, high side LO
0x00 // No stereo noise canceling, no search mode
};
Wire.beginTransmission(TEA5767_I2C_ADDRESS);
Wire.write(data, 5);
Wire.endTransmission();
delay(100); // Delay for radio to settle
}
bool tea5767_init() {
Wire.beginTransmission(TEA5767_I2C_ADDRESS);
byte error = Wire.endTransmission();
return (error == 0);
}
bool initRadio() {
Serial.println("Initializing TEA5767 radio...");
Wire.begin(I2C_SDA, I2C_SCL);
Wire.setClock(100000);
if (!tea5767_init()) {
Serial.println("TEA5767 not detected!");
return false;
}
// Set initial frequency
tea5767_setFrequency(arrowFreq);
Serial.println("TEA5767 initialized successfully!");
return true;
}
/*==============================================================
BASIC FUNCTIONS
==============================================================*/
static inline void putpix(int x, int y, uint16_t c) {
if ((unsigned)x < DISPLAY_WIDTH && (unsigned)y < DISPLAY_HEIGHT)
fb[y * DISPLAY_WIDTH + x] = c;
}
void draw_arrow_pointer(float freq)
{
float angDeg = fmFreqToAngle(freq);
float a = angDeg * PI / 180.0f;
int x1 = CX + (int)(ARROW_R_START * sin(a));
int y1 = CY - (int)(ARROW_R_START * cos(a));
int x2 = CX + (int)(ARROW_R_END * sin(a));
int y2 = CY - (int)(ARROW_R_END * cos(a));
// draw thickness
for (int w = -ARROW_THICK/2; w <= ARROW_THICK/2; w++)
{
draw_line(
x1 + (int)(w * cos(a)),
y1 + (int)(w * sin(a)),
x2 + (int)(w * cos(a)),
y2 + (int)(w * sin(a)),
ARROW_COL
);
}
}
void draw_filled_circle(int cx, int cy, int r, uint16_t col)
{
for(int y = -r; y <= r; y++)
for(int x = -r; x <= r; x++)
if(x*x + y*y <= r*r)
putpix(cx + x, cy + y, col);
}
void draw_ring(int cx, int cy, int rOuter, int thickness, uint16_t col)
{
int rInner = rOuter - thickness;
int rOuter2 = rOuter * rOuter;
int rInner2 = rInner * rInner;
for(int y = -rOuter; y <= rOuter; y++)
{
int yy = y * y;
for(int x = -rOuter; x <= rOuter; x++)
{
int rr = x*x + yy;
if(rr <= rOuter2 && rr >= rInner2)
putpix(cx + x, cy + y, col);
}
}
}
void draw_line(int x0,int y0,int x1,int y1,uint16_t c){
int dx=abs(x1-x0), sx=x0<x1?1:-1;
int dy=-abs(y1-y0), sy=y0<y1?1:-1;
int err=dx+dy, e2;
for(;;){
putpix(x0,y0,c);
if(x0==x1 && y0==y1) break;
e2=2*err;
if(e2>=dy){ err+=dy; x0+=sx; }
if(e2<=dx){ err+=dx; y0+=sy; }
}
}
/* ----------- RETRO NARROW 8x12 DIGIT FONT ----------- */
const uint8_t RETRO_DIGIT[10][12] PROGMEM = {
{0x3C,0x42,0x46,0x4A,0x52,0x62,0x42,0x42,0x42,0x42,0x3C,0x00}, // 0
{0x08,0x18,0x28,0x48,0x08,0x08,0x08,0x08,0x08,0x08,0x3E,0x00}, // 1
{0x3C,0x42,0x02,0x02,0x04,0x08,0x10,0x20,0x40,0x40,0x7E,0x00}, // 2
{0x3C,0x42,0x02,0x02,0x1C,0x02,0x02,0x02,0x02,0x42,0x3C,0x00}, // 3
{0x04,0x0C,0x14,0x24,0x44,0x44,0x7E,0x04,0x04,0x04,0x1F,0x00}, // 4
{0x7E,0x40,0x40,0x40,0x7C,0x02,0x02,0x02,0x02,0x42,0x3C,0x00}, // 5
{0x1C,0x20,0x40,0x40,0x7C,0x42,0x42,0x42,0x42,0x42,0x3C,0x00}, // 6
{0x7E,0x02,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x20,0x00}, // 7
{0x3C,0x42,0x42,0x42,0x3C,0x42,0x42,0x42,0x42,0x42,0x3C,0x00}, // 8
{0x3C,0x42,0x42,0x42,0x42,0x3E,0x02,0x02,0x02,0x04,0x38,0x00} // 9
};
/* ----------- SIMPLE 8x12 LETTER FONT (A, M, F) ----------- */
const uint8_t RETRO_LETTER[3][12] PROGMEM = {
{0x18,0x24,0x42,0x42,0x7E,0x42,0x42,0x42,0x42,0x42,0x42,0x00}, // A - FIXED
{0x41,0x63,0x55,0x49,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x00}, // M - FIXED
{0x7F,0x40,0x40,0x40,0x7C,0x40,0x40,0x40,0x40,0x40,0x40,0x00} // F - FIXED (was 0x7E, now 0x7F for full left bar)
};
// ... [REST OF THE DRAWING FUNCTIONS REMAIN THE SAME AS YOUR ORIGINAL WORKING CODE]
/*==============================================================
Create horizontal label into 80x40 bitmap (using RETRO_DIGIT)
==============================================================*/
void buildLabelBitmap(const char *text, int scale)
{
for (int y = 0; y < LABEL_H; y++)
for (int x = 0; x < LABEL_W; x++)
labelBuf[y][x] = 0;
int len = strlen(text);
if (len <= 0) return;
const int glyphW = 8;
const int glyphH = 12;
int digitW = glyphW * scale;
int digitH = glyphH * scale;
int gap = scale;
int labelW = len * digitW + (len - 1) * gap;
int startX = (LABEL_W - labelW) / 2;
int startY = (LABEL_H - digitH) / 2;
for (int d = 0; d < len; d++) {
int digit = text[d] - '0';
if (digit < 0 || digit > 9) continue;
const uint8_t *glyph = RETRO_DIGIT[digit];
int x0 = startX + d * (digitW + gap);
for (int row = 0; row < glyphH; row++) {
uint8_t bits = pgm_read_byte(&glyph[row]);
for (int col = 0; col < glyphW; col++) {
if (bits & (0x80 >> col)) {
for (int sx = 0; sx < scale; sx++) {
for (int sy = 0; sy < scale; sy++) {
int xx = x0 + col * scale + sx;
int yy = startY + row * scale + sy;
if (xx >= 0 && xx < LABEL_W && yy >= 0 && yy < LABEL_H)
labelBuf[yy][xx] = 1;
}
}
}
}
}
}
}
/*==============================================================
Draw rotated label
angleDeg = tangential FM angle (0° = top)
==============================================================*/
void blitLabelRotated(const char *text,
float angleDeg,
int radius,
int scale,
uint16_t col)
{
buildLabelBitmap(text, scale);
float theta = angleDeg * PI / 180.0f; // screen rotation
float ct = cos(theta);
float st = sin(theta);
// Center position of label on the dial
float a = angleDeg * PI / 180.0f;
float cx_label = CX + radius * sin(a);
float cy_label = CY - radius * cos(a);
float cxLocal = LABEL_W / 2.0f;
float cyLocal = LABEL_H / 2.0f;
for (int by = 0; by < LABEL_H; by++) {
for (int bx = 0; bx < LABEL_W; bx++) {
if (!labelBuf[by][bx]) continue;
float lx = bx - cxLocal;
float ly = by - cyLocal;
float rx = lx*ct - ly*st;
float ry = lx*st + ly*ct;
int sx = (int)(cx_label + rx);
int sy = (int)(cy_label + ry);
putpix(sx, sy, col);
}
}
}
/*==============================================================
Draw horizontal text (for AM/FM labels) - FIXED VERSION
==============================================================*/
void drawHorizontalText(const char *text, int x, int y, int scale, uint16_t col) {
int len = strlen(text);
if (len <= 0) return;
const int glyphW = 8;
const int glyphH = 12;
for (int d = 0; d < len; d++) {
char c = text[d];
const uint8_t *glyph;
// Select appropriate glyph
switch(c) {
case 'A': glyph = RETRO_LETTER[0]; break;
case 'M': glyph = RETRO_LETTER[1]; break;
case 'F': glyph = RETRO_LETTER[2]; break;
default: continue;
}
int x0 = x + d * (glyphW * scale + scale);
for (int row = 0; row < glyphH; row++) {
uint8_t bits = pgm_read_byte(&glyph[row]);
for (int col_bit = 0; col_bit < glyphW; col_bit++) {
if (bits & (0x80 >> col_bit)) {
for (int sx = 0; sx < scale; sx++) {
for (int sy = 0; sy < scale; sy++) {
int xx = x0 + col_bit * scale + sx;
int yy = y + row * scale + sy;
if (xx >= 0 && xx < DISPLAY_WIDTH && yy >= 0 && yy < DISPLAY_HEIGHT)
putpix(xx, yy, col);
}
}
}
}
}
}
}
/*==============================================================
Helper: inner yellow arc with thickness
==============================================================*/
void draw_inner_arc()
{
const float A0 = -155.0f;
const float A1 = 155.0f;
const int R_ARC = 160; // radius of the arc (slightly smaller than ticks)
const int TH_ARC = 3; // thickness of the arc
const int rOuter = R_ARC;
const int rInner = R_ARC - TH_ARC;
const float step = 0.2f; // degrees, smaller = smoother
for (float angDeg = A0; angDeg <= A1; angDeg += step) {
float a = angDeg * PI / 180.0f;
int xOuter = CX + (int)(rOuter * sin(a));
int yOuter = CY - (int)(rOuter * cos(a));
int xInner = CX + (int)(rInner * sin(a));
int yInner = CY - (int)(rInner * cos(a));
// small radial segment, repeated along the arc → thick band
draw_line(xInner, yInner, xOuter, yOuter, COL_TICK);
}
}
//--------------------------------------------------
// Draw a short arc segment (for AM green arcs)
//--------------------------------------------------
void draw_short_arc(int cx, int cy, int r,
float angStart, float angEnd,
int thickness, uint16_t col)
{
float step = 0.5;
for (int t = 0; t < thickness; t++)
{
int rr = r - t; // inner offset → makes arc thicker
for (float a = angStart; a <= angEnd; a += step)
{
float ang = a * PI / 180.0f;
int x = cx + rr * sin(ang);
int y = cy - rr * cos(ang);
putpix(x, y, col);
}
}
}
/*==============================================================
ROTARY ENCODER FUNCTIONS (from older flawless version)
==============================================================*/
void updateEncoder() {
int MSB = digitalRead(ENCODER_CLK);
int LSB = digitalRead(ENCODER_DT);
int encoded = (MSB << 1) | LSB;
int sum = (lastEncoded << 2) | encoded;
if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) {
encoderValue++;
}
if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) {
encoderValue--;
}
lastEncoded = encoded;
}
void handleEncoder() {
noInterrupts();
long currentValue = encoderValue;
interrupts();
if (currentValue != lastEncoderValue) {
// Calculate frequency change (0.1 MHz per step)
float freqChange = (currentValue - lastEncoderValue) * 0.1;
arrowFreq += freqChange;
// Clamp frequency to valid range
if (arrowFreq < ARROW_MIN) arrowFreq = ARROW_MIN;
if (arrowFreq > ARROW_MAX) arrowFreq = ARROW_MAX;
// Update TEA5767 radio module
if (radioInitialized) {
tea5767_setFrequency(arrowFreq);
}
// Redraw the dial with new arrow position
draw_dial();
gfx->draw16bitRGBBitmap(0, 0, fb, DISPLAY_WIDTH, DISPLAY_HEIGHT);
Serial.printf("Frequency: %.1f MHz\n", arrowFreq);
lastEncoderValue = currentValue;
}
}
/*==============================================================
DRAW FULL FM DIAL
==============================================================*/
void draw_dial()
{
// Clear framebuffer
for (int i = 0; i < DISPLAY_WIDTH * DISPLAY_HEIGHT; i++)
fb[i] = COL_BG;
// ----- COMMON CONSTANTS -----
const int MAJ = 11;
const float A0 = -155.0f;
const float A1 = 155.0f;
const int R_OUT = 240;
const int TICK_LONG = 25; // main tick length
const int TICK_SHORT = 11; // short tick length
const int TICK_MID = 20; // mid tick length (5th division)
const int TH_LONG = 7; // main tick thickness
const int TH_SHORT = 2; // short & mid tick thickness
const int R_TEXT = R_OUT - 53;
const int SCALE = 3;
// ----- 1) INNER YELLOW ARC -----
draw_inner_arc();
// ----- 2) FM TICKS (MAIN + MINOR + MID) -----
for (int i = 0; i < MAJ; i++)
{
float angDeg = A0 + i * (A1 - A0) / (MAJ - 1);
float a = angDeg * PI / 180.0f;
int x1 = CX + (int)((R_OUT - TICK_LONG) * sin(a));
int y1 = CY - (int)((R_OUT - TICK_LONG) * cos(a));
int x2 = CX + (int)(R_OUT * sin(a));
int y2 = CY - (int)(R_OUT * cos(a));
// main thick tick
for (int w = -TH_LONG / 2; w <= TH_LONG / 2; w++)
{
draw_line(
x1 + (int)(w * cos(a)), y1 + (int)(w * sin(a)),
x2 + (int)(w * cos(a)), y2 + (int)(w * sin(a)),
COL_TICK
);
}
// minor ticks + mid (5th) tick
if (i < MAJ - 1)
{
float a0 = angDeg * PI / 180.0f;
float a1 = (A0 + (i + 1) * (A1 - A0) / (MAJ - 1)) * PI / 180.0f;
for (int j = 1; j < 10; j++)
{
float aj = a0 + (a1 - a0) * (j / 10.0f);
int tickLen = (j == 5) ? TICK_MID : TICK_SHORT; // 5th tick longer
int xi = CX + (int)((R_OUT - tickLen) * sin(aj));
int yi = CY - (int)((R_OUT - tickLen) * cos(aj));
int xo = CX + (int)(R_OUT * sin(aj));
int yo = CY - (int)(R_OUT * cos(aj));
for (int w = -TH_SHORT / 2; w <= TH_SHORT / 2; w++)
{
draw_line(
xi + (int)(w * cos(aj)), yi + (int)(w * sin(aj)),
xo + (int)(w * cos(aj)), yo + (int)(w * sin(aj)),
COL_TICK
);
}
}
}
} // <-- end FM ticks loop
/****************************************************
AM BORDER TICKS (correct radius at AM arc)
****************************************************/
{
const float borders[2] = { -155.0f, 155.0f };
const int R_AM_BORDER = 160; // <-- THIS radius is used below
const int AM_TICK_LEN = 25;
const int AM_TICK_TH = 7;
for (int b = 0; b < 2; b++)
{
float angDeg = borders[b];
float a = angDeg * PI / 180.0f;
// --------- USE R_AM_BORDER HERE (not R_AM) ---------
int x1 = CX + (int)((R_AM_BORDER - AM_TICK_LEN) * sin(a));
int y1 = CY - (int)((R_AM_BORDER - AM_TICK_LEN) * cos(a));
int x2 = CX + (int)(R_AM_BORDER * sin(a));
int y2 = CY - (int)(R_AM_BORDER * cos(a));
// ----------------------------------------------------
for (int w = -AM_TICK_TH/2; w <= AM_TICK_TH/2; w++)
{
draw_line(
x1 + (int)(w * cos(a)), y1 + (int)(w * sin(a)),
x2 + (int)(w * cos(a)), y2 + (int)(w * sin(a)),
COL_TICK
);
}
}
}
// ----- 4) AM GREEN ARCS (8 PIECES, MEDIUM WIDTH) -----
{
const int AM_ARCS = 8;
const float AM_A0 = -140.0f;
const float AM_A1 = 140.0f;
const int AM_TH = 8;
const float ARC_WIDTH = 12.0f;
for (int i = 0; i < AM_ARCS; i++)
{
float base = AM_A0 + i * (AM_A1 - AM_A0) / (AM_ARCS - 1);
float angStart = base - ARC_WIDTH / 2;
float angEnd = base + ARC_WIDTH / 2;
draw_short_arc(
CX, CY,
R_AM, // global const R_AM = 145
angStart, angEnd,
AM_TH,
COL_NUM
);
}
}
// ----- 5) AM LABELS (TANGENTIAL, RETRO FONT, SCALE 2) -----
{
const int AM_ARCS = 8;
const int AM_FREQ[8] = { 53, 68, 83, 98, 113, 128, 143, 158 };
const float AM_A0 = -140.0f;
const float AM_A1 = 140.0f;
const int R_AM_TEXT = 115; // radius for AM text
const int SCALE_AM = 2;
for (int i = 0; i < AM_ARCS; i++)
{
float angDeg = AM_A0 + i * (AM_A1 - AM_A0) / (AM_ARCS - 1);
char buf[8];
sprintf(buf, "%d", AM_FREQ[i]);
blitLabelRotated(
buf,
angDeg,
R_AM_TEXT,
SCALE_AM,
COL_NUM
);
}
}
/****************************************************
3–RING GRAY BUTTON (Customizable)
****************************************************/
{
// --- Radii (you can adjust anytime) ---
int R_OUTER = 90; // outside radius
int R_INNER = 65; // inner filled disk
int R_LIGHT = 82; // light ring radius (midway)
int LIGHT_W = 3; // light ring thickness
int OUTER_W = 20; // outer ring thickness
// --- Draw dark outer ring (thick) ---
draw_ring(CX, CY, R_OUTER, OUTER_W, BTN_DARK);
// --- Draw light thin ring (for highlight) ---
draw_ring(CX, CY, R_LIGHT, LIGHT_W, BTN_LIGHT);
// --- Draw full inner medium disk ---
draw_filled_circle(CX, CY, R_INNER, BTN_MID);
}
// ----- 6) FM LABELS (TANGENTIAL, RETRO FONT, SCALE 3) -----
{
const int FM[11] = { 88,90,92,94,96,98,100,102,104,106,108 };
for (int i = 0; i < MAJ; i++)
{
float angDeg = A0 + i * (A1 - A0) / (MAJ - 1);
char buf[8];
sprintf(buf, "%d", FM[i]);
blitLabelRotated(
buf,
angDeg,
R_TEXT,
SCALE,
COL_NUM
);
}
}
// ----- 7) AM/FM HORIZONTAL LABELS (FIXED POSITIONING) -----
{
const int LABEL_SCALE = 3; // Larger size for better visibility
// "AM" label - positioned near the yellow AM arc (top)
int amX = CX - 30; // Centered horizontally (2 chars * 8px * 4 scale / 2)
int amY = CY + 120; // Position above center, near AM arc
// "FM" label - positioned above FM scale numbers (bottom)
int fmX = CX - 30; // Centered horizontally
int fmY = CY + 200; // Position below center, above FM numbers
drawHorizontalText("AM", amX, amY, LABEL_SCALE, COL_TICK);
drawHorizontalText("FM", fmX, fmY, LABEL_SCALE, COL_TICK);
}
// ----- 8) DRAW THE ARROW POINTER (LAST - ON TOP OF EVERYTHING) -----
draw_arrow_pointer(arrowFreq);
}
/*==============================================================
DISPLAY INITIALIZATION
==============================================================*/
void init_display() {
pinMode(BL_PIN, OUTPUT);
digitalWrite(BL_PIN, HIGH);
panelBus = new Arduino_SWSPI(
GFX_NOT_DEFINED, PANEL_CS, PANEL_SCK, PANEL_SDA, GFX_NOT_DEFINED);
rgbpanel = new Arduino_ESP32RGBPanel(
40,7,15,41,
46,3,8,18,17,
14,13,12,11,10,9,
5,45,48,47,21,
1,50,10,50,
1,30,10,30,
PCLK_NEG,8000000UL );
#if TYPE_SEL == 7
gfx = new Arduino_RGB_Display(
DISPLAY_WIDTH, DISPLAY_HEIGHT, rgbpanel, 0, true,
panelBus, GFX_NOT_DEFINED,
st7701_type7_init_operations, sizeof(st7701_type7_init_operations)
);
#else
gfx = new Arduino_RGB_Display(
DISPLAY_WIDTH, DISPLAY_HEIGHT, rgbpanel, 0, true,
panelBus, GFX_NOT_DEFINED,
st7701_type5_init_operations, sizeof(st7701_type5_init_operations)
);
#endif
gfx->begin(16000000);
fb = (uint16_t*)ps_malloc(DISPLAY_WIDTH * DISPLAY_HEIGHT * 2);
}
/*==============================================================
SETUP / LOOP
==============================================================*/
void setup() {
Serial.begin(115200);
// Initialize rotary encoder pins
pinMode(ENCODER_CLK, INPUT_PULLUP);
pinMode(ENCODER_DT, INPUT_PULLUP);
pinMode(ENCODER_SW, INPUT_PULLUP);
// Attach interrupt for encoder (flawless version)
attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), updateEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(ENCODER_DT), updateEncoder, CHANGE);
// Initialize display
init_display();
// Initialize TEA5767 radio
radioInitialized = initRadio();
// Draw initial display
draw_dial();
gfx->draw16bitRGBBitmap(0, 0, fb, DISPLAY_WIDTH, DISPLAY_HEIGHT);
Serial.println("Radio Dial Ready - Rotate encoder to change frequency");
Serial.printf("Initial frequency: %.1f MHz\n", arrowFreq);
if (radioInitialized) {
Serial.println("TEA5767 Radio: INITIALIZED");
} else {
Serial.println("TEA5767 Radio: NOT FOUND - Display only mode");
}
}
void loop() {
handleEncoder();
delay(2); // Small delay to debounce
}
Comments