Recepção e armazenamento de comandos IR

Olá foristas!

Estou extendendo um projeto de TCC com a biblioteca IRremote, que envolve receber e armazenar comandos IR e posteriormente emitir estes comandos.

O circuito e o código fonte eu já tenho (fiz algumas adaptações para simplificar o código). A questão é que não estou conseguindo armazenar os códigos IR. Fazendo o debug no código, notei que no IF da linha 69 (irReceptor.decode(&resultadoIR)) o resultado sempre é FALSE.

Criei algumas rotinas pra testar o RX de IR, que está funcionando certinho. Não sei se devo fazer mais algum teste…

Alguém já passou por algo parecido? Não tenho muita experiência com essa biblioteca (e nem muita experiência com Arduino pra ser sincero…)

Obrigado!

#include "IRremote.h"
#include "EEPROM.h"

int ESPACO_RESERVADO_EEPROM = 500; //Espaço que cada comando ocupará
int COMANDO_1 = 0; //Posição da memória em que ficará salvo
long CODE_EEPROM = 112358; //Se esse número estiver na primeira posição, então tem algo salvo lá

//Deve ser conectado um LED IR no pino 9 para MEGA2560
char PIN_BOTAO_1 = 51; //Botão que envia ou grava o sinal 1
char PIN_IR_RECEPTOR = 5; //Pino que irá ouvir os sinais IR
char PIN_BOTAO_APRENDER = 3; //Se pressionado, indica que o dispositivo está no modo aprendizagem
char PIN_IR_STATUS = 7; //Pino para um LED exibir um status

IRrecv irReceptor(PIN_IR_RECEPTOR);
IRsend irEmissor;
decode_results resultadoIR;

int ultimoBotao1Estado;
int ultimoBotaoAprenderEstado;

struct Comando {
  int id;
  int codeType = -1; // O tipo do código
  unsigned long codeValue; // O código quando não utilizado o códido bruto
  unsigned int rawCodes[RAWBUF]; // As durações do código quando for bruto
  int codeLen; // O tamanho do código
  char toggle = 0; // The RC5/6 toggle state
} comando;

void setup() {
  Serial.begin(9600);
  pinMode(PIN_BOTAO_1, INPUT);
  pinMode(PIN_BOTAO_APRENDER, INPUT);
  pinMode(PIN_IR_STATUS, OUTPUT);

  pinMode(LED_BUILTIN, OUTPUT);

  Serial.println("SISTEMA INICIADO");
}

void loop() {
  int botao1Estado = digitalRead(PIN_BOTAO_1);
  int botaoAprenderEstado = digitalRead(PIN_BOTAO_APRENDER);

//  if (!digitalRead(5)) {
//        digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
//        digitalWrite(PIN_IR_STATUS, HIGH);
//        delay(200);                       // wait for a second
//        digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
//        digitalWrite(PIN_IR_STATUS, LOW);
//        delay(200);  
//  }
  

  if(botaoAprenderEstado == LOW){ //EMITIR COMANDO
    if (ultimoBotaoAprenderEstado == HIGH) {
      Serial.println("Busca por sinal IR finalizada");
    }
    
   if (ultimoBotao1Estado == LOW && botao1Estado == HIGH) {
      Serial.println("Botao 1 foi pressionado");
      validarEnviarComando(COMANDO_1);
    } 
  } else { //APRENDER COMANDO
    if (ultimoBotaoAprenderEstado == LOW) {
      Serial.println("Aguardando sinal IR");
      irReceptor.enableIRIn(); // Ativa o receptor IR
    }
    if (irReceptor.decode(&resultadoIR)) {
      //PISCA O LED SE ENTRAR AQUI!
      if(botao1Estado == HIGH){
        digitalWrite(PIN_IR_STATUS, HIGH);
        delay(500);                       
        digitalWrite(PIN_IR_STATUS, LOW);
        criarComando(&resultadoIR, COMANDO_1, &comando);
      }
      irReceptor.resume(); // reinicia receptor
    }
  }
  ultimoBotao1Estado = botao1Estado;
  ultimoBotaoAprenderEstado = botaoAprenderEstado;
}

