Control IR de la lámpara Livarno de Lidl

Hace unos pocos meses, al estar haciendo algunas pruebas con Arduino como receptor de infrarrojos, me di cuenta que el mando IR que normalmente viene con el Arduino Starter Kit, me cambiaba de repente el color de la lámpara que tengo a mi lado, en la que puse una bombilla de LED de Lidl, Livarno HG04230...

El mando a distancia del Starter Kit es éste:

...Y si eso ocurría sin duda era porque el protocolo de señales de este mando y el de la lámpara es el mismo, el popular protocolo NEC...

Este protocolo funciona con una portadora de 38 Khz, y los bits de información vienen dados por la separación entre el flanco de subida de frames de impulsos de 560 uS de duración. Si la separación es de 2.250 uS es un 1, y si es de 1.120 uS es un 0...

...Si descontamos la duración del "frame", que siempre es la misma (560 uS), el tiempo a 0 entre ellos será de 1.690 uS para el 1 lógico y 560 uS para el 0 lógico...

A la vez, el protocolo NEC está formado por la siguiente secuencia:

1) Un frame de 9 mS
2) Un tiempo de espera de 4,5 mS
3) A continuación vienen 33 frames de 560 uS cuya separación define 32 bits, agrupados de la siguiente forma

  1. 8 bits de dirección, seguidos de 8 bits complementarios de los anteriores. Estos bits de dirección normalmente valen 0 (00000000), y por lo tanto su complementario es 1 (11111111). Hasta ahora no he visto que estos bits de dirección se utilicen para el envío de comandos IR de control de electrodomésticos.

  2. 8 bits de datos que contienen la orden que se envía, seguidos de 8 bits complementarios de los anteriores. Esta disposición de un valor seguido de su complementario es para ayudar al software receptor a identificar datos corrompidos durante la trasmisión, ya que para ser válido un byte más su complementario ha de sumar 255...

Continuará...

Saludos

Saludos .
-Alex

Saludos, Alex...

...A la vez, en el Starter Kit el receptor infrarrojo es normalmente un TL1838B, que es mucho más que un fotodiodo o un fototransistor, ya que integra en su interior los necesarios amplificadores, filtros y conformadores de señal para que recuperar la secuencia de bits lo más fielmente posible...

El montaje que utilizaremos para las experiencias con infrarrojos será de lo más sencillo... La salida del sensor TL1838B conectada al pin 11 del Arduino, la masa a GND y el positivo a +5V pero a través de una resistencia de 200Ω y un condensador de filtro de 4,7 uF...

...Ahora podemos buscar algún programa monitor entre los propios "ejemplos" del IDE de Arduino, que nos mostrará en la ventana serie los códigos de cada tecla del mando a distancia que vayamos pulsando... Un resultado puede ser el siguiente:

En este caso los valores nos los ha dado en binario, el valor de pulsación de la tecla, más una serie de 1's que corresponden a las repeticiones. Cambiando la orden de impresión "Serial.print (dato, BIN);" por "Serial.print (dato, HEX);" obtendremos los valores en hexadecimal:

...El problema, que también reportan en foros, es que los datos son inseguros. Pulsando el mismo botón a veces salen valores distintos, y eso ocurre en un porcentaje demasiado alto como para ser fiable. Con todo, a base de repetir los resultados dudosos, reuní las equivalencias botón/dato para este mando a distancia concreto.

Continuará...

Saludos

Llorens

El problema de los frecuentes fallos de lectura parecían tener su origen en las librerías IR. Probé varias y todas hacían lo mismo, e incluso un par me daban errores de compilación, con la dificultad añadida por la caprichosa forma cómo las busca el IDE de Arduino, ya que si tienes dos distintas, una en sus carpetas y la otra en el escritorio, nunca sabes cual realmente te acaba cargando...

Esto lo solucioné olvidando las librerías IR y programando una función específica. Para ello, en primer lugar escribí un "Monitor de tiempos de pulsos" que llamé "IR_NEC_TimeMonitor_01.", ya que si bien el protocolo NEC los determina, es importante saber los valores reales que lee el Arduino...

// IR_NEC_TimeMonitor_01. Llorens Mercadal, jul 2019
// Monitor de longitud de pulsos de protocolo NEC, en uSec.

int pin = 11;         // Pin de entrada de datos
long L_inputs[34];

void setup() 
  {
  Serial.begin(9600);
  }

