Electrónica y Programación en Personal
«Si no se soluciona con un script en Python entonces no es viable»

por Yeison Cardona el 28 de septiembre de 2012 a las 21:28 UTC
Esta bien, no es exactamente un decodificar IR, de hecho no estoy seguro de que hace uno de éstos, el proyecto que presento en un procesador de mandos infrarrojos, como los de los controles remotos, que de seguro han de tener unos cuantos por ahí y no saben que hacer con ellos.


Objetivo:
Desarrollar una interfaz que permita controlar los comando multimedia estándar en los sistemas GNU/Linux usando un control remoto por infrarrojo y la tarjeta Pinguino.


Hardware:




La tarjeta Pinguino (obvio), un equipo con GNU/Linux (supongo que igual se podrá con un Windows o un MAC pero no tengo ninguno), un control remoto y un sencillo receptor infrarrojo (como el de la imagen).






Software:
Usaremos Python (como siempre) y el programa xdotool el cual pueden instalar desde los repositorios de Ubuntu.
$ sudo apt-get install xdotool
Código en Pinguino: Hay que dar créditos al autor del que me basé para entender el protocolo IR http://learn.adafruit.com/ir-sensor quien también distribuye un código para Arduino. No quiero decir que el código original esté mal, simplemente que es mas complejo de lo necesario, o al menos con Pinguino no necesitamos de tantas instrucciones. El protocolo IR maneja unos tiempos bastante reducidos, tanto, que cuando hacemos digitalRead() ya han pasado unos cuantos datos que nos hemos perdido así que necesitamos un modo mas rápido para leer pines y la respuesta está en las librerías de Pinguino (digitalw.c) la cual resumiendo y para nuestro caso específico usaremos de la siguiente manera; usando el pin 0, que pertenece al puerto B, entonces: digitalRead(0) --> (PORTB &amp; 1<<0) ahora implementando este truco en el código obtenemos:
/*----------------------------------------------------- 
Author: Yeison Cardona --
Date: Fri Sep 28 18:56:57 2012
Description: Decodificador IR con Pinguino

-----------------------------------------------------*/

#define IRpin 0
#define READ_IRpin (PORTB &amp; 1<<0)

#define MAXPULSE 1000
#define RESOLUTION 20

u16 pulses[50]; 
u32 con_pulse, i, d0;
u16 total_pulse=0;

void setup(void) {
    pinMode(IRpin,INPUT);
    }

void loop(void) {  
    con_pulse=0;
    
    //Contamos el tiempo en que el pin se encuentra en alto
    while(READ_IRpin){ //while(digitalRead(IRpin)){
        delayMicroseconds(RESOLUTION); //para contar en bloques de 20 microsegundos
        con_pulse++;
        if (con_pulse>MAXPULSE){ //Si ya se terminó un comando (o no se ha ingresado ninguno)
            printpulses(); //Envía los datos
            return; //reinicia loop
            }
         } 
     pulses[total_pulse]=con_pulse; //guardamos el tiempo que duró el alto
     //el tiempo real sería con_pulse*RESOLUTION microsgundos
     
    //Saltamos el tiempo en que el pin se encuentra en bajo
    //En mi caso este valor se mantenía constante, así que no lo cuento
    con_pulse=0;
    while(!READ_IRpin){
        delayMicroseconds(RESOLUTION);
        con_pulse++;
        } 
        
    //Aumentamos la cantidad de pulsos leídos
    total_pulse++;   
    }

void printpulses() {
    //Enviar la secuencia de datos
    if (total_pulse>2){ //Si hay los suficientes datos
        total_pulse=0;//Reiciamos el contador
        for (i = 0; i < 50; i++) { //Para todos los datos almacenados
            d0=pulses[i]; //Obtenmos uno
            if (d0>34) CDC.print("0"); //y si es mayor a 34 enviamos un 0
            //  en esta lista se almacenan diferentes valores, en mi caso números 
            //  muy cercanos a 60 (1.2 ms) y 15 (0.3 ms)
            //  de ahí aparece el 24 :P
            else CDC.print("1"); //de lo contrario un 1
            }
        CDC.print("\n"); //Finalizamos la línea
        }
        }
El código se encuentra bastante comentado, pero lo único que hace es contar y almacenar el tiempo que dura un alto (señal en pin) en cada comando y si dura mucho tiempo es porque ya ha terminado una instrucción entonces envía el comando. Éstas señales son cuadradas, como se puede ver en las siguientes capturas tomadas de página antes mencionada:
http://learn.adafruit.com/ir-sensor/ir-remote-signals
http://learn.adafruit.com/ir-sensor/ir-remote-signals
http://learn.adafruit.com/ir-sensor/ir-remote-signals
Script en Python: Entonces ya teniendo una secuencia binaria que describe esta señal sólo queda almacenarla para compararla, crear una subrutina para leer mas señales y ejecutar un comando dependiendo de la señal.
#!/usr/bin/env python
#-*- coding: utf-8 -*-