//Convert *resultadoIR em um comando
void criarComando(decode_results *resultadoIR, int comandoId, Comando * comando) {    
  digitalWrite(PIN_IR_STATUS, HIGH);
  Serial.print("Salvando o comando ");
  Serial.println(comandoId);
  comando->id = comandoId;
  comando->codeType = resultadoIR->decode_type;
  int count = resultadoIR->rawlen;
  int isRawCode = 0;
  
  if (comando->codeType == UNKNOWN) {
    Serial.println("Received unknown code, saving as raw");
    isRawCode = 1;
  } else if (comando->codeType == NEC) {
    Serial.print("Received NEC: ");
    if (resultadoIR->value == REPEAT) {
      // Don't record a NEC repeat value as that's useless.
      Serial.println("repeat; ignoring.");
      digitalWrite(PIN_IR_STATUS, LOW);
      return;
    }
    Serial.println(resultadoIR->value, HEX);
  } else if (comando->codeType == SONY) {
    Serial.print("Received SONY: ");
  } else if (comando->codeType == PANASONIC) {
    Serial.print("Received PANASONIC: ");
  } else if (comando->codeType == JVC) {
    Serial.print("Received JVC: ");
  } else if (comando->codeType == RC5) {
    Serial.print("Received RC5: ");
  } else if (comando->codeType == RC6) {
    Serial.print("Received RC6: ");
  } else {
    Serial.print("Unexpected codeType ");
    Serial.println(comando->codeType, DEC);
    comando->codeType = UNKNOWN;
    isRawCode = 1;
  }
  
  if(isRawCode == 0) {
    Serial.println(resultadoIR->value, HEX);
    comando->codeValue = resultadoIR->value;
    comando->codeLen = resultadoIR->bits;
  } else {
    comando->codeValue = -1;
    comando->codeLen = resultadoIR->rawlen - 1;
    
    // Para armazenar os dados brutos:
    // Descartar o primeiro valor;
    // converter para microsegundos;
    // Deixa as marcas menores e o espaços maiores para diminuir a distorção do receptor
    for (int i = 1; i <= comando->codeLen; i++) {
      if (i % 2) {
        // Mark
        comando->rawCodes[i - 1] = resultadoIR->rawbuf[i]*USECPERTICK - MARK_EXCESS;
      } 
      else {
        // Space
        comando->rawCodes[i - 1] = resultadoIR->rawbuf[i]*USECPERTICK + MARK_EXCESS;
      }
    }
    printCodigoBruto(comando);
  }
  gravarNoEEPROM(comando);
  digitalWrite(PIN_IR_STATUS, LOW);
}

void printCodigoBruto(Comando *command) {
  /*for (int i = 1; i <= command->codeLen; i++) {
    if (i % 2) {
      Serial.print(" m"); // Mark
    } 
    else {
      Serial.print(" s"); // Space
    }
    Serial.print(command->rawCodes[i - 1], DEC);
  }
  Serial.println("");*/
  Serial.print(command->codeLen);
  Serial.println(" bytes");
}

//Verifica se é possível enviar o comando e envia
void validarEnviarComando(int comandoId) {
  //Verifica se existe comando salvo na memória EEPRON
  if(comandoEstaSalvoEEPROM(comandoId)) {
    //Busca o comando salvo na memória
    carregarComandoEEPROM(comandoId, &comando);
    enviarComando(&comando);
  }
}

//Envia o comando
void enviarComando(Comando *comando) {  
  digitalWrite(PIN_IR_STATUS, HIGH);
  irEmissor.sendRaw(comando->rawCodes, comando->codeLen, 38);
  digitalWrite(PIN_IR_STATUS, LOW);
  delay(50);
}

Olá pessoal!

Ninguém respondeu, mas eu não desisti! :sweat_smile:

Depois de muito pesquisar, achei o AnalysIR. É simplesmente o melhor lugar pra quem precisa lidar com qualquer coisa ligada a emissão e recepção de IR.

Fiz algumas perguntas no fórum deles e baseado em alguns sketches do blog deles, consegui fazer funcionar.

Vou deixar abaixo os códigos, pode ser útil pra alguém. E atentem-se à biblioteca (necessária só pra fazer o envio dos pulsos).

Aprender o comando (ainda não armazena, só escreve na serial):

