Decoding Infrared Codes

I was trying to remotely maneuver a prototype robot I have made last week. If you happen to read it, you might know about it. I had an infrared receiver named TSOP1738 and a few different remote controls left useless from a car audio system and a TV here. I had IR LEDs as well, but to avoid unnecessary complexity I chose to use one of the remote controls over building one on my own with the LEDs. Here is a study I have made to make this thing work as I expected.

First thing to do was to check what kind of signal I was getting out of the TSOP module when I press a button on a remote control. So I wired up the module to an Arduino as shown in the above image. Pin #3 is the output of the TSOP, which is wired to the Arduino’s pin #2 and power supply is taken out of the 5V-GND header of the board. The reason why I wired up it with Arduino is that I didn’t have a separate 5V power supply here ;). Anyway I hooked up my oscilloscope’s probes to the TSOP module and pressed a button on the remote control. Bingo! I got a signal that looks like the following.

The TSOP module’s output is not bare IR pulses sent by the remote control. TSOP demodulates (removes the 38Khz carrier frequency) the signal and presents a digitally usable output. It does also block off some noise, I don’t really know much about it. Anyway, I pressed different buttons on the remote control and I got significantly similar signals all the time. These were the characteristics generally.

  1. All the signals started with a LOW pulse which is quite long compared to the other pulses in the entire stretch. This portion of the signal is called the start burst. It was around 6000 micro seconds long as I measured roughly using the oscilloscope’s rulers.
  2. Right after the start burst, there is a long HIGH pulse as well. This portion is not as long as the start burst, but affirmatively longer than the other pulses. It was around 4000 micro seconds in time.
  3. After these two pulses, there were 32 trailing HIGH pulses equally separated. Each of them is either somewhere around 2000 micro seconds, or around 600 micro seconds.

The start burst and the high pulse after that are marked in the image above. As I learned from this video, there are different types of encoding techniques like RC5 or the NEC protocol etc. I’m not quite sure which encoding does the remote control I have here use anyway. But I chose to code the 2000uS long pulses as logical 1’s and 600uS long ones as logical 0’s. I decoded the first button on the remote control as the following.

11100000 00011111 10100000 01011111

The second byte is the inverse of the first and the fourth byte is the inverse of the third. I think it is some mechanism to reduce errors. As I gone through encoding each keys manually, I figured out that the first 16 bits are the same for all keys. I later learned that it could be a device ID or something. Leaving that behind, the last 16 bits gave me usable data to find which button on the remote control has been pressed. See below the quite intuitive C code I used with the Arduino to decode each key’s code values.

int irPin      = 2;    //Sensor pin 1 wired to Arduino's pin 2
int start_bit  = 2200; //Start bit threshold (Microseconds)
int bin_1      = 1000; //Binary 1 threshold (Microseconds)
int bin_0      = 400;  //Binary 0 threshold (Microseconds)

void setup() {
  pinMode(irPin, INPUT);
  Serial.begin(9600);
  Serial.println("Waiting:");
}

void loop() {
  unsigned int key = getIRKey();          //Fetch the key
  if(key != 65535)  { //That is -1 aka invalid key
    Serial.println("Key:" + key);
  }
}

int getIRKey() {
  int data[32];
  int i;

  while(pulseIn(irPin, LOW) < start_bit); //Wait for a start bit
  
  for(i = 0 ; i < 32 ; i++)
    data[i] = pulseIn(irPin, HIGH); //Start measuring bits, I only want high pulses
  
  for(i = 0 ; i < 32 ; i++)  {
    if(!((data[i] > 400 && data[i] < 1000) || (data[i] > 1700 && data[i] < 3000)))  { //Filter out anything that seems  erroneous
      Serial.println("Invalid Signal");
      return -1;
    }
  }
  
  int key_data[16];
  for(i = 16 ; i < 32 ; i++)  //Parse them
  {
    if(data[i] > bin_1) //is it a 1?
      key_data[i-16] = 1;
    else if(data[i] > bin_0) //is it a 0?
      key_data[i-16] = 0;
    else
      return -1; //Flag the data as invalid; I don't know what it is! Return -1 on invalid data
  }
  
  unsigned int result = 0;
  for(i = 0 ; i < 16 ; i++)  { //Convert key_data bits to integer
    if(key_data[i] == 1)
      result |= (1<<(i));
  }
  return result; //Return key number
}

Lately I got into some inconsistency problems and noise issues. I fixed a few of them, but I still have some pain in the ass kind of problems remaining. Here’s one of them.