void loop() 
  {
  if(digitalRead(pin)==0)          // Detecta dato de entrada
    {  
    for (int f=1; f<=33; f++)
      {
      L_inputs[f] = pulseIn(pin, HIGH);  
      }
    for (int f=1; f<=33; f++)
      {
      Serial.print(f);  
      Serial.print("  ");      
      Serial.println(L_inputs[f]);
      }
    Serial.println();
    delay(300); 
    }
  }

El programa usa "pulseIn(pin, HIGH)" para contar el tiempo en uSec que duran los impulsos. El conteo comienza cuando el pin 11 cae a 0, ya que el sensor IR es de lógica negativa. Entonces guarda en un array los uSec de los siguientes 33 pulsos, tras lo cual los imprime en el PC.

La siguiente imagen muestra en un formato racionalizado los valores de tiempo obtenidos de un "frame IR" correspondiente a la pulsación de una tecla cualquiera...

El número rojo indica el orden de los impulsos y el negro el tiempo correspondiente en microsegundos. Por ejemplo, el primer impulso es de 4439 uSec, y el segundo 551 ...Los valores estándar del protocolo NEC son 4500 para el impulso inicial, 560 uSec para los 0's y 1690 para los 1's, sin embargo yo obtengo valores del impulso inicial de 4439 (margen 61), para los 0's que van desde 551 a 599 (margen 48 uSec), y para los 1's desde 1657 a 1702 (margen 45 uSec). Los valores medios leídos son más o menos 580 uSec para los 0's y 1680 para los 1's. Entonces pienso que tal vez, los errores que me dan las librerías habituales IR puedan deberse a filtros demasiado estrictos en los valores de lectura.

Continuará...

Saludos

Llorens

Excelente tutorial.

Gracias Surbyte, siempre intento exponer las cosas tal como me gustaría encontrarlas cuando las busco, aunque no siempre lo consigo. También pasa que me gusta "desmenuzar" los temas y entender las interioridades, que es cuando se aprende de ellos, en vez de limitarme a copiar o a utilizar herramientas que han hecho otros.

Partiendo del programa anterior he escrito otro que me da los códigos de los botones del mando a distancia con más seguridad que las librerías IR habituales...

// IR_NEC_DecoderMonitor_01, Llorens Mercadal, jul 2019
// Decodificador de pulsos en protocolo NEC
// Recibe códigos de 0 a 255
// -1 indica error de lectura
// -2 indica repetición de tecla

int pin = 11;         // Pin de entrada de datos
int datoIR = -1;      // Valor de la tecla pulsada
//**************************************************************

void setup() 
  {
  Serial.begin(9600);
  }
//**************************************************************

void loop() 
  {
  if(digitalRead(pin)==0)         // Detecta dato de entrada
    {  
    datoIR = Decode_IR_NEC();     // Decodifica datos
    Serial.println(datoIR);  
    delay(300); 
    }
  }
//**************************************************************

int Decode_IR_NEC()       // Función de decodificación de pulsos 
  {
  int init = 0;
  int inter = 0;
  int acum = 1;
  int dato0 = 0;
  int dato1 = 0;
  int dato2 = 0;
  
  init = pulseIn(pin, HIGH);
  if(init>4300 && init<4800)                      // Detecta pulso de arranque de valor medio 4500 uS
    {
    for(int f=1; f<=32; f++)                      // Comprueba los 32 pulsos siguientes 
      {
      inter = pulseIn(pin, HIGH);                 // Duracion de cada pulso en microsegundos
      if(inter > 1600 && inter <1800)             // Detecta pulsos de valor lógico 1 (VM 1700 uS)
        {
        if (f>=17 && f<=24) dato1 = dato1 + acum; // Creación bit a bit de dato de salida
        if (f>=25 && f<=32) dato2 = dato2 + acum; // Creación bit a bit de dato complementario
        }
      acum = acum * 2;
      if (f==16 || f==24) acum = 1;      
      }
    }
  dato0 = dato1;
  if ((dato1 + dato2) != 255) dato1 = -1;         // Detección de error de lectura
  if (dato0 == 0 && dato2 == 0) dato1 = -2;       // Detección de repetición
  return dato1;
  }

...El trabajo lo realiza principalmente la función "int Decode_IR_NEC()", que primero detecta el pulso largo de inicio, luego descarta 16 bits de direcciones y guarda por separado dos valores de 8 bits, el dato y su inverso, que utilizo para comprobar la integridad del anterior...

El resultado es excelente, con buena señal IR no falla nunca, retornando el código de tecla, y cuando el mando está muy lejos y la señal llega mal, indica error, retornado un "-1". También detecta la repetición de tecla pulsada, retornado un "-2"...

