Construye una radio FM a prueba de agua para la ducha con Arduino Nano, RDA5807, LCD Nokia y PAM8403
Componentes y materiales
| Componente | Cantidad |
|---|---|
| Arduino Nano R3 | 1 |
| Módulo FM RDA5807 (RDA Microelectronics) | 1 |
| Pantalla LCD Nokia 5110 (versión 3V-5V) | 1 |
| Módulo RTC Tiny (DS1307 + EEPROM 24C32) | 1 |
| Amplificador PAM8403 (2×3W clase D) | 1 |
| Convertidor DC-DC LM2596 Step-Down | 1 |
| Batería recargable LIR2032 3.6V | 1 |
| Resistencia 100 Ω | 1 |
| Resistencia 1 kΩ | 1 |
| Resistencia 10 kΩ | 6 |
| Resistencia 100 kΩ | 1 |
| MOSFET de potencia IRLZ44 (Infineon) | 1 |
| Altavoces PEIYING PY1010C 60W 4Ω | 1 |
| Fuente 12V 3A (recuperada) | 1 |
| Condensadores 2200 µF 25V | 2 |
| Condensador 100 µF | 1 |
| Bombillas LED 3W (sustituyen halógenos G4 10W) | 1 |
Sobre el proyecto
Hace unos años compré una cabina de ducha china con sistema de control TR028 que incluía radio. Un día dejó de funcionar completamente y nadie pudo repararlo. Compré otra radio de ducha más barata; murió al año. Mi familia estaba acostumbrada a tener radio en la ducha, así que investigué cómo funcionaban estos dispositivos. Dentro del TR028 encontré un módulo TEA5767; buscando descubrí el RDA5807, muy similar pero con RDS, control de volumen y bass boost. Decidí usar el RDA5807.
Mi visión
- Dispositivo con pantalla táctil para construcción a prueba de agua (reutilicé el panel táctil del TR028 muerto).
- Radio FM.
- Varias emisoras predefinidas favoritas.
- Control de volumen.
- Búsqueda automática de emisoras.
- Memorizar emisora encontrada en predefinida.
- Reloj para ver la hora actual.
- Encendido/Apagado.
- Control de luces.
- Información menor: temperatura interior, RDS.
En AliExpress compré RDA5807, Tiny RTC con EEPROM 32KB, PAM8403, LCD Nokia 5110, módulos LM2596 por menos de 10€ y empecé a experimentar.
Qué conseguí finalmente
- Radio FM con RDS de 2 líneas.
- 6 predefinidas para emisoras favoritas.
- Sintonía automática o manual.
- Guardar emisora encontrada en una de las 6 predefinidas.
- Control de volumen y bass boost.
- Control de luces de la cabina.
- Reloj y calendario.
- Indicador RSSI (intensidad de señal).
- Indicador de modo estéreo.
- Función Encendido/Apagado (standby).
Funcionamiento
- Encendido: al conectar la alimentación no arranca la radio (evita arranques si la red es inestable). Para encender por primera vez, conecta la alimentación y tras unos segundos pulsa la tecla de encendido. Reproducirá la última emisora con volumen 03. Modo operativo: control de VOLUMEN. Para apagar, pulsa la tecla de encendido: apaga LCD, retroiluminación, amplificador y lámpara LED/halógena.
- Búsqueda de emisora: elige Auto o Manual con tecla "Mod". Con "<" o ">" busca disminuyendo/aumentando frecuencia. Para guardar, pulsa "Mem" y tienes 4 segundos para elegir una de las 6 predefinidas.
- Ver fecha: pulsa tecla "I" (info). La fecha se muestra 4 segundos (usa
delay(), optimizable). - Ajuste de reloj: mantén pulsada la tecla "D" al menos 2 seg al oír la señal horaria o ver los últimos segundos de una hora en un reloj exacto. Suelta "D" para fijar hh.00.00. Si el reloj adelantaba 1-15 min, solo minutos/segundos a 00; si atrasaba 15-1 min, horas +1 y minutos/segundos a 00.
Qué cambiaría
- Resonador del RTC para mejor precisión (la función de ajuste lo soluciona).
- LCD 5110 por uno mayor y más brillante (1.8" o 2.0" color); a veces cuesta leer en el 5110.
- Amplificador PAM8403 por PAM8610 (2×15W) o TDA7297.
Conclusión
Estoy contento con el resultado. Tras 1 mes funcionando sin problemas salvo la precisión del reloj. No soy programador, el código se puede optimizar. Mi experiencia en C/C++ es de ~1 año, autodidacta. Es mi primer proyecto útil con Arduino y el primero que comparto. Disculpad posibles errores y mi mal inglés. Preguntas en comentarios o PM.
Actualización 1: Fuse 2A en línea de 12V (seguridad). Software: línea 586 menu = 1; restaura modo VOLUMEN al pulsar encendido.
Actualización 2: El LCD está fallando. Busco alternativa barata al Nokia 5110; no pondré otro 5110 (pequeño y difícil de leer). Probaré TFT 1.8" (más grande, brillante, mejor resolución) aunque consumirá más recursos. Acepto sugerencias.
Código
Sketch principal (Arduino)
/////////////////////////////////////////////////////////////////
// Arduino based shower FM Radio Project
// Arduino NANO, RDA5807M, RTC, EEPROM, LCD5110, Thermistor
/////////////////////////////////////////////////////////////////
#include <LCD5110_Graph.h>
#include <AT24CX.h>
#include <Wire.h>
#include <RDSParser.h>
#include <radio.h>
#include <RTClib.h>
#include <RDA5807M.h>
#include <Keypad.h>
#define MAXmenu 4
#define ledPin 13
#define blPin 7
char keys[7][2] = {
{'L', 'P'}, // LED, POWER
{'I', 'D'}, // INFO, DISPLAY
{'1', '2'}, // predefinidas
{'3', '4'},
{'5', '6'},
{'M', 'm'}, // MODE, MEM
{'<', '>'} // down, up
};
byte rowPins[7] = {11, 12, 10, 17, 9, 16, 8};
byte colPins[2] = {15, 14};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, 7, 2);
boolean bass = 0, dspl = 0, memdisplay = 0, mempress = 0, adj = 0;
boolean ledPin_state, power_state;
int menu, volume, volumeOld = 5, frequency, frequencyOld;
int txtl = 0, temparray = 0;
int samples[5];
unsigned int status[6];
unsigned long timeprevious = 0, timeprev = 0;
AT24CX mem;
RTC_DS1307 rtc;
LCD5110 lcd(6, 5, 4, 2, 3);
RDA5807M radio;
RDSParser rds;
extern unsigned char SmallFont[];
extern uint8_t signal5[]; extern uint8_t signal4[]; extern uint8_t signal3[];
extern uint8_t signal2[]; extern uint8_t signal1[];
void setup() {
analogReference(EXTERNAL);
Serial.begin(9600);
Wire.begin();
radio.init(); radio.debugEnable();
lcd.InitLCD(); lcd.clrScr(); lcd.setFont(SmallFont); lcd.enableSleep();
power_state = 0; // standby al conectar alimentación
keypad.addStatedEventListener(keypadEvent);
keypad.setHoldTime(1500);
pinMode(ledPin, OUTPUT); pinMode(blPin, OUTPUT);
digitalWrite(ledPin, LOW); digitalWrite(blPin, LOW);
ledPin_state = digitalRead(ledPin);
frequency = mem.readInt(201);
volume = 2; menu = 1;
if (volume < 0) volume = 0; if (volume > 15) volume = 15;
if (frequency < 0) frequency = 0; if (frequency > 210) frequency = 210;
WriteReg(0x02, 0xC00d); // soft reset, enable, RDS
radio.attachReceiveRDS(RDS_process);
rds.attachServicenNameCallback(DisplayServiceName);
rds.attachTextCallback(DisplayText);
}
void loop() {
if (frequency != frequencyOld) { frequencyOld = frequency; mem.writeInt(201, frequency); canal(frequency); }
if (volume != volumeOld) { volumeOld = volume; WriteReg(5, 0x84D0 | volume); }
char key = keypad.getKey();
radio.checkRDS();
// Temperatura cada 0.6s, 5 muestras, media
unsigned long timenow = millis();
if ((unsigned long)(timenow - timeprevious) > 600) {
timeprevious = timenow;
samples[temparray] = analogRead(A7);
temparray++;
}
if (temparray == 5) {
float average = 0;
for (int i = 0; i < 5; i++) average += samples[i];
printTemp(average);
temparray = 0;
}
// Timeout 4s para MEM
unsigned long dabar = millis();
if (mempress == 1) { timeprev = dabar; memdisplay = 1; mempress = 0; }
if (memdisplay == 1) {
if ((unsigned long)(dabar - timeprev) < 4000) memdisplay = 1; else memdisplay = 0;
}
// Ajuste RTC por Serial
DateTime now = rtc.now();
if (Serial.available() > 0) {
char t = Serial.read();
switch (t) {
case 'h': { unsigned int hours = Serial.parseInt(); rtc.adjust(DateTime(now.year(), now.month(), now.day(), hours, now.minute(), now.second())); Serial.println(F("Hours")); Serial.println(hours); break; }
case 'm': { unsigned int mins = Serial.parseInt(); rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), mins, now.second())); Serial.println(F("Minutes")); Serial.println(mins); break; }
case 's': { unsigned int sec = Serial.parseInt(); rtc.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), now.minute(), sec)); Serial.println(F("Seconds")); Serial.println(sec); break; }
}
}
printSignalStrength(); printLines(); printTime(); printFreq(); printStereo(); printMode(); printMenu(); printDate();
lcd.update();
}
// ... funciones de visualización, PID, RDS, teclado, etc. (ver código original completo)
Símbolos de intensidad de señal (C/C++)
#if defined(__AVR__)
#include <avr/pgmspace.h>
#define imagedatatype const uint8_t
#elif defined(__PIC32MX__)
#define PROGMEM
#define imagedatatype const unsigned char
#elif defined(__arm__)
#define PROGMEM
#define imagedatatype const unsigned char
#endif
imagedatatype signal5[] PROGMEM={ 0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xF0,0xC0,0xF8,0xC0,0xFC,0xC0,0xFE };
imagedatatype signal4[] PROGMEM={ 0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xF0,0xC0,0xF8,0xC0,0xFC,0xC0,0xC0 };
imagedatatype signal3[] PROGMEM={ 0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xF0,0xC0,0xF8,0xC0,0xC0,0xC0,0xC0 };
imagedatatype signal2[] PROGMEM={ 0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xF0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0 };
imagedatatype signal1[] PROGMEM={ 0xC1,0xC2,0xC4,0xFF,0xC4,0xC2,0xC1,0xC0,0xE0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0 };
Esquemático
Esquema completo del circuito (imagen referenciada).
Proceso de manufactura
- La Evolución e Ingeniería de la Tecnología de Radio
- Circuitos integrados (CI): chips compactos y potentes que impulsan la electrónica moderna
- Generador de Ondas JX – Oscilador DDS Compatible con Arduino con Pantalla OLED
- MotionSense: Detección inteligente de intrusos con Arduino y ESP8266
- Construye una Caja Puzle IoT Segura con Arduino MKR Bundle – Guía Paso a Paso
- Regulador de Ducha Inteligente – Válvula Solenoide para Control Preciso de Temperatura
- Escáner de Temperatura IR Inalámbrico – Arduino Nano, Bluetooth y MLX90614
- Construye tu Propio Tanque Autónomo: Walabot, Arduino, Raspberry Pi y Alexa
- Laboratorio avanzado de microcontroladores: ESP32, Arduino, PIC y módulos de sensores
- Kit MOSMusic: Control de Iluminación de Alta Potencia Sincronizada con Música vía MOSFET y Arduino
- Pulsera inteligente con Arduino MKR GSM 1400 y Hologram IoT