#include <SPI.h>
#include "DisplayUI.h" // Nuestro archivo para la UI elegante y minimalista
#include "RotaryControl.h" // Nuestro archivo para el Encoder Rotativo
// Variables globales de estado
bool isFM = true;
bool isAutoTune = false; // false = Sintonía Manual, true = Búsqueda Automática (Seek)
// Límites y frecuencias iniciales requeridos
uint16_t currentFreqFM = 9850; // 98.5 MHz (Unidad enviada al chip: decenas de kHz)
uint16_t currentFreqAM = 1000; // 1000 kHz
// Variables SIMULADAS de extracción de datos del Datasheet
int signalLevel = 0;
int usn = 0;
int wam = 0;
int offset = 0;
bool isStereo = false;
char rdsBuffer[15] = "";
unsigned long lastDisplayUpdate = 0;
void setup() {
Serial.begin(115200);
// Inicializar Pantalla TFT y Táctil (desde la carpeta src)
initUI();
// Pantalla de carga inicial
drawSplashScreen();
// Inicializar Encoder (desde la carpeta src)
initEncoder();
// SIMULACIÓN: En lugar de inicializar el TEF6686, solo esperamos un momento
delay(1500);
clearScreen();
}
void loop() {
// 1. Leer estado del Encoder (Maneja rotación y clics del botón)
handleEncoder();
// 2. Comprobar toques en la pantalla TFT (Para futuras aplicaciones táctiles)
handleTouch();
// 3. Refresco de pantalla cada 150 ms para no saturar
if (millis() - lastDisplayUpdate > 150) {
updateDatasheetMetrics(); // Genera valores simulados
updateUI(isFM, isAutoTune, isFM ? currentFreqFM : currentFreqAM, signalLevel, isStereo, rdsBuffer, usn, wam, offset);
lastDisplayUpdate = millis();
}
}
// --- LÓGICA DEL ENCODER Y SINTONÍA ---
void handleEncoder() {
int encAction = getEncoderAction(); // Devuelve +1 (derecha), -1 (izquierda) o 0
int btnAction = getEncoderButton(); // Devuelve 1 (Click corto) o 2 (Click largo)
// ACCIÓN DE CLICK LARGO: Cambiar entre Sintonía Manual y Automática
if (btnAction == 2) {
isAutoTune = !isAutoTune;
clearRDS();
}
// ACCIÓN DE CLICK CORTO: Cambiar Banda (FM / AM)
else if (btnAction == 1) {
isFM = !isFM;
// SIMULACIÓN: Aquí se enviaría radio.setFrequency()
clearRDS();
}
// ACCIÓN DE ROTACIÓN
if (encAction != 0) {
if (isAutoTune) {
// SIMULACIÓN SINTONÍA AUTOMÁTICA: Simula un salto grande a una emisora al azar
if (isFM) {
currentFreqFM += (encAction > 0) ? 200 : -200;
if (currentFreqFM > 10800) currentFreqFM = 8800;
if (currentFreqFM < 8800) currentFreqFM = 10800;
} else {
currentFreqAM += (encAction > 0) ? 40 : -40;
if (currentFreqAM > 1700) currentFreqAM = 530;
if (currentFreqAM < 530) currentFreqAM = 1700;
}
} else {
// SINTONÍA MANUAL (Saltos exactos solicitados)
if (isFM) {
if (encAction > 0) currentFreqFM += 10; // Sube +100 kHz (1 decimal)
else currentFreqFM -= 10; // Baja -100 kHz
// Límites FM requeridos (88.0 a 108.0)
if (currentFreqFM > 10800) currentFreqFM = 8800;
if (currentFreqFM < 8800) currentFreqFM = 10800;
} else {
if (encAction > 0) currentFreqAM += 5; // Sube +5 kHz (Sin decimales)
else currentFreqAM -= 5; // Baja -5 kHz
// Límites AM requeridos (530 a 1700)
if (currentFreqAM > 1700) currentFreqAM = 530;
if (currentFreqAM < 530) currentFreqAM = 1700;
}
}
clearRDS(); // Borrar nombre de la emisora anterior al cambiar de frecuencia
}
}
void handleTouch() {
if (isTouched()) {
// Aquí puedes agregar lógica táctil futura.
}
}
void clearRDS() {
memset(rdsBuffer, 0, sizeof(rdsBuffer));
}
// --- SIMULACIÓN: EXTRACCIÓN DE DATOS ---
// En el hardware real, aquí usaríamos radio.getLevel(), etc.
// Para el simulador Wokwi, inventamos valores para ver la UI en acción.
void updateDatasheetMetrics() {
// Generamos un nivel de señal aleatorio con algo de estabilidad para que se vea realista
int noise = random(-2, 3);
signalLevel = (isFM ? 60 : 40) + noise;
usn = random(0, 5); // Ruido muy bajo simulado
wam = random(0, 10); // Multipath bajo simulado
offset = 0; // Emisora perfectamente sintonizada
isStereo = (signalLevel > 35); // Simula que entra el estéreo si la señal es fuerte
// Simulación de RDS
if (isFM && signalLevel > 45) {
strcpy(rdsBuffer, "WOKWI FM");
} else {
memset(rdsBuffer, 0, sizeof(rdsBuffer));
}
}