[Tutorial] Haz un control remoto con Altair


#1

#Tutorial Control Remoto

Este tutorial es algo complejo, no obstante se tratará hacerlo lo más fácil de entender para una gama amplia de programadores.
Antes de avanzar, ten en cuenta que Necesitarás al menos un Altair
##Teoría
Primero que nada debemos entender que la luz infrarroja es simplemente luz con un color específico fuera del espectro que nosotros humanos podemos percibir a simple vista.

De hecho, la longitud de onda de la luz infrarroja va desde los 750 nanómetros (nm), que es un poco más allá de donde nosotros ya no vemos. Longitudes de onda más grandes también son distintas cosas; las ondas microondas, por ejemplo.

Como un pequeño experimento para ver la luz infrarroja de un control remoto, podrías simplemente usar alguna cámara que tengas a la mano, y apuntar al emisor del control remoto mientras presionas algún botón.

Se debería ver algo así

El problema es que todo lo que nos rodea emite luz infrarroja, de distinta longitud de onda pero sin dejar de ser infrarroja (por eso, al ver a través de una cámara no se ve todo brillando, las cámaras sólo perciben hasta cierta longitud de onda igual que nosotros, sin embargo nos permiten ver la luz emitida por un control remoto). Teniendo esto en mente, se necesita un método para poder enviar información usando luz infrarroja y omitir toda la luz infrarroja “basura” del medio ambiente.

Algo similar a la clave morse fue desarrollado para resolver este problema.
Modulación es de hecho el nombre que lleva y, básicamente es hacer parpadear el led infrarrojo en a ~35kHz (aunque varía según el fabricante aparato).

De hecho al enviar un comando, este sale en forma de destellos con espacios entre ellos (medidos en microsegundos), para no “perder la atención” del receptor. El receptor, por su cuenta, sólo mide el tiempo total que duró ese bloque. Entre bloque y bloque el espacio de tiempo es mayor, pero al estar hablando en microsegundos, la demora para nosotros es prácticamente nula…

Para que quede más claro, échenle un vistazo al siguiente gif.

Tengan en cuenta que para un receptor infrarrojo (derecha) siempre está recibiendo “1”, mientras el emisor (izquierda) no esté enviando nada.


##Práctica
Ya quedó explicada la parte teórica, ahora falta poner todo el conocimiento sobre la mesa y trabajar con él.

Antes de hacer un control con el Altair que envíe comandos, lo primero será aprender a leer la información emitida por algún control remoto; de un televisor, por ejemplo.

Para calentar motores, me gustaría que hagan este pequeño test.
(En vez de las baterías podrían usar directamente el Altair, conectando los cables de alimentación a 3.3v y a GND)


Lo que sucederá es que el LED parpadeará cada que el Receptor IR reciba algo viniendo del control.

Ahora sí, en este código harán que el Altair lea lo que un control transmita.

//Como aqui el timing es muy importante, no se usara digitalRead
//ya que es lento comparado con lo que se usará.


//Revisar link de referencia después del código; en el tutorial.
//Significa que del pin 0 al 7 serán de SÓLO LECTURA
#define IRpin_PIN PIND
#define IRpin 2
 
//El pulso más largo por el que se va a leer será de 65 ms (cuando se aplique en el código, será en una función de microsegundos
//Así que no será tomado como 65000ms (siendo ésto 65 segundos); de hecho 65 ms es mucho en éste ámbito.
#define MAXPULSE 65000
 
//Nuestra resolución de tiempo, entre más grande es el valor mejor,
//ya que es más preciso, pero toma más tiempo procesarlo y
//Se comienza a perder la precisión que se ganó por no usar digitalRead
#define RESOLUTION 20
 
//El pulso que se recibirá será en pares, 100 pares en éste ámbito es muchísimo
uint16_t pulses[100][2]; // Recuerda que un "valor" consiste en un ON y OFF del LED, así que en la matriz se almacenan en pares. 
uint8_t currentpulse = 0; // Se usará para saber cuántos pares de ON y OFF se recibieron.
 
