RC6 IR protocol decoded

As my first code share, I have created a program that will decode the RC6 IR protocol (mode 6 or 32 bit version). I made it to decode the signal from my Media Center Remote Control but it could be easily adapted to read any RC6 (others are mode 0 or 16bit) remote. I borrowed heavily from the IR analyzer program in the playground but the decoding process was all me. Because Im no pro programmer, the code is very straightforward and should be easy for even a noob to understand.

Unfortunately its not perfect, some buttons stall the program while others give an infinite loop. The ones #defined work perfectly tho. So needles to say, if anyone sees an obvious problem give me a shout.

Reference sites stated in the code header

//RC6 IR decoder
//Written by Miles Moody
//Set up for mode 6 (32bit) can be changed for mode 0(16bit)
//Based on Walter Anderson's IRanalyzer.pde
//For more info on RC6 protocol see:
//http://www.sbprojects.com/knowledge/ir/rc6.htm
//http://www.lompec.nl/avusirx.html

#include <avr/interrupt.h>
#include <avr/io.h>

#define TIMER_RESET  TCNT1 = 0
#define SAMPLE_SIZE  68      //#of pin changes up to 255
#define headerlower 2500
#define headerupper 2900
#define singlelower 300
#define singleupper 575
#define doublelower 725
#define doubleupper 1050
#define triplelower 1250
#define tripleupper 1500
#define togglebit 16

#define OK       1058
#define LEFT     1056
#define UP       1054
#define PLAY     1046
#define FF       1044
#define PAUSE    1048
#define MUTE     1038
#define SKIPF    1050
#define CHUP     1042
#define VOLUP    1040
#define EJECT    1060
#define LISTING  1062
#define RECTV    1096
#define CLEAR    1034

int TXpin=7;
int RXpin = 2;
unsigned int TimerValue[SAMPLE_SIZE];
byte pulsecode[SAMPLE_SIZE];
char direction[SAMPLE_SIZE];
boolean bit[32];
boolean lastbit;
int lastcode;
int currentcode;
unsigned int commandcode;//1st 16 data bits
unsigned int buttoncode;//2nd 16 data bits
char button[16]={"None"};
int i;
int n;
byte change_count;
int plen[SAMPLE_SIZE];
long time;
long time2;

void setup() {
  Serial.begin(115200);
  Serial.println("Analyze RC6 IR Remote");
  TCCR1A = 0x00;          // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
  // ICNC1=0 => Capture Noise Canceler disabled -- ICES1=0 => Input Capture Edge Select (not used) -- CTC1=0 => Clear Timer/Counter 1 on Compare/Match
  // CS12=0 CS11=1 CS10=1 => Set prescaler to clock/64
  TCCR1B = 0x03;          // 16MHz clock with prescaler means TCNT1 increments every 4uS
  // ICIE1=0 => Timer/Counter 1, Input Capture Interrupt Enable -- OCIE1A=0 => Output Compare A Match Interrupt Enable -- OCIE1B=0 => Output Compare B Match Interrupt Enable
  // TOIE1=0 => Timer 1 Overflow Interrupt Enable
  TIMSK1 = 0x00;          
  pinMode(RXpin, INPUT);
  pinMode(TXpin, OUTPUT);
}