/*
Author: AnalysIR
Revision: 1.0

This code is provided to overcome an issue with Arduino IR libraries
It allows you to capture raw timings for signals longer than 255 marks & spaces.
Typical use case is for long Air conditioner signals.

You can use the output to plug back into IRremote, to resend the signal.

This Software was written by AnalysIR.

Usage: Free to use, subject to conditions posted on blog below.
Please credit AnalysIR and provide a link to our website/blog, where possible.

Copyright AnalysIR 2014

Please refer to the blog posting for conditions associated with use.
http://www.analysir.com/blog/2014/03/19/air-conditioners-problems-recording-long-infrared-remote-control-signals-arduino/

Connections:
IR Receiver      Arduino
V+          ->  +5v
GND          ->  GND
Signal Out   ->  Digital Pin 2
(If using a 3V Arduino, you may connect V+ to +3V)
*/

#include <IRremote.h>

#define LEDPIN 13
//you may increase this value on Arduinos with greater than 2k SRAM
#define maxLen 800

volatile  unsigned int irBuffer[maxLen]; //stores timings - volatile because changed by ISR
volatile unsigned int x = 0; //Pointer thru irBuffer - volatile because changed by ISR



void setup() {
  Serial.begin(115200); //change BAUD rate as required
  attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);//set up ISR for receiving IR signal
  pinMode(LEDPIN, OUTPUT);
  digitalWrite(LEDPIN, LOW);
}

void loop() {
  // put your main code here, to run repeatedly:

  Serial.println(F("Press the button on the remote now - once only"));
  delay(5000); // pause 5 secs
  if (x) { //if a signal is captured
    digitalWrite(LEDPIN, HIGH);//visual indicator that signal received
    Serial.println();
    Serial.print(F("Raw: (")); //dump raw header format - for library
    Serial.print((x - 1));
    Serial.print(F(") "));
    detachInterrupt(0);//stop interrupts & capture until finshed here
    for (int i = 1; i < x; i++) { //now dump the times
      if (!(i & 0x1)) Serial.print(F("-"));
      Serial.print(irBuffer[i] - irBuffer[i - 1]);
      Serial.print(F(", "));
    }
    x = 0;
    Serial.println();
    Serial.println();
    digitalWrite(LEDPIN, LOW);//end of visual indicator, for this time
    attachInterrupt(0, rxIR_Interrupt_Handler, CHANGE);//re-enable ISR for receiving IR signal
  }

}

void rxIR_Interrupt_Handler() {
  if (x > maxLen) return; //ignore if irBuffer is already full
  irBuffer[x++] = micros(); //just continually record the time-stamp of signal transitions

}

Enviar o comando (é necessário tirar os sinais negativos nos arrays com os sinais):

#include <IRremote.h>

//SPRINGER AC ON SIGNAL
//const unsigned int AC_irSignal1[] PROGMEM = {9032, 4476, 580, 572, 556, 600, 556, 576, 576, 1648, 560, 1648, 580, 572, 556, 600, 556, 596, 556, 576, 576, 576, 556, 600, 552, 1648, 556, 600, 556, 600, 556, 572, 552, 600, 556, 600, 556, 596, 556, 1648, 556, 600, 556, 572, 580, 576, 556, 596, 556, 576, 580, 572, 576, 576, 560, 596, 556, 572, 580, 1648, 560, 572, 580, 1648, 556, 1672, 560, 572, 580, 572, 556, 600, 556, 596, 556, 576, 552, 600, 556, 576, 576, 576, 580, 572, 556, 1652, 580, 572, 552, 604, 552, 1652, 580, 1648, 556, 596, 556, 576, 576, 10100, 8980, 4504, 556, 1672, 560, 572, 580, 576, 552, 600, 556, 576, 576, 576, 552, 1652, 580, 576, 576, 1648, 560, 1648, 580, 1648, 556, 596, 560, 572, 580, 1648, 556, 600, 556, 576, 576, 576, 552, 600, 556, 576, 576, 1648, 556, 600, 556, 576, 576, 576, 556, 600, 552, 600, 556, 572, 556, 600, 556, 596, 556, 576, 552, 600, 556, 596, 556, 576, 580, 572, 556, 600, 556, 576, 576, 1648, 556, 1648, 584, 572, 552, 604, 552, 600, 556, 576, 576, 576, 552, 600, 556, 576, 580, 572, 552, 604, 552, 580, 576, 572, 556};