Con este programa, al primer intento he obtenido de nuevo los códigos, esta vez en decimal, que naturalmente coinciden con los códigos anteriores en binario y hexadecimal obtenidos con las librerías IR, los cuales que tuve que repetir bastantes veces hasta dar por seguros los valores.

OJO: notar que en este listado el valor asignado a la repetición "repet" es 0, cuando antes he dicho que es -2. Esto es porque este listado lo obtuve con una versión anterior del programa, que efectivamente asignaba a la repetición el valor 0, pero luego resultó que el mando a distancia propio de la lámpara Livarno también utilizaba el 0 para identificar un botón concreto, con lo cual tuve que cambiar el código y dejar este valor disponible.

Continuará...

Saludos

Llorens

Seguidamente, con el mismo programa he obtenido los códigos de las teclas del mando IR de la bombilla de Lidl...

...Observando que algunos coinciden con el mando del Starter Kit de Arduino, por lo cual es explicable que afectara a la lámpara.

Otro detalle es le código correspondiente a la tecla "Strobe", que es "0", y es que en realidad dicho código es distinto a su ausencia, y por tanto se debería poder utilizar. Por este motivo cambié en el programa anterior el código de salida para la "repetición", dejándolo en -2 y permitiendo el control del pulsador "Strobe"

Ahora lo siguiente será programar un emisor de IR en protocolo NEC, sin utilizar librerías. A ver que tal me sale...

Continuará...

Saludos

Llorens

Estoy tan compenetrado en este momento con un proyecto que no he leído con la profundidad debida tu detallado tutorial pero ya te haré mi devolución, aunque claro que no la necesitas, pero tengo el mismo criterio de "desmenuzar" los temas.

Hay dos sistemas básicos para generar la secuencia que enviaremos a un LED emisor de infrarrojos, o bien utilizar la orden "tone (pin, frecuencia)", que no es demasiado estable debido al firmware interno del Arduino, o mejor programar directamente los "timers" del microcontrolador Atmega328, opción que elegí, aunque en el momento de realizar este proyecto aún no me había metido en con el tema de los Timers, que cuesta un algo entender porque las descripciones que normalmente se encuentran en la Red o son demasiado simples o demasiado complejas y confusas, y por tanto en este caso utilicé las órdenes de configuración de un código de Nick Gammon que encontré en una búsqueda.

TCCR2A = _BV (COM2A0) | _BV(WGM21); // configura Timer 2
TCCR2B = _BV (CS20); // Sin preescaler
OCR2A = 209; // compara valor registro A (210 * clock)= 13.125 nS. Frequencia 1/(2*13.125)=38095

...Esta rutina genera una señal continua de 38 Khz que sale por el pin 11 y no puede interrumpirse. Para modular los IR con los pulsos concretos de la señal NEC, es necesario utilizar otro pin en lógica negativa (el 7), y que el LED emisor (con su resistencia en serie) esté conectado con el positivo al pin 11 y el negativo al pin 7. De esta manera, si el pin 7 está en HIGH el cátodo del LED es siempre positivo y no emitirá infrarrojos. En el instante que el pin 7 se ponga a LOW, el cátodo se lleva a masa y entonces se emitirán infrarrojos al ritmo de 38 Khz del pin 11, conectado al ánodo del LED...

El código del programa es el siguente:

// Control IR de lámpara Lidl Livarno HG04230, Llorens Mercadal (Anilandro) jul 2019

int dato=-1;
int secuencia[33];                      // Secuencia NEC a emitir + 1 bit a 0 de final
int pinLED = 11;                        // Pin salida de 38 Khz
int pinCOM = 7;                         // Pin de modulación señal NEC

void setup()
  {
  Serial.begin(9600);
  pinMode (pinLED, OUTPUT);                       // Pin salida de 38 Khz
  pinMode (pinCOM, OUTPUT);                      // Pin de modulación señal NEC
  digitalWrite (pinCOM, HIGH);                      // Desactiva salida
  TCCR2A = _BV (COM2A0) | _BV(WGM21);   // configura Timer 2
  TCCR2B = _BV (CS20);                              // Sin preescaler
  OCR2A =  209;                                    // compara valor registro A (210 * clock)= 13.125 nS. Frequencia 1/(2*13.125)=38095
  }

void loop()
  {
  dato = -1;
  datoSerie();                          // Entrada de datos serie
  if (dato>-1)                          // Si se ha introducido un dato
    {
    montarSec();                      // Montar secuencia NEC                 
    sendData();                        // Emitir por IR la secuencia NEC     
    dato = -1;                          // Borra dato
    }
  delay(100);
  }