void loop()
{
  Serial.println("Waiting...");
  change_count = 0;
  while(digitalRead(IRpin) == HIGH) {}                                 
  TIMER_RESET;
  TimerValue[change_count] = TCNT1;
  direction[change_count++] = '0';
  while (change_count < SAMPLE_SIZE) {
    if (direction[change_count-1] == '0') {
      while(digitalRead(RXpin) == LOW) {}
      TimerValue[change_count] = TCNT1;
      direction[change_count++] = '1';
    } else {
      while(digitalRead(RXpin) == HIGH) {}
      TimerValue[change_count] = TCNT1;
      direction[change_count++] = '0';
    }
  }//end of recording
  change_count=0;
  while (change_count<SAMPLE_SIZE){
    plen[change_count]=(TimerValue[change_count+1]-TimerValue[change_count])*4;
    if(plen[change_count]>headerlower &&plen[change_count]<headerupper) pulsecode[change_count]=0; //header pulse
    else if(plen[change_count]>singlelower &&plen[change_count]<singleupper) pulsecode[change_count]=1; //single pulse
    else if(plen[change_count]>doublelower &&plen[change_count]<doubleupper) pulsecode[change_count]=2; //double pulse
    else if(plen[change_count]>triplelower &&plen[change_count]<tripleupper) pulsecode[change_count]=3; //triple pulse or trailer(2) plus single(1)
    else pulsecode[change_count]=9; //ender or error pulse
    change_count++;
  }//done converting to pulse code
  
  change_count=10;//start at trailer
  i=0;//start at first (MSB) bit
  if(pulsecode[change_count-2]==0) bit[i]=0;  //decode bit directly after trailer
  else if(pulsecode[change_count-2]==1) bit[i]=1; 
  lastbit=bit[i];
  change_count+=2;
  i++;
  while(i<=31 || pulsecode[change_count]==9){  //decode the rest based on whether
    lastcode=pulsecode[change_count-1];        //or not they change
    currentcode=pulsecode[change_count];
    if(lastcode==2){
      bit[i]=!lastbit;
    }
    else if(lastcode==1){
      bit[i]=lastbit;
    }
    lastbit=bit[i];
    i++;
    if(currentcode==2)change_count++;
    else if(currentcode==1)change_count+=2;
  }
  BtoI(0, 16);      //binary to integer for command bits
  BtoI(16, 16);     //bin to int for data bits
  
  Serial.println("Bit stream detected!");
  //remove commenting for LOTS more info than just button pressed
  /*change_count = 0;      
  i=0;
  Serial.println("p len\tcode\thi/lo\tch cnt\tbit");
  while(change_count<SAMPLE_SIZE){
    Serial.print(plen[change_count], DEC);
    Serial.print("\t");
    Serial.print(pulsecode[change_count],DEC);
    Serial.print("\t");
    Serial.print(direction[change_count+1]);
    Serial.print("\t");
    Serial.print(change_count, DEC);
    Serial.print("\t");
    if(change_count<32) Serial.print(int(bit[change_count]), DEC);
    Serial.println();
    change_count++;
  }*/
  //Again, remove commenting for pure codes
  /*Serial.print("\nCommand Code\t");
  Serial.println(commandcode, DEC);
  Serial.print("\nButton code\t");
  Serial.println(buttoncode,DEC);*/
  Serial.print("Button\t\t");
  spitbutton();
  Serial.println("\nBit stream end!");
  change_count=0;
  i=0;
  delay(2000);
}

void BtoI(int start, int numofbits){    //binary array to integer conversion
  int integer=0;
  i=start;
  while(i<numofbits+start){
    if(i==togglebit){/*ignore toggle bit*/}
    else integer+=bit[i]<<(numofbits+start-i-1); //same as pow()
    i++;
  }
  if (start==16)buttoncode=integer;
  else if (start==0)commandcode=integer;
}

void spitbutton(){    //just prints out the button pressed
  switch (buttoncode){
    case UP:
      Serial.print("UP");
      break;
    case OK:
      Serial.print("OK");
      break;
    case LEFT:
      Serial.print("LEFT");
      break;
    case PLAY:
      Serial.print("PLAY");
      break;
    case FF:
      Serial.print("FF");
      break;
    case PAUSE:
      Serial.print("PAUSE");
      break;
    case MUTE:
      Serial.print("MUTE");
      break;
    case SKIPF:
     Serial.print("SKIPF");
      break;
    case CHUP:
      Serial.print("CHUP");
      break;
    case VOLUP:
      Serial.print("VOLUP");
      break;
    case EJECT:
      Serial.print("EJECT");
      break;
    case LISTING:
      Serial.print("LISTING");
      break;
    case RECTV:
      Serial.print("RECTV");
      break;
    case CLEAR:
      Serial.print("CLEAR");
      break;
  }
}

Hi,

I tried your codes.. it works ok but when I pressed the buttons too fast or any unrecognized buttons, the whole thing just "hang" there and need to be reseted as you mentioned.

I notice the hang was bcos it did not recognize any RC6 codes and did not reset itself to listen to new RC6 codes...

Why don't you put default switch to catch all unknown buttons...??

A few questions..why do you need to do a wait for 2000ms ??

I have some questions about the RC6 protocol.. I'm also using a MCE Remote controller...

The first time I pressed the PLAY button, it show 2148500502 but the second time I pressed the PLAY button it show 2148467734.

This is the same for all the buttons... all the buttons have two codes

Is this normal in the RC6 protocol ?

Thank

@knuckles
It would be very nice if you could make a library out of it. This would hide the core like the sending/ timing and only expose the calls an "enduser" needs like:

RC6 Remote(); // constructor
Remote.UP();
Remote.Eject();
etc

It is not that difficult, see http://arduino.cc/en/Hacking/LibraryTutorial