void setup(void)
{
  Serial.begin(9600); //Se usará más adelante para poder imprimir en el monitor serial lo que el Altair lea.
  Serial.println("Ready to decode IR!"); //Pasado este mensaje sabremos que acaba de terminar el SETUP y comienza a leer pulsos.
}

 
void loop(void)
{
  uint16_t highpulse, lowpulse; // En estas variables se almacenará el pulso temporalmente.
  highpulse = lowpulse = 0; //Se va a separar en dos pasos este proceso, cuando el LED esté en HIGH; HighPulse y los de LOW;LowPulse
  
  //Todo este while es para cuando el pulso está en High.
  
  //  --- Explicación Compleja --- //
  //Tomando lo que esté en los pines 0-7 se le aplica una "máscara" y se toma sólo lo que está en la posición 00000100
  //Si se acomodan los pines de esta manera-> 76543210 podemos ver de manera más clara que el 00000100 está en la antepenúltima posición, igual que en (xxxxx2xx)
  //El operador '&' genera una tabla de verdad entre 00000100 y lo que esté conectado en los pines (el microcontrolador ignorará todo lo que no esté en 2; en otras palabras)
  //En resumen, si hay algo conectado en el PIN 2, esto va a funcionar.
  while (IRpin_PIN & (1 << IRpin)) 
  {
    highpulse++;
    delayMicroseconds(RESOLUTION);
 
    // Si el pulso es demasiado largo, este if suspende el escaneo
    // Anota todo lo leído y reinica contadores.
    if ((highpulse >= MAXPULSE) && (currentpulse != 0))
    {
      printpulses();
      currentpulse=0;
      return;
    }
  }
  // Si nunca se entró al if, entonces se anota el nuevo valor alto en la matriz.
  pulses[currentpulse][0] = highpulse;
  
  //---------------------------------------------
  
  // Se repite el proceso para la segunda parte del pulso (recuerda que un pulso consiste en un HIGH y LOW juntos)
  //Esto es lo mismo que en el otro WHILE, sólo que con otra notación, donde _BV es BITVALUE. Revisa los links de asistencia.
  //Y este va a contar cada que el receptor no reciba nada.
  while (! (IRpin_PIN & _BV(IRpin)))
  {
    lowpulse++;
    delayMicroseconds(RESOLUTION);
    
    // Si el pulso es demasiado largo, este if suspende el escaneo
    // Anota todo lo leído y reinica contadores.
    if ((lowpulse >= MAXPULSE) && (currentpulse != 0))
    {
      printpulses();
      currentpulse=0;
      return;
    }
  }
  pulses[currentpulse][1] = lowpulse;
 
  // Acaba de leerse un ON-OFF del pulso, se repetirá el proceso hasta que algún comando exceda la longitud de un ON o un OFF (y entonces entre al IF)
  currentpulse++;
}


//Lo único que hace esta función es imprimir todo lo recibido después de que ya terminó el pulso (en microsegundos; *miu*seconds)
void printpulses(void)
{
  Serial.println("\n\r\n\rReceived: \n\rOFF   \tON");
  for (uint8_t i = 0; i < currentpulse; i++)
  {
    Serial.print(pulses[i][0] * RESOLUTION, DEC); //DEC <- En caso de que no esté en decimales, esto lo obliga a pasarlo a decimales.
    Serial.print(" usec, ");
    Serial.print(pulses[i][1] * RESOLUTION, DEC);
    Serial.println(" usec");
  }
  // Y después lo escribe en milisegundos.
  Serial.println("\nTraducido a ms");
  Serial.println("ON, OFF");
  for (uint8_t i = 0; i < currentpulse-1; i++)
  {
    Serial.print(pulses[i][1] * RESOLUTION / 10, DEC);
    Serial.print(", ");
    Serial.print(pulses[i+1][0] * RESOLUTION / 10, DEC);
    Serial.println(",");
  }
  Serial.print(pulses[currentpulse-1][1] * RESOLUTION / 10, DEC);
  Serial.print(", 0};");
}