import sys, os
import serial
from time import sleep as delay

########################################################################
class IR_controller:
    """"""
    #----------------------------------------------------------------------
    def __init__(self):
        self.pinguino = False
        
        #Comando proviamente almacenados
        Play_Pause = "00000000000011111111011001111001100011111111010101"
        Stop = "00000000000011111111101111110100000011111111010101"
        Skip_UP = "00000000001111111111011111001000000011111111010101"
        Skip_Down = "00000000000011111111100111110110000011111111010101"
        Prog_EQ = "00000000001111111100110111110010000011111111010101"
        Mode = "00000000000011111111110001110011100011111111010101"
        USB_Card = "00000000000011111111001001111101100011111111010101"
        
        
        #A cada comando se le asocia en un diccionario un comando de consola
        self.exe = {Play_Pause: "xdotool key 'XF86AudioPlay'",
                    Stop: "xdotool key 'XF86AudioStop'",
                    Skip_UP: "xdotool key 'XF86AudioNext'",
                    Skip_Down: "xdotool key 'XF86AudioPrev'",
                    Prog_EQ: "xdotool key 'XF86AudioLowerVolume'",
                    Mode: "xdotool key 'XF86AudioRaiseVolume'",
                    USB_Card: "xdotool key 'XF86AudioMute'"}
        
        #Enlazamos Pinguino
        self.conectar()
        if self.pinguino == False:  #si se puede
            raw_input("No hay dispositivos")
            sys.exit()
    
    #----------------------------------------------------------------------
    def conectar(self):
        #Conectamos al primer puerto serie que se encuentre
        for i in range(20):
            try:
                self.pinguino = serial.Serial("/dev/ttyACM%d"%i, timeout=0.01)
                return self.pinguino
            except: self.pinguino = False
        return False
    
    #----------------------------------------------------------------------
    def readCode(self):
        #Leemos un código
        try:
            data = self.pinguino.readline().replace("\n", "")
            if len(data) > 0:  #Si es de longitud mayor a cero, entoncer contiene información
                os.system(self.exe[data])  #ejecutamos la ordes asociada a la secuencia leída
                print self.exe[data]  #Imprimimos el comando ejecutado
        except:  #Si no se puede, ya sea por ruino o algo mas
            self.readCode  #llamamos a la funcion de nuevo (recursiva)

        
        

Ping = IR_controller() #Creamos el objeto Ping
Ping.pinguino.flushInput() #Limpiamos el buffer
Ping.readCode() #Hacemos una primera lectura, que siempre contiene ruido
while (True):  Ping.readCode()  #leemos y ejecutamos ordenes en tiempo real



Conclusiones:
Una implementación muy sencilla y bastante económica, que nos permitirá rehusar esos controles viejos que de seguro tenemos, puede utilizarse para muchas otras cosas como en aplicaciones de domótica por ejemplo.

También podría interesarte:

Comentarios:
nadakaser dijo:
13 de diciembre de 2012 13:22
Buen dia yeison, creeme cuando te digo ke lo ke ustedes han hecho es fenomenal, el pinguino me ha simplificado bastantes cosas.

Mi duda es, como puedo implementar esto en windows?
Por lo pronto uso xp para no batallar mucho, aunke me imagino ke batallaria menos con linux XD

Andy Ochoa
Responder a nadakaser
Respuesta de nadakaser:
13 de diciembre de 2012 13:28


Bueno, segui mirando tu blog, y no estoy seguro, pero, habria ke crear una biblioteca?
Responder a nadakaser
Yeison Cardona dijo:
20 de diciembre de 2012 14:11
Un saludo @nadakaser No conozco un software para emular estos comando en windows, pero casi seguro de que existe uno que lo haga, sería bueno contar con una librería que hiciera esto, el truco básicamente es implementar un algoritmo que optimice las lecturas análogas y conocer el protocolo a decodificar, por ningún motivo en específico continué con estos ejemplos, ahora que estoy en vacaciones creo que podré pulir bien el código y hacer algo &quot;definitivo&quot;.
hasta pronto.
Responder a Yeison Cardona
Añadir un nuevo comentario:
Si desean una respuesta para su comentario sólo deben agregarme en G+ y hacer una mención a Yeison Cardona, así les podré responder lo antes posible.