//CONSUL AC ON SIGNAL
const unsigned int AC_irSignal1[] PROGMEM = {8980, 4520, 584, 1668, 584, 1668, 584, 548, 584, 556, 580, 560, 580, 564, 580, 564, 584, 1676, 584, 544, 584, 1668, 584, 1672, 584, 552, 584, 556, 584, 564, 580, 564, 584, 556, 584, 544, 580, 548, 592, 1664, 584, 556, 584, 556, 584, 560, 584, 564, 584, 556, 584, 544, 584, 1668, 584, 552, 584, 576, 560, 584, 556, 1684, 584, 1688, 584, 580, 556, 568, 560, 572, 556, 580, 556, 580, 556, 584, 556, 588, 532, 616, 556, 584, 556, 568, 536, 596, 532, 604, 556, 580, 556, 584, 536, 608, 536, 612, 536, 584, 536, 8024, 556, 572, 556, 572, 556, 580, 556, 580, 532, 608, 556, 588, 556, 592, 532, 1704, 556, 596, 556, 572, 532, 604, 532, 604, 556, 584, 560, 584, 560, 592, 556, 580, 532, 592, 556, 576, 556, 576, 536, 604, 532, 608, 532, 612, 532, 616, 556, 580, 556, 572, 556, 576, 556, 576, 532, 604, 556, 588, 556, 588, 532, 612, 532, 608, 556, 568, 556, 576, 556, 576, 556, 580, 536, 604, 532, 616, 552, 592, 556, 584, 552, 572, 532, 600, 552, 580, 556, 580, 556, 584, 560, 588, 556, 588, 556, 584, 532, 592, 532, 600, 532, 600, 556, 580, 532, 608, 556, 588, 556, 592, 556, 580, 556, 568, 556, 1672, 580, 1676, 556, 608, 556, 584, 556, 1684, 556, 1716, 580, 1660, 556, 7996, 580, 572, 532, 596, 532, 604, 552, 584, 556, 584, 532, 612, 556, 588, 532, 608, 532, 1692, 556, 596, 532, 604, 556, 580, 532, 608, 532, 612, 532, 616, 532, 608, 556, 568, 556, 576, 556, 580, 556, 580, 556, 584, 532, 612, 556, 592, 532, 608, 556, 568, 536, 596, 560, 576, 556, 580, 556, 584, 532, 612, 556, 592, 532, 608, 532, 596, 556, 572, 556, 580, 532, 604, 556, 1684, 556, 1708, 556, 616, 556, 584, 532, 592, 532, 600, 532, 600, 556, 584, 532, 608, 556, 588, 532, 616, 532, 604, 532, 1692, 556, 600, 556, 576, 556, 580, 556, 1688, 556, 1708, 560, 612, 536, 584, 560};

//PHILIPS TV
//const unsigned int AC_irSignal1[] PROGMEM = {2628, 912, 416, 912, 420, 496, 392, 496, 392, 900, 860, 504, 392, 496, 392, 496, 392, 496, 392, 496, 392, 496, 392, 496, 392, 496, 392, 496, 392, 496, 392, 496, 392, 496, 836, 496, 392, 912, 420, 496, 392, 17524, 2632, 908, 416, 916, 416, 496, 392, 500, 388, 900, 864, 500, 392, 476, 412, 472, 416, 472, 416, 472, 416, 472, 416, 472, 416, 472, 416, 472, 416, 496, 392, 496, 392, 496, 836, 496, 416, 892, 440, 472, 412};

IRsend irsend;

char PIN_BUTTON = 51;

void sendRAW_Flash(const unsigned int * signalArray, unsigned int signalLength, unsigned char carrierFreq) {
  irsend.enableIROut(carrierFreq); //Inicia a freq. da portadora para cada sinal a ser enviado

  for(unsigned int i = 0; i < signalLength; i++) {
    if (i & 1) {
      irsend.space(pgm_read_word_near(&signalArray[i]));
    } else {
      irsend.mark(pgm_read_word_near(&signalArray[i]));
    }  
  }
  irsend.space(1);
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(PIN_BUTTON, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned int botao1 = digitalRead(PIN_BUTTON);
  if (botao1) {
      Serial.print("Starting IR transmission: ");
      Serial.println(micros());
      sendRAW_Flash(AC_irSignal1, sizeof(AC_irSignal1), 38);
      Serial.print("End of IR transmission: ");
      Serial.println(micros());
      delay(500);
  }
}

É interessante usar essas rotinas, porque pra sinais como os de ar-condicionados, a quantidade de pulsos é gigantesca (aquele sinal ali do ar Consul, tem quase 400 pulsos!), e as bibliotecas mais usadas só funcionam com os protocolos mais conhecidos (controle de TV, DVD, etc.).