Links de asistencia:
http://www.arduino.cc/en/pmwiki.php?n=Reference/PortManipulation (PIND)
http://dubworks.blogspot.mx/2012/07/introduction-to-c-language-bitwise_21.html (Explicación BV(2))
http://es.wikipedia.org/wiki/Máscara
(inform%C3%A1tica) (Máscara de Bits)

Aquí les muestro cómo conectar el Altair para que funcione como receptor. Siempre revisen el DATASHEET del Receptor (eviten quemarlo, como me pasó a mí en la realización del tutorial)


Ahora, se llevará a cabo la explicación de cómo hacer el receptor también emita. La conexión del Altair, componentes con protoboard se mostrará después del código. Asimismo, los links de asistencia estarán inmediatamente después del código, no obstante el código está comentado de la mejor manera posible. Cualquier duda con alguna parte del código que no entiendas, no dudes en preguntar como una respuesta en el código, nadie nace sabiendo, sobretodo esto que es bastante complejo si no estás familiarizado con la programación en bajo nivel.

El siguiente código tiene una o más modificaciones del primer código, se recomienda tenerlo como un archivo distinto.

//Como aqui el timing es muy importante, no se usara digitalRead
//ya que es lento comparado con lo que se usará.

//Revisar link de referencia después del código; en el tutorial.
//Significa que del pin 0 al 7 serán de SÓLO LECTURA
#define IRpin_PIN PIND

#define MASK 2 //Se tomará como número binario (00000010)
#define Boton 33 //Boton interno del Altair (Tiene lógica inversa! LOW = Está presionado, HIGH = No está presionado)
#define Boton2 10 //Boton externo, lógica normal! HIGH = Presionado, LOW = No presionado!
#define IR 9
//El pulso más largo por el que se va a leer será de 65 ms (cuando se aplique en el código, será en una función de microsegundos
//Así que no será tomado como 65000ms (siendo ésto 65 segundos); de hecho 65 ms es mucho en éste ámbito.
#define MAXPULSE 65000

//Nuestra resolución de tiempo, entre más grande es el valor mejor,
//ya que es más preciso, pero toma más tiempo enviarlo. Podría ser inestable incrementarlo. (Valor predeeterminado = 20)
#define RESOLUTION 20

//El pulso que se recibirá será en pares, 100 pares en éste ámbito es muchísimo
uint16_t pulses[120][2]; // Recuerda que un "valor" consiste en un ON y OFF del LED, así que en la matriz se almacenan en pares.
uint8_t currentpulse = 0; // Se usará para saber cuántos pares de ON y OFF se recibieron.
bool full = false;



void setup()
{
  Serial.begin(9600);
  pinMode(Boton, INPUT);
  pinMode(Boton2, INPUT);
  pinMode(IR, OUTPUT);
  pinMode(15, OUTPUT);
  pinMode(14, OUTPUT);

//           7        6       5        4       3         2       1      0
//TCCR2A - [COM2A1, COM2A0, COM2B1, COM2B0, reserved, reserved, WGM21, WGM20]
//           7        6       5        4       3       2    1     0
//TCCR2B - [FOC2A, FOC2B, reserved, reserved, WGM22, CS22, CS21, CS20]


  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); // Se le aplica un 'or' a todos y luce así -> TCCR2A = 0110 0011  (99)
  TCCR2B = _BV(WGM22) | _BV(CS21); // Se le aplica un 'or' y queda así -> TCCR2B= 0000 1010 = 10
  OCR2A = 25; //Timer interno llegará hasta 25 (dividido entre la freq del micro (16MHz) y multiplicado por dos (pues la división sólo mostrará la mitad de la onda, queremos saber la duración de un ciclo completo.
  TCCR2A ^= _BV(COM2A1); // Prepara el LED IR

  Serial.println("Listo para empezar!"); //Pasado este mensaje sabremos que acaba de terminar el SETUP y comienza a leer pulsos.
}




