En este tutorial a vamos controlar un sistema mediante un mando a distancia, pero antes de todo esto, sería bueno conocer un poco mejor la tecnología que hay detrás, ya que aunque parezca cosa de magia, no lo es, tan solo se trata de un emisor y un receptor que para comunicarse usan un tipo de luz no visible por el ojo humano (luz infrarroja) que mediante pulsos de luz son capaces de codificar información. Pero no nos adelantemos...
¿Qué es la luz Infrarroja?
La luz o radiación infrarroja, se encuentra dentro del espectro electromagnético, entre el espectro electromagnético visible y los microondas, su longitud de onda va desde los 0,7 a 1000 micrómetros, en función de su longitud de onda, los infrarrojos se subdividen en 3 categorías:
Infrarrojo cercano (de 800 nm a 2500 nm)
Infrarrojo medio (de 2.5 µm a 50 µm)
Infrarrojo lejano (de 50 µm a 1000 µm)
Como curiosidad, decir que si queremos ver si un mando a distancia emite o no infrarrojos, podemos usar cualquier cámara (la del móvil nos sirve) para verlos, esto es así debido a que las cámaras están preparadas para ver algo más de rango del espectro visible y nos lo traducen a algo que podemos ver. Esto nos resultará muy útil si queremos averiguar si nuestro mando está averiado o no.
¿Cómo funciona un mando a distancia infrarrojo?
Bueno, antes de nada, decir que cada fabricante de mandos desarrolló su propio protocolo de comunicación, es por ello que no todos los mandos son compatibles y cuando alguno se nos rompe, debemos ir a comprar uno universal (al que configuramos el protocolo en función del modelo del aparato a controlar). Todos los protocolos de comunicación tienen algo en común y es el uso de una señal portadora en la que se modulan los pulsos que contienen la información, esta señal portadora oscila en frecuencia de los 36 a 50KHz, siendo lo mas común usar esta señal a 38KHz.
Entre los protocolos de transmisión de datos, uno de los más extendidos es el protocolo NEC, desarrollado por la empresa Japonesa Nippon Electronic Company, este protocolo, utiliza una onda portadora de 38KHz moduladas por distancia de pulsos (PDM). La onda portadora tiene un periodo de 26µs y la diferenciación entre un nivel lógico de 1 y un nivel lógico de 0 estará definido por la duración entre los pulsos.
Nivel Lógico 0: Un pulso de 562.5µs seguido por un espacio de 562.5µs.
Nivel Lógico 1: Un pulso de 562.5µs seguido por un espacio de 1.675µs.
En protocolo NEC utiliza 2 byte para la comunicación, el primer byte hace referencia a la dirección y el segundo byte a la información, esto quiere decir que con este protocolo se pueden controlar hasta 256 dispositivos diferentes y enviar a cada uno de estos dispositivos 256 instrucciones distintas. Además este protocolo tiene la particularidad de enviar tanto el byte de dirección como el de datos 2 veces, la primera vez que lo envía lo hace de forma normal y la segunda vez lo hace negado, esto sirve para verificar que tanto la dirección como el dato que se envía son correctos.
Ua vez se recibe esta señal, tan solo hay que decodificarla, pero debido a la popularidad del los sistemas de control remoto mediante infrarrojos, esta tecnología esta bastante desarrollada y el trabajo más duro lo hace el propio decodificador, que ya está preparado para trabajar con la mayoría de estándares que existen en el mercado y nos facilitan mucho la vida, en nuestro caso, en este tutorial usaremos el TL1838, pero la mayoría de receptores trabajan de forma similar.
Montaje del Circuito
En este tutorial vamos a montar un circuito sencillo, que nos permita capturar los datos que emite un mando a distancia y con estos datos vamos a encender o apagar diferentes LEDs, el montaje que presento aquí se puede complicar todo lo que queráis añadiendo tantos LEDs como necesitéis y recordar que al final un LED se puede sustituir por un relé y activar cargas de más potencia, por lo que podéis hacer pequeñas automatizaciones en casa utilizando este sistema.
Receptores IR
Antes de montar el receptor IR en la placa de prototipado y alimentarlo, debéis comprobar que receptor tenéis y cual es el patillaje que tiene, si lo montáis de forma incorrecta es muy probable que lo dañéis, en mi caso voy a utilizar el receptor TL1838 que funciona bastante bien, no necesita ningún dispositivo externo y su precio es muy económico.
Podéis encontrar estos receptores como un componente independiente o montados sobre pequeñas placas a las que normalmente se les añade un LED para indicar la recepción de datos. Si compráis la opción en la que esta montado sobre una placa es mucho más sencillo conectarlos, ya que en la propia placa vendrán indicadas las conexiones.
Programa de control
Debido a que cada mando a distancia tiene una codificación diferente, vamos a trabajar con dos programas, el primero nos va a ayudar a identificar que código manda cada una de las teclas del mando que estéis usando, esto es esencial, ya que si no conocéis que código manda cada tecla, no podréis programar instrucciones de programa que interactúen al recibir uno u otro código. El segundo programa ya sabiendo que código debe recibir, encenderá uno u otro LED dependiendo del código recibido.
Programa de lectura de códigos
Este es el programa que recibirá los datos de mando a distancia y los mostrará por el puerto serie
#include <IRremote.h> //Enlaza la librería del receptor IR #define RECV_PIN 10 IRrecv irrecv(RECV_PIN); decode_results results; void setup() { Serial.begin(9600); //Inicializa el puerto serie irrecv.enableIRIn(); //Inicializa el receptor } void loop() { if (irrecv.decode(&results)) //Si recibe un dato { Serial.println(results.value, HEX); //Muestra el dato en el monitor serie irrecv.resume(); //Se prepara para recibir el siguiente valor } }
Este es un programa muy básico donde tenemos unos pocos elementos que pasaremos a describir detenidamente
La cabecera del programa
En la cabecera del programa lo primero que nos encontramos es la librería IRremote.h esta librería es la encargada de realizar la codificación de datos y devolvernos el valor recibido en hexadecimal.
Podéis descargaros la librería IRremote.h aqui
Posteriormente asignamos a RECV_PIN el valor 10 con la instrucción #define RECV_PIN 10 y le indicamos a la liberría cual será el PIN por el que entrará el dato del mando a distancia con IRrecv irrecv(RECV_PIN);
La siguiente instrucción decode_results results; asigna "result" como el lugar donde almacenará los datos decodificados por la librería.
Bloque setup()
El bloque setup() de este programa es muy simple, ya que tan solo necesitamos habilitar el puerto serie pera ver los datos que envía el mando y habilitar el receptor, esto se hace en tan solo un par de instrucciones.
void setup() { Serial.begin(9600); //Inicializa el puerto serie irrecv.enableIRIn(); //Inicializa el receptor } (adsbygoogle = window.adsbygoogle || []).push({}); Bloque loop() El bloque loop() en este programa estará constantemente "escuchando" para ver si recibe alguna señal del mando, en el momento que una señal sea recibida la mostrará por el puerto serie void loop() { if (irrecv.decode(&results)) //Si recibe un dato { Serial.println(results.value, HEX); //Muestra el dato en el monitor serie irrecv.resume(); //Se prepara para recibir el siguiente valor } }
La forma en la que funciona es la siguiente, el bloque condicional "if" no se ejecutará mientras no reciba un "resultado", esto lo hace la instrucción if (irrecv.decode(&results)) , en el momento que recibe un resultado, entra en el bloque mostrando el dato por el puerto serie con la instrucción Serial.println(results.value, HEX); e inicializa el receptor preparándolo para recibir el siguiente dato irrecv.resume();
Programa para la activación de LEDs mediante mando a distancia
Una vez que conocemos los códigos que envía nuestro mando a distancia y sabemos como el programa capta esta codificación, el problema se resume básicamente a comparar los códigos recibidos con los que nosotros le establezcamos, si al comparar el código este concuerda con alguno igual, ejecutará la parte de programa que le tengamos indicada.
#include <IRremote.h> //Enlaza la librería del receptor IR IRrecv irrecv(RECV_PIN); decode_results results; #define RECV_PIN 10 //Asocia el pin de recepción con el pin 10 #define led_1 11 //Asocia led_1 al pin 11 #define led_2 12 //Asocia led_2 al pin 12 boolean led_1_est = false; //variable que almacena el estado del led boolean led_2_est = false; //variable que almacena el estado del led void setup() { Serial.begin(9600); //Inicializa el puerto serie irrecv.enableIRIn(); //Inicializa el receptor pinMode (led_1, OUTPUT); //Establece led_1 como salida pinMode (led_2, OUTPUT); //Establece Led_2 como salida } void loop() { if (irrecv.decode(&results)) //Si recibe un dato { Serial.println(results.value, HEX); //muestra en el monitor serie el dato irrecv.resume(); //Se prepara para recibir el siguiente valor if (results.value == 0xFF51AE) led_1_est = !led_1_est; //Si el dato recibido coincide con el almacenado //cambia el estado del led_1 else if (results.value == 0xFF916E) led_2_est = !led_2_est; //Si el dato recibido coincide con el almacenado //cambia el estado del led_2 else { Serial.println ("Codigo no valido"); //Si no coincide con los valores almacenados muestra "Codigo no valido" } } digitalWrite(led_1,led_1_est); //Asigna a led_1 el estado que posea "led_1_est" digitalWrite(led_2,led_2_est); //Asigna a led_2 el estado que posea "led_2_est" }
Como este programa guarda mucha similitud con el anterior, tan solo voy a explicar la parte de la lógica de control, que es la que difiere entre ambos programas.
La cabecera del programa
Aquí podemos encontrar definidos los dos LEDs que hemos establecido en los pines 11 y 12, además de 2 variables que almacenarán el estado de los LEDs
#define led_1 11 //Asocia led_1 al pin 11
#define led_2 12 //Asocia led_2 al pin 12
boolean led_1_est = false; //variable que almacena el estado del led
boolean led_2_est = false; //variable que almacena el estado del led
Estas variables nos serán muy útiles en la lógica del programa, ya que en lugar de encender o apagar el LED al cumplirse la condición, lo que vamos a cambiar es el valor del estado, siendo este valor el que luego vamos a transmitir al LED.
Bloque setup()
void setup() { Serial.begin(9600); //Inicializa el puerto serie irrecv.enableIRIn(); //Inicializa el receptor pinMode (led_1, OUTPUT); //Establece led_1 como salida pinMode (led_2, OUTPUT); //Establece Led_2 como salida }
En este bloque tan solo encontramos como diferencia del ejemplo anterior la definición de los LEDs como salida.
pinMode (led_1, OUTPUT); //Establece led_1 como salida
pinMode (led_2, OUTPUT); //Establece Led_2 como salida
Bloque loop()
void loop() { if (irrecv.decode(&results)) //Si recibe un dato { Serial.println(results.value, HEX); //muestra en el monitor serie el dato irrecv.resume(); //Se prepara para recibir el siguiente valor if (results.value == 0xFF51AE) led_1_est = !led_1_est; //Si el dato recibido coincide con el almacenado //cambia el estado del led_1 else if (results.value == 0xFF916E) led_2_est = !led_2_est; //Si el dato recibido coincide con el almacenado //cambia el estado del led_2 else { Serial.println ("Codigo no valido"); //Si no coincide con los valores almacenados muestra "Codigo no valido" } } digitalWrite(led_1,led_1_est); //Asigna a led_1 el estado que posea "led_1_est" digitalWrite(led_2,led_2_est); //Asigna a led_2 el estado que posea "led_2_est" }
En este bloque, el programa comprobará continuamente si recibe algún dato, si es así, entrará en el primer bloque if (ya explicado en el programa anterior) y en el segundo bloque se comprobará si el valor recibido es igual a algun valor establecido:
if (results.value == 0xFF51AE) led_1_est = !led_1_est;
else if (results.value == 0xFF916E) led_2_est = !led_2_est;
Si el resultado leído coincide con el resultado establecido (en naranja), el programa cambia el valor de la variable de estado led_1_est = !led_1_est o lo que es lo mismo, si el estado del LED es "0" o apagado, pasará a "1" o encendido y al revés.
Si no coincide con ningún valor, se ejecutará el else final que mostrará en el puerto serie "Código no valido".
En la última parte, ya fuera de todos los bloques condicionales se refrescará el valor de los pines de salidas asociados a los LEDs en función del estado de las variables
digitalWrite(led_1,led_1_est); //Asigna a led_1 el estado que posea "led_1_est"
digitalWrite(led_2,led_2_est); //Asigna a led_2 el estado que posea "led_2_est"
Como se puede ver, este es un programa totalmente escalable y la lógica de control se puede complicar tanto como quedamos, pudiendo por ejemplo establecer una secuencia de 2 o más botones pulsados para ejecutar alguna acción.
Comentarios potenciados por CComment