//****** FUNCIONES ******************************************************

void datoSerie()           // Captura dato serie desde el PC
  {
  if (Serial.available()==0) return;
  while (Serial.available()>0)                           // Mientras hay datos serie (max 2 bytes)
    {
    if (dato== -1) dato = (Serial.read()-48);     // Lee primer byte  
    delay(200);
    if (Serial.available()==0) return;                 // Si no hay más bytes, regresa a loop()
    if (Serial.available()>0)                               // Si hay otro dato
      {
      dato = dato * 10;                                     // multiplica x 10              
      dato = dato + (Serial.read()-48);               // lee nuevo y lo suma
      }
    }
  }
//***********************************************************************

void montarSec()          // Monta la secuencia NEC
  {
  for (int f=0; f<=32; f++)
    {
    if (f>-1 && f<=7) secuencia [f] = 0;          // Byte de dirección a 0
    if (f>=8 && f<=15) secuencia [f] = 1;       // Byte inverso de dirección a 1
    if (f>=16 && f<=23)                                // Byte dato   
      {
      if (bitRead(dato, f-16)==0) secuencia [f] = 0;  
      if (bitRead(dato, f-16)==1) secuencia [f] = 1;      
      }

    if (f>=24 && f<=31)                                 // Crea byte inverso de dato   
      {
      if (secuencia [f-8]== 0) secuencia [f] = 1 ;  
      if (secuencia [f-8]== 1) secuencia [f] = 0 ;      
      }
    if (f==32) secuencia [f] = 0;                      // Añade bit de finalización a 0      
    }
  }
//***********************************************************************

void sendData()           // Envía secuencia NEC a través de IR
  {
  digitalWrite(pinCOM, LOW);                      // activa marca 
  delayMicroseconds(9000);                        // marca, 9 mS
  digitalWrite(pinCOM, HIGH);                     // desactiva marca
  delayMicroseconds(4500);                        // espacio, 4,5 mS
  for (int x=0;x<=32;x++)                         // bucle de envio de 32 pulsos
    {
    digitalWrite(pinCOM, LOW);                               // activa marca
    delayMicroseconds(560);                                   // marca 560 uS
    digitalWrite(pinCOM, HIGH);                              // desactiva marca           
    if (secuencia[x]==0) delayMicroseconds(560);    // espacio de 560 uS en caso de ser un 0
    if (secuencia[x]==1) delayMicroseconds(1690);   // espacio de 1690 uS en caso de ser un 1
    }
  }    
//***********************************************************************

El programa se maneja desde el PC, no utiliza librerías IR y tiene tres funciones. La "void datoSerie()" que captura un dato serie enviado desde el PC que debe corresponder al código de botón del mando IR que pulsaríamos.

...Una vez recibido el código del botón desde el PC, la función "void montarSec()" monta la secuencia completa del protocolo NEC correspondiente a este código y la guarda en el array "secuencia[]" en forma de 1 y 0.

...Y la función "void sendData()" lee el array "secuencia[]" y traduce los bits a impulsos con una marca de 560 mSec, y un tiempo variable entre ellos según sea el impulso de inicio (9 ms), o un "0" (560 uSec), o un "1" (1690 uSec). Estos impulsos modulan la portadora de 38 Khz del pin 11 a través del pin 7...

Continuará...

Saludos

Llorens

En este caso también aprovecho para rescatar un programita de Visual Basic que hice hace algunos años, que llamé "Arduino Remote Control" (ARC) y que permite configurar 10 pulsadores con los valores que queramos, y que al pulsar envía a través del puerto COM seleccionado, que en este caso deberá ser el del Arduino...

...El instalador del programa ARC.exe, contenido en un fichero comprimido .rar, se puede bajar desde:

ARC.rar

En XP se instalará sin más, y en W7 lo tenéis que ejecutar como Administrador, porque en caso contrario dará errores y no cargará los OCX necesarios. Supongo que en W8 y W10 también funciona, pero no lo he probado...

Continuará...

Saludos

Llorens

Un último añadido a este tema. Me he metido en mi viejo VBasic 6.0 y he programado el mando de IR concreto para lámpara de Lidl. Con todas sus teclas operativas igual que el mando real. El archivo de instalación para W7-8-10 (que se debe ejecutar como Administrador) se puede bajar en formato .rar desde:

Setup_LED_Lampe_Lidl.rar

El aspecto del programa es el siguiente...

Saludos

Llorens