void IRcarrier (unsigned int matrix)
{
  if (matrix != 0)
  {
    TCCR2A ^= _BV(COM2A1); //Cambia el bit 8. Enciende el LED
    delayMicroseconds(matrix); //Espera matrix-microsegundos con el LED encendido
    TCCR2A ^= _BV(COM2A1); //Apaga el IR
  }
}


void Enviar(uint16_t pulse[120][2])
{
  digitalWrite(15, LOW);
  for (int i = 0; i < 120; i++)
  {
    if (pulse[i][0] == 0) //Si un slot de la matriz está vacío, se sale del bucle.
    {
      break;
    }
    delayMicroseconds(pulse[i][0]); //Espera n microsegundos con el IR apagado
    IRcarrier(pulse[i][1]); //Entra a la función mandando el delay que toma el ON del IR
  }
  digitalWrite(15, HIGH);
}

//Lo único que hace esta función es imprimir todo lo recibido después de que ya terminó el pulso (en microsegundos; *miu*seconds)
void printpulses()
{
  Serial.println("\n\r\n\rReceived: \n\rOFF\t|\tON");
  Serial.print("{");
  for (uint8_t i = 0; i < currentpulse; i++)
  {
    Serial.print("{");
    Serial.print(pulses[i][0], DEC); //DEC <- En caso de que no esté en decimales, esto lo obliga a pasarlo a decimales.
    Serial.print(",");
    Serial.print(pulses[i][1], DEC);
    Serial.print("}");
  }
  Serial.print("};\n");
  full = true;
}



void loop()
{
  digitalWrite(15, HIGH); //LEDs internos del Altair, usa lógica inversa
  digitalWrite(14, HIGH); //Los dos inician apagados!


  if (digitalRead(Boton2) == HIGH || (currentpulse != 0))
  {
    if (digitalRead(Boton2) == HIGH)
    {
      Serial.println("Leyendo!");
      digitalWrite(14, LOW); //LED VERDE ENCENDIDO
      full = false;
    }

    uint16_t highpulse = 0, lowpulse = 0; // En estas variables se almacenará el pulso temporalmente. Se va a separar en dos pasos este proceso, cuando el LED esté en HIGH; HighPulse y los de LOW;LowPulse
    
  //Todo este while es para cuando el pulso está en High.
  //  --- Explicación Compleja --- //
  //Tomando lo que esté en los pines 0-7 se le aplica una "máscara" y se toma sólo lo que está en la posición 00000100
  //Si se acomodan los pines de esta manera-> 76543210 podemos ver de manera más clara que el 00000100 está en la antepenúltima posición, igual que en (xxxxx2xx)
  //El operador '&' genera una tabla de verdad entre 00000100 y lo que esté conectado en los pines (el microcontrolador ignorará todo lo que no esté en 2; en otras palabras)
  //En resumen, si hay algo conectado en el PIN 2, esto va a funcionar.
  while (IRpin_PIN & (1 << MASK) && full == false)
  {
    highpulse++;
    delayMicroseconds(RESOLUTION);

    // Si el pulso es demasiado largo, este if suspende el escaneo
    // Anota todo lo leído y reinica contadores.
    if ((highpulse >= MAXPULSE) && (currentpulse != 0))
    {
      printpulses();
      currentpulse = 0;
      return;
    }
  }
  // Si nunca se entró al if, entonces se anota el nuevo valor alto en la matriz.
  pulses[currentpulse][0] = highpulse * RESOLUTION;

  //---------------------------------------------

  // Se repite el proceso para la segunda parte del pulso (recuerda que un pulso consiste en un HIGH y LOW juntos)
  //Esto es lo mismo que en el otro WHILE, sólo que con otra notación, donde _BV es BITVALUE. Revisa los links de asistencia!
  //Y este va a contar cada que el receptor no reciba nada.
  while (! ( IRpin_PIN & _BV(MASK) ) && full == false)
  {
    lowpulse++;
    delayMicroseconds(RESOLUTION);
    if ((lowpulse >= MAXPULSE) && (currentpulse != 0))
    {
      printpulses();
      currentpulse = 0;
      return;
    }
  }

  pulses[currentpulse][1] = lowpulse * RESOLUTION;
  // Acaba de leerse un ON-OFF del pulso, se repetirá el proceso hasta que algún comando exceda la longitud de un ON o un OFF (y entonces entre al IF)
  currentpulse++;
  
  
  }


  if (digitalRead(Boton) == LOW && full == true)
  {
    Enviar(pulses);
    delay(500);
  }
  digitalWrite(14,HIGH);
}

