Let's Make Robots!

Simple IR decoder for Sony remotes

Allows any Arduino board to accept commands from an IR receiver
SIRC_decoder.zip1.07 KB
SIRC_with_Timer1.zip1.17 KB

Most homes these days have at least 1 infrared remote control for controlling the television. These remote controls can also be used to control robots or even to act as navigation beacons.

The only hardware your robot needs to receiver these signals is an IR receiver module. These look a bit like a large transistor with a lens on the front. These modules are small, cheap and include not only an IR sensor but also filter and amplification circuitry.

If you want detailed information how IR remotes work then this tutorial explains it better than I could. This tutorial is concerned primarily with the software used to decode the signal.

There are some libraries on the internet already that allow you to use an IR remote. Most of these libraries use a timer interrupt to monitor the IR receiver's signal pin. This can be a problem. The timer used may also be used by other functions or library such as the Tone() command which uses timer 2 and the Servo library which uses timer 1.

My sample code uses an external interrupt instead to solve this problem. It is not a library, just a small ISR (Interrupt Service Routine) that gets called when the IR receivers output changes state. This is about as small and efficient as I can make the code. It does not use an interrupt timer so it should work with all Arduino compatible controllers not matter what processor or clock speed they use. Because it is small it is ideal for use with boards that use the ATmega8 processor.

I have now attached a version that does use the Timer1 interrupt library. if you are using an Arduino Mega1280 or Mega2560 then you may prefer to use the Timer3 library instead. The only real difference in the code is that the width of the IR pulse is measured in 200uS blocks. if the width is greater than two 200μS blocks it is a "0", if it is greater than four 200μS blocks it is a "1" and if it is greater than eight 200μS blocks then it is a start bit.

How it works:
The first part of the sample code is where you define which pin your IR receiver is connected to and initialize a global variable IRC which stores the command received from the remote. There is only one line of code that must be in the setup() function. This is where you attach the ISR to the external interrupt pin. If you have not used an external interrupt before then you should read this first.

In my sample, the main loop simply prints the IRC value on the screen and then waits 100mS because IR remotes usually repeat a command 2 or 3 times. The IRC value is then set back to 0 until a new command is received.

#define IRpin 2                              // this must be an external interrupt pin
volatile byte IRC;                           // global variable used to store IR Command

void setup()
  attachInterrupt(0,IRdecode,CHANGE);        // call IRdecode when IRpin changes state (use external interrupt that matches IRpin)
  Serial.begin(9600);                        // initialize USB serial port for serial monitor on PC
  Serial.println("Testing IR");

void loop()
  if(IRC!=0)                                 // has an IR command been received?                                               
    Serial.println(IRC,DEC);                 // display received command                
    delay(100);                              // most remotes will repeat signal 2 or 3 times, use this delay to prevent repetition of command                           
    IRC=0;                                   // reset IRC until new command received

The ISR function is where the decoding takes place. It should be noted that this code only works with Sony IR Code (SIRC). If you do not have a sony remote then universal TV remotes are very cheap and can be programmed to behave as a Sony remote. The Sony protocol is fully explained here.

To understand my sample code you only need to know that "0" bits are 600μS wide, "1" bits are 1200μS wide and the start bit is 2400μS wide. My code only reads the first 7 command bits and ignores everything else (typically a 5 bit device signature).

//============================================= Sony IR Code (SIRC) Decoder)===================================================
void IRdecode()
  volatile static unsigned long irbitwidth;  // width of data bit in uS
  volatile static byte irdata;               // temporary storage of byte value as bits are read
  volatile static byte irbitvalue;           // decimal value of bit being read (1,2,4,8,16,32,64,128)
  volatile static byte irbitcount;           // counts number of bits since start bit (only first 7 bits are used)       

  if(digitalRead(IRpin)==0)                  // check state of IR receiver output 
    irbitwidth=micros();                     // bit starts when IR receiver pin goes low
  else                                       // IR receiver output high - end of bit
    irbitwidth=micros()-irbitwidth;          // measure width of bit
    if(irbitwidth>800ul)                     // bitwidth>800uS = logic "1" (logit 1 typically 1200uS)
      irdata+=irbitvalue;                    // increment data by bit value
    irbitvalue*=2;                           // bitvalue updated for next bit (1,2,4,8,16,32,64,128)
    irbitcount++;                            // count the bits

    if(irbitcount==7)                        // only want first 7 bits
      irbitvalue=0;                          // additional bits have a value of 0
      IRC=irdata+1;                          // make data available to user

    if(irbitwidth>1800ul)                    // start bit resets decoder values (startbit typically 2400uS)

When the IR receiver output goes from high to low a bit has started so the start time is recorded in the variable irbitwidth. When the output goes high the bit has ended. The start time is subtracted from the current time to give the bit width in μS.

If the bit is wider than 800μS then it is considered a "1" and the decimal value of the command is incremented. Regardless if the bit is a 1 or a 0 the irbitcount is incremented and the irbitvalue multiplied by 2 ready for the next bit.

On the 8th bit the command value is stored in the global variable IRC and all additional bits have no value. If the bit is wider than 1800μS then it is considered to be a  startbit. The variables irbitvalue, irbitcount and irdata are reset ready for a new command.





Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Thanks for posting. I see that it is tagged as Tip/walkthrough but I don't see anything in the Tip/wallkthrough options at the top of the LMR page. I would like to save it for an upcomming project. I typicall use PIC24's which often contain a IR peripheral that I have never used.

You can make your own custom collection by clicking on the "collect" button.

If you look at the datasheet for the TSOP-1738 it has an open collector output with a built in pullup resistor.

You say that your TSOP-1738 output remains high constantly so either you have wired it wrong or the remote you tested it with was not modulating at 38kHz.


If this device and TSOP - 1738 which I have at hand at the moment are same i.e. can one be substituted for another ?

As far as my experience goes, with tsop, it gives high on o/p pin irrespective of which key is pressed on the remote...

So can the above recognise if "1" is pressed or "5" etc..on the remote?

P.S :You mistyped sony protocol as sory (sorry?) protocol above ;) Wonder if a sorry protocol exists?! Brought a smile on my face though!