Este proyecto consiste en la construcción de un medidor de potencia y ROE digital, utilizando un Arduino Nano y una pantalla LCD 20x4 con interfaz I2C. El dispositivo está diseñado para medir con precisión la relación de ondas estacionarias (ROE) y la potencia en sistemas de radiofrecuencia, ideal para aplicaciones de radioaficionados. Incluye un pequeño circuito detector de RF para capturar las señales y calcular tanto la potencia transmitida como la ROE, ofreciendo una lectura clara y fácil de interpretar en la pantalla. La combinación de componentes sencillos y accesibles hace que este proyecto sea una solución eficaz y económica para quienes deseen monitorear el rendimiento de sus transmisores de radio.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4); // dirección 0x3f, 20 caracteres, 4 líneas
// definimos en qué puerto está la tensión directa y en cuál la reflejada para usarla después
char FWD = A0;
char REF = A1;
// buzzer
int BUZZER = 9;
// inicializamos valores
float SWR = 0.00;
// definimos a qué valor sonará la alarma de roe alta
float LIMITE = 2.2;
void setup() {
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Medidor Potencia SWR");
lcd.setCursor(0, 1);
lcd.print("transmisores");
lcd.setCursor(0, 2);
lcd.print("y proyectos");
pinMode(FWD, INPUT);
pinMode(REF, INPUT);
pinMode(BUZZER, OUTPUT);
beep(50);
beep(50);
beep(50);
delay(3000);
}
void loop() {
// Tensión actual de alimentación
float TENSION = float(readVcc()) / 1000;
// Leemos puerto
float Vfwd = analogRead(FWD);
float Vref = analogRead(REF);
// Convertimos a tensiones
float TENSION_DIRECTA = ((Vfwd * TENSION) / 1023);
float TENSION_REFLEJADA = ((Vref * TENSION) / 1023);
// Calculamos tensión RMS
float RMS_DIR = TENSION_DIRECTA * 0.707 * 10; // Relación de transformación 1:10 en toroide
float RMS_REF = TENSION_REFLEJADA * 0.707 * 10;
// Potencias
float POWER_DIR = (pow((RMS_DIR), 2) / 50) * 10; // Relación de transformación 1:10 en toroide
float POWER_REF = (pow((RMS_REF), 2) / 50) * 10;
// Despejamos las estacionarias
float SWR = (1 + sqrt(POWER_REF / POWER_DIR)) / (1 - sqrt(POWER_REF / POWER_DIR));
// Fin cálculos, pintamos los datos.
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("POTENCIA:");
lcd.setCursor(12, 0);
lcd.print(POWER_DIR);
LCD_progress_bar(1, POWER_DIR, 0, 100);
lcd.setCursor(0, 2);
lcd.print("ROE:");
lcd.setCursor(12, 2);
lcd.print(SWR);
if (SWR < 1.00) {
// nada
} else {
LCD_progress_bar(3, SWR, 0, 10);
}
if (SWR > LIMITE) {
beep(100);
lcd.setCursor(12, 3);
lcd.print("ALARMA");
}
delay(1000); // Un segundo de retardo para refrescar pantalla
}
long readVcc() {
// Leer referencia de 1.1V contra AVcc
// Configurar la referencia a Vcc y la medición a la referencia interna de 1.1V
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Esperar a que se estabilice Vref
ADCSRA |= _BV(ADSC); // Iniciar conversión
while (bit_is_set(ADCSRA, ADSC)); // Medición en curso
uint8_t low = ADCL; // Leer ADCL primero - luego bloquea ADCH
uint8_t high = ADCH; // Desbloquea ambos
long result = (high << 8) | low;
result = 1125300L / result; // Calcular Vcc (en mV); 1125300 = 1.1*1023*1000
return result; // Vcc en milivoltios
}
void LCD_progress_bar(int row, int var, int minVal, int maxVal) {
int block = map(var, minVal, maxVal, 0, 20); // El bloque representa el espacio actual del LCD
int line = map(var, minVal, maxVal, 0, 80); // La línea representa las líneas teóricas que se deben imprimir
int bar = (line - (block * 5)); // La barra representa las líneas actuales que se imprimirán
/* Caracteres de barra de progreso LCD, crea tus barras personalizadas */
byte bar1[8] = { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10};
byte bar2[8] = { 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18};
byte bar3[8] = { 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C};
byte bar4[8] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E};
byte bar5[8] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F};
lcd.createChar(1, bar1);
lcd.createChar(2, bar2);
lcd.createChar(3, bar3);
lcd.createChar(4, bar4);
lcd.createChar(5, bar5);
for (int x = 0; x < block; x++) { // Imprimir todos los bloques llenos
lcd.setCursor(x, row);
lcd.write(5); // Cambiado a 5 (el último bloque definido)
}
lcd.setCursor(block, row); // Establecer el cursor en el bloque actual y imprimir las líneas necesarias
if (bar != 0) lcd.write(bar);
if (block == 0 && line == 0) lcd.write(32); // Espacio en blanco (ASCII 32)
for (int x = 16; x > block; x--) { // Imprimir todos los bloques en blanco
lcd.setCursor(x, row);
lcd.write(32); // Espacio en blanco (ASCII 32)
}
}
// Función para hacer sonar el buzzer
void beep(unsigned char pausa) {
analogWrite(BUZZER, 500);
delay(pausa);
analogWrite(BUZZER, 0);
delay(pausa);
}