Links de asistencia
http://www.fiz-ix.com/2012/01/how-to-configure-arduino-timer-2-registers-to-drive-an-ultrasonic-transducer-with-a-square-wave/ (Timer Internos)
http://arduino.cc/en/pmwiki.php?n=Tutorial/SecretsOfArduinoPWM (PWM)

##Diagrama de conexión

Teniendo esto implementado, podría simplemente copiarse el código recibido en Monitor Serial, pegarlo como una nueva matriz en el código. Luego, añadiendo las funciones de acción de Aquila para así llamar matrices en particular y tener un control remoto más completo a través de la plataforma Aquila (la ventaja de esto es que no tendrían que comprarse más push buttons y que de esta forma se puede automatizar como ahora mencionaré).

Ve esto como una “simple” aplicación más para el Altair, imagina que todo esto lo configures para que a N horas del día tu televisor se encienda y comience a grabar tu programa favorito.


Ahora, vamos a añadir unas cuantas líneas al código para implementar toda la plataforma de Aquila. En el código incluyo una matriz con muchos números; esos son lo que envía el control que usé al televisor que usé (que de hecho extraje directamente del monitor Serial). Tú deberás cambiar esos valores por los que te devuelva el monitor serial.
No obstante, lo dejé ahí para que veas cómo es la estructura de una matriz.

//Como aqui el timing es muy importante, no se usara digitalRead
//ya que es lento comparado con lo que se usará.

//Revisar link de referencia después del código; en el tutorial.
//Significa que del pin 0 al 7 serán de SÓLO LECTURA
#define IRpin_PIN PIND

#include <Wire.h>
#include <Mesh.h>
#include <AquilaProtocol.h>

#define MASK 2 //Se tomará como número binario (00000010)
#define Boton 33 //Boton interno del Altair (Tiene lógica inversa! LOW = Está presionado, HIGH = No está presionado)
#define Boton2 10 //Boton externo, lógica normal! HIGH = Presionado, LOW = No presionado!
#define IR 9
//El pulso más largo por el que se va a leer será de 65 ms (cuando se aplique en el código, será en una función de microsegundos
//Así que no será tomado como 65000ms (siendo ésto 65 segundos); de hecho 65 ms es mucho en éste ámbito.
#define MAXPULSE 65000

//Nuestra resolución de tiempo, entre más grande es el valor mejor,
//ya que es más preciso, pero toma más tiempo enviarlo. Podría ser inestable incrementarlo. (Valor predeeterminado = 20)
#define RESOLUTION 20

//El pulso que se recibirá será en pares, 100 pares en éste ámbito es muchísimo
uint16_t pulses[120][2]; // Recuerda que un "valor" consiste en un ON y OFF del LED, así que en la matriz se almacenan en pares.
uint8_t currentpulse = 0; // Se usará para saber cuántos pares de ON y OFF se recibieron.
bool full = false;


  uint16_t turnON [120][2]={{46264,1100},{540,1160},{480,1960},{540,1080},{520,1120},{520,1120},{520,1100},{540,1080},{560,1060},{1420,1040},{600,1800},{720,920},{20324,980},{640,980},{660,1780},{740,880},{760,860},{760,880},{760,880},{760,880},{760,860},{1600,900},{760,1700},{760,880},{20384,940},{700,920},{700,1740},{760,880},{760,860},{780,860},{760,880},{760,900},{720,900},{1600,880},{740,1740},{760,860}};
  //Este es un ejemplo de como luce el codigo de encendido de un televisor, sera distinto al tuyo, asi que deberas modificar las matrices en las funciones siguientes (Y en esta tambien) :)


  uint16_t vUP[120][2]={0};
  uint16_t vDWN[120][2]={0};
  uint16_t cUP[120][2]={0};
  uint16_t cDWN[120][2]={0};
  uint16_t inp[120][2]={0};     

