Descripción General: Esta práctica amplía el sistema de la Práctica 3 incorporando control inalámbrico mediante Infrarrojos (IR) a través de un receptor conectado en el Pin 43 de la placa Arduino Mega 2560. El sistema mantiene la barra de 9 LEDs, los 4 pulsadores físicos, el teclado matricial 4x4, el sensor DHT11 y la pantalla gráfica GLCD ST7920 sin bloqueos temporales mediante el uso de millis().
Distribución de entradas, salidas y líneas de comunicación del sistema:
| Módulo / Dispositivo | Pines en Arduino Mega | Tipo / Señal | Modo / Configuración |
|---|---|---|---|
| Barra de 9 LEDs | 13, 12, 11, 10, 9, 8, 7, 6, 5 | Salidas Digitales | OUTPUT (Nivel HIGH enciende el LED) |
| 4 Pulsadores Físicos | 28, 26, 24, 22 | Entradas Digitales | INPUT_PULLUP (Activos a nivel LOW) |
| Teclado Matricial 4x4 | Filas: 23, 25, 27, 29 | Cols: 31, 33, 35, 37 | E/S Matricial | Gestionado por librería Keypad |
| Sensor DHT11 | Pin 41 | Señal Digital (Data) | Lectura ambiental de Tª y Humedad |
| Pantalla GLCD ST7920 | 50 (RS/CS), 51 (R/W/DATA), 53 (E/CLK) | Software SPI | Librería U8g2 (128x64 px) |
| Receptor Infrarrojo (IR) | Pin 43 | Entrada Digital (Out) | Demodulador a 38 kHz (Librería IRremote) |
IRremoteEl sensor infrarrojo capta luz IR modulada a una frecuencia estándar de 38 kHz para rechazar interferencias de la luz ambiental. Al recibir señales, demodula la trama y entrega pulsos digitales al Pin 43 de la Arduino Mega.
Reasignación de Temporizadores en Arduino Mega 2560:
Por defecto, la librería IRremote usa el Timer 2 en microcontroladores AVR para medir los tiempos entre pulsos. Para evitar conflictos con el refresco de pantalla o salidas PWM, se reconfigura expresamente para emplear el Timer 3 declarando la siguiente directiva antes de incluir la librería:
La lectura del sensor se realiza en la función leerIR() de forma no bloqueante:
IrReceiver.decode(): Evalúa si hay una nueva trama decodificada disponible.IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT: Identifica y descarta ráfagas automáticas de repetición cuando un botón se mantiene presionado.IrReceiver.decodedIRData.command & 0xFF: Extrae el byte del comando recibido.IrReceiver.resume(): Reinicia el receptor para capturar el siguiente pulso.El mando a distancia utiliza el protocolo **NEC** para transmitir los códigos. Al presionar una tecla, el receptor obtiene el comando en formato hexadecimal (HEX) mapeado hacia los modos del sistema:
| Botón del Mando | Código Hexadecimal (Command) | Tecla Registrada | Acción sobre los LEDs |
|---|---|---|---|
| Botón 1 | 0x45 |
'1' | Modo 0: Enciende los 9 LEDs fijamente |
| Botón 2 | 0x46 |
'2' | Modo 1: Apaga todos los LEDs |
| Botón 3 | 0x47 |
'3' | Modo 2: Secuencia LED individual (barrido) |
| Botón 4 | 0x44 |
'4' | Modo 3: Secuencia LED acumulativa |
| Origen del Control | Dispositivo | Función de Lectura | Dato / Trama Procesada |
|---|---|---|---|
| Pulsadores Físicos | Botones (Pines 28, 26, 24, 22) | leerBotones() |
Lectura digital directa (LOW) |
| Teclado Matricial | Matriz 4x4 (Pines 23 a 37) | leerTeclado() |
Caracteres ASCII ('1' a '4') |
| Control Infrarrojo | Mando IR + Receptor (Pin 43) | leerIR() |
Comandos Hex (0x45, 0x46, 0x47, 0x44) |
/*
=============================================================================
PRÁCTICA 4: Integración de Receptor IR (Pin 43)
Placa: Arduino Mega 2560
=============================================================================
*/
// ==========================================
// CONFIGURACIÓN DE TIMERS (ANTES DE INCLUIR LIBRERÍAS)
// ==========================================
#define IR_USE_AVR_TIMER3
#include <Keypad.h>
#include <DHT.h>
#include <U8g2lib.h>
#include <IRremote.hpp>
// ==========================================
// 1. PINES DE LOS LED
// ==========================================
const int leds[] = {13, 12, 11, 10, 9, 8, 7, 6, 5};
const int NUM_LEDS = sizeof(leds) / sizeof(leds[0]);
// ==========================================
// 2. PULSADORES FISICOS
// ==========================================
const int botones[] = {28, 26, 24, 22};
const int NUM_BOTONES = sizeof(botones) / sizeof(botones[0]);
// ==========================================
// 3. TECLADO MATRICIAL 4x4
// ==========================================
const byte FILAS = 4;
const byte COLUMNAS = 4;
char teclas[FILAS][COLUMNAS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte pinesFilas[FILAS] = {23, 25, 27, 29};
byte pinesColumnas[COLUMNAS] = {31, 33, 35, 37};
Keypad teclado = Keypad(
makeKeymap(teclas),
pinesFilas,
pinesColumnas,
FILAS,
COLUMNAS
);
// ==========================================
// 4. SENSOR DHT11
// ==========================================
const int PIN_DHT = 41;
DHT dht(PIN_DHT, DHT11);
float temperatura = 0;
float humedad = 0;
unsigned long tiempoDHT = 0;
const unsigned long INTERVALO_DHT = 2000;
// ==========================================
// 5. PANTALLA GLCD
// ==========================================
const int GLCD_RS = 50;
const int GLCD_RW = 51;
const int GLCD_E = 53;
U8G2_ST7920_128X64_F_SW_SPI pantalla(
U8G2_R2,
GLCD_E,
GLCD_RW,
GLCD_RS,
U8X8_PIN_NONE
);
// ==========================================
// 6. RECEPTOR IR
// ==========================================
const int PIN_IR = 43; // Receptor IR en Pin 43
#define IR_BOTON_1 0x45
#define IR_BOTON_2 0x46
#define IR_BOTON_3 0x47
#define IR_BOTON_4 0x44
// ==========================================
// 7. VARIABLES DEL PROGRAMA
// ==========================================
int modo = 1;
int ledActual = 0;
bool acumulativo = false;
char ultimaTecla = '0';
unsigned long tiempoLED = 0;
const unsigned long INTERVALO_LED = 300;
unsigned long tiempoPantalla = 0;
const unsigned long INTERVALO_PANTALLA = 500;
// ==========================================
// 8. FUNCIONES DE LOS LED
// ==========================================
void apagarLeds() {
for (int i = 0; i < NUM_LEDS; i++) {
digitalWrite(leds[i], LOW);
}
}
void encenderLeds() {
for (int i = 0; i < NUM_LEDS; i++) {
digitalWrite(leds[i], HIGH);
}
}
void seleccionarModo(int nuevoModo) {
modo = nuevoModo;
ledActual = 0;
acumulativo = false;
apagarLeds();
if (modo == 0) {
encenderLeds();
}
}
// ==========================================
// 9. LECTURA DE LOS PULSADORES FISICOS
// ==========================================
void leerBotones() {
for (int i = 0; i < NUM_BOTONES; i++) {
if (digitalRead(botones[i]) == LOW) {
ultimaTecla = '1' + i;
seleccionarModo(i);
}
}
}
// ==========================================
// 10. LECTURA DEL TECLADO MATRICIAL
// ==========================================
void leerTeclado() {
char tecla = teclado.getKey();
if (tecla >= '1' && tecla <= '4') {
ultimaTecla = tecla;
seleccionarModo(tecla - '1');
}
}
// ==========================================
// 11. LECTURA DEL MANDO IR
// ==========================================
void leerIR() {
if (IrReceiver.decode()) {
if (!(IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)) {
uint8_t comando = IrReceiver.decodedIRData.command & 0xFF;
Serial.print("IR recibido: 0x");
Serial.println(comando, HEX);
switch (comando) {
case IR_BOTON_1:
ultimaTecla = '1';
seleccionarModo(0);
break;
case IR_BOTON_2:
ultimaTecla = '2';
seleccionarModo(1);
break;
case IR_BOTON_3:
ultimaTecla = '3';
seleccionarModo(2);
break;
case IR_BOTON_4:
ultimaTecla = '4';
seleccionarModo(3);
break;
}
}
IrReceiver.resume();
}
}
// ==========================================
// 12. ANIMACION DE LOS LED
// ==========================================
void actualizarLeds() {
if (modo < 2) {
return;
}
if (millis() - tiempoLED < INTERVALO_LED) {
return;
}
tiempoLED = millis();
if (modo == 2) {
apagarLeds();
digitalWrite(leds[ledActual], HIGH);
ledActual++;
if (ledActual >= NUM_LEDS) {
ledActual = 0;
}
}
else if (modo == 3) {
digitalWrite(leds[ledActual], HIGH);
ledActual++;
if (ledActual >= NUM_LEDS) {
apagarLeds();
ledActual = 0;
}
}
}
// ==========================================
// 13. LECTURA DEL DHT11
// ==========================================
void actualizarDHT() {
if (millis() - tiempoDHT >= INTERVALO_DHT) {
tiempoDHT = millis();
float nuevaHumedad = dht.readHumidity();
float nuevaTemperatura = dht.readTemperature();
if (!isnan(nuevaHumedad) && !isnan(nuevaTemperatura)) {
humedad = nuevaHumedad;
temperatura = nuevaTemperatura;
}
}
}
// ==========================================
// 14. ACTUALIZACION DE LA PANTALLA
// ==========================================
void actualizarPantalla() {
if (millis() - tiempoPantalla < INTERVALO_PANTALLA) {
return;
}
tiempoPantalla = millis();
pantalla.clearBuffer();
pantalla.setFont(u8g2_font_6x10_tf);
pantalla.drawStr(0, 10, "Practica 3 EMP");
char strTemp[10];
char strHum[10];
char textoTemperatura[25];
char textoHumedad[25];
char textoTecla[25];
dtostrf(temperatura, 4, 1, strTemp);
dtostrf(humedad, 4, 1, strHum);
snprintf(textoTemperatura, sizeof(textoTemperatura), "Temp: %s C", strTemp);
snprintf(textoHumedad, sizeof(textoHumedad), "Hum: %s %%", strHum);
snprintf(textoTecla, sizeof(textoTecla), "Ultima Tecla: %c", ultimaTecla);
pantalla.drawStr(0, 27, textoTemperatura);
pantalla.drawStr(0, 40, textoHumedad);
pantalla.drawStr(0, 53, textoTecla);
pantalla.sendBuffer();
}
// ==========================================
// 15. CONFIGURACION INICIAL
// ==========================================
void setup() {
for (int i = 0; i < NUM_LEDS; i++) {
pinMode(leds[i], OUTPUT);
}
for (int i = 0; i < NUM_BOTONES; i++) {
pinMode(botones[i], INPUT_PULLUP);
}
Serial.begin(115200);
apagarLeds();
IrReceiver.begin(PIN_IR, DISABLE_LED_FEEDBACK);
dht.begin();
pantalla.begin();
pantalla.clearBuffer();
pantalla.setFont(u8g2_font_6x10_tf);
pantalla.drawStr(0, 15, "Practica 3 EMP");
pantalla.drawStr(0, 30, "Iniciando...");
pantalla.sendBuffer();
tiempoDHT = millis() - INTERVALO_DHT;
tiempoPantalla = millis() - INTERVALO_PANTALLA;
}
// ==========================================
// 16. BUCLE PRINCIPAL
// ==========================================
void loop() {
leerBotones();
leerTeclado();
leerIR();
actualizarLeds();
actualizarDHT();
actualizarPantalla();
}