bool encender (uint8_t param, bool gotParam)
{
  Enviar (turnON);
  delay(500);
}

bool volumenUP (uint8_t param, bool gotParam)
{
  Enviar(vUP);
  delay(500);
}

bool volumenDWN (uint8_t param, bool gotParam)
{
  Enviar(vDWN);
  delay(500);
}

bool canalUP (uint8_t param, bool gotParam)
{
  Enviar(cUP);
  delay(500);
}

bool canalDWN (uint8_t param, bool gotParam)
{ 
  Enviar(cDWN);
  delay(500);
}

bool input (uint8_t param, bool gotParam)
{
  Enviar(inp);
  delay(500);
}


void setup()
{
  Mesh.begin();
  Aquila.begin();
  Aquila.setClass("mx.makerlab.test");
  Aquila.setName("Control");
  
  Aquila.addAction("Encender", encender);
  Aquila.addAction("Volumen +", volumenUP);
  Aquila.addAction("Volumen -", volumenDWN);
  Aquila.addAction("Canal +", canalUP);
  Aquila.addAction("Canal -", canalDWN);
  Aquila.addAction("Input", input);
  
  Mesh.announce(HUB);
  
  Serial.begin(9600);
  pinMode(Boton, INPUT);
  pinMode(Boton2, INPUT);
//    pinMode(Boton2, INPUT_PULLUP);
  pinMode(IR, OUTPUT);
  pinMode(15, OUTPUT);
  pinMode(14, OUTPUT);

//           7        6       5        4       3         2       1      0
//TCCR2A - [COM2A1, COM2A0, COM2B1, COM2B0, reserved, reserved, WGM21, WGM20]
//           7        6       5        4       3       2    1     0
//TCCR2B - [FOC2A, FOC2B, reserved, reserved, WGM22, CS22, CS21, CS20]


  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); // Se le aplica un 'or' a todos y luce así -> TCCR2A = 0110 0011  (99)
  TCCR2B = _BV(WGM22) | _BV(CS21); // Se le aplica un 'or' y queda así -> TCCR2B= 0000 1010 = 10
  OCR2A = 25; //Timer interno llegará hasta 25 (dividido entre la freq del micro (16MHz) y multiplicado por dos (pues la división sólo mostrará la mitad de la onda, queremos saber la duración de un ciclo completo.
  TCCR2A ^= _BV(COM2A1); // Prepara el LED IR

  Serial.println("Listo para empezar!"); //Pasado este mensaje sabremos que acaba de terminar el SETUP y comienza a leer pulsos.
}




void IRcarrier (unsigned int matrix)
{
  if (matrix != 0)
  {
    TCCR2A ^= _BV(COM2A1); //Cambia el bit 8. Enciende el LED
    delayMicroseconds(matrix); //Espera matrix-microsegundos con el LED encendido
    TCCR2A ^= _BV(COM2A1); //Apaga el IR
  }
}


void Enviar(uint16_t pulse[120][2])
{
  digitalWrite(15, LOW);
  for (int i = 0; i < 120; i++)
  {
    if (pulse[i][0] == 0) //Si un slot de la matriz está vacío, se sale del bucle.
    {
      break;
    }
    delayMicroseconds(pulse[i][0]); //Espera n microsegundos con el IR apagado
    IRcarrier(pulse[i][1]); //Entra a la función mandando el delay que toma el ON del IR
  }
  digitalWrite(15, HIGH);
}

//Lo único que hace esta función es imprimir todo lo recibido después de que ya terminó el pulso (en microsegundos; *miu*seconds)
void printpulses()
{
  Serial.println("\n\r\n\rReceived: \n\rOFF\t|\tON");
  Serial.print("{");
  for (uint8_t i = 0; i < currentpulse; i++)
  {
    Serial.print("{");
    Serial.print(pulses[i][0], DEC); //DEC <- En caso de que no esté en decimales, esto lo obliga a pasarlo a decimales.
    Serial.print(",");
    Serial.print(pulses[i][1], DEC);
    Serial.print("},");
  }
  Serial.print("};\n");
  full = true;
}



void loop()
{
  Mesh.loop();
  Aquila.loop();  
  
  digitalWrite(15, HIGH); //LEDs internos del Altair, usa lógica inversa
  digitalWrite(14, HIGH); //Los dos inician apagados!


  if (digitalRead(Boton2) == HIGH || (currentpulse != 0))
  {
    if (digitalRead(Boton2) == HIGH)
    {
      Serial.println("Leyendo!");
      digitalWrite(14, LOW); //LED VERDE ENCENDIDO
      full = false;
    }

    uint16_t highpulse = 0, lowpulse = 0; // En estas variables se almacenará el pulso temporalmente. Se va a separar en dos pasos este proceso, cuando el LED esté en HIGH; HighPulse y los de LOW;LowPulse
    
  //Todo este while es para cuando el pulso está en High.
  //  --- Explicación Compleja --- //
  //Tomando lo que esté en los pines 0-7 se le aplica una "máscara" y se toma sólo lo que está en la posición 00000100
  //Si se acomodan los pines de esta manera-> 76543210 podemos ver de manera más clara que el 00000100 está en la antepenúltima posición, igual que en (xxxxx2xx)
  //El operador '&' genera una tabla de verdad entre 00000100 y lo que esté conectado en los pines (el microcontrolador ignorará todo lo que no esté en 2; en otras palabras)
  //En resumen, si hay algo conectado en el PIN 2, esto va a funcionar.
  while (IRpin_PIN & (1 << MASK) && full == false)
  {
    highpulse++;
    delayMicroseconds(RESOLUTION);

    // Si el pulso es demasiado largo, este if suspende el escaneo
    // Anota todo lo leído y reinica contadores.
    if ((highpulse >= MAXPULSE) && (currentpulse != 0))
    {
      printpulses();
      currentpulse = 0;
      return;
    }
  }
  // Si nunca se entró al if, entonces se anota el nuevo valor alto en la matriz.
  pulses[currentpulse][0] = highpulse * RESOLUTION;

  //---------------------------------------------

  // Se repite el proceso para la segunda parte del pulso (recuerda que un pulso consiste en un HIGH y LOW juntos)
  //Esto es lo mismo que en el otro WHILE, sólo que con otra notación, donde _BV es BITVALUE. Revisa los links de asistencia!
  //Y este va a contar cada que el receptor no reciba nada.
  while (! ( IRpin_PIN & _BV(MASK) ) && full == false)
  {
    lowpulse++;
    delayMicroseconds(RESOLUTION);
    if ((lowpulse >= MAXPULSE) && (currentpulse != 0))
    {
      printpulses();
      currentpulse = 0;
      return;
    }
  }

  pulses[currentpulse][1] = lowpulse * RESOLUTION;
  // Acaba de leerse un ON-OFF del pulso, se repetirá el proceso hasta que algún comando exceda la longitud de un ON o un OFF (y entonces entre al IF)
  currentpulse++;
  
  
  }


  if (digitalRead(Boton) == LOW && full == true)
  {
    Enviar(pulses);
    delay(500);
  }
  digitalWrite(14,HIGH);
}

Si tiene dudas al respecto no duden e comentar.


Recursos para Aquila (Documentación, código, etc.)
#2

Genial, con esto puedo hacer que se enciendan distintas teles en la casa cuando salgamos de la ciudad por varios días.
Gracias!