Friday, June 1, 2012

IR remote control experiments

Introduction

I have a few projects in mind for the near future which all have one thing in common – they all require remote controls to be operated. So I decided to experiment with remote controls a bit.


A small selection of my huge pile of never or a very long time ago used remote controls that are waiting to be repurposed.


Selecting a transmitter

Since this is my first attempt to use IR as a communication channel I wanted to start out with a simple setup. This involved using an existing remote control so that I only have to deal with receiving and decoding IR messages. I already have a few IR remotes on my living room table and countless more in my junk pile…

After a bit of research on the internet I found Vishay’s TSOP series IR receiver modules which greatly reduce the complexity in receiving IR messages. According to the data sheet: “The TSOP312.., TSOP314.. series are miniaturized receivers for infrared remote control systems. A PIN diode and a preamplifier are assembled on a lead frame, the epoxy package acts as an IR filter.” I ordered a few of these decoders and when they arrived I immediately created the minimum test circuit to check out how they work.

I used this minimalistic circuit to visualize the waveforms.

I used an oscilloscope to visualize the decoded pulses, but I also found some other solutions for hobbyists without a scope on other web sites. This page shows you how to use Microchip’s PicKit2 while this page uses a very simple circuit attached to the serial port of a PC to capture and visualize the output of the TSOP.

The following waveforms are from some of the different remote controls I tried:






Unfortunately, as you can see, no two remotes use the same standard. Eventually, I settled on using an IR remote of a Media Center that I had extra. This seems to use one of the simpler protocols of all the tested ones.
An old USB TV card kindly "donated" the chosen remote control

This remote seems to use some standard very close to the NEC Infrared Transmission Protocol. The only difference I found is that the address bits are not repeated in inverse. It seems to me that this approach allows more devices to be addressed - at the cost of some loss off fault tolerance.
These are some sample messages I measured:

00010000 01100111 10101000 01010111 1
00010000 01100111 00101000 11010111 2
00010000 01100111 01101000 10010111 3
00010000 01100111 10000000 01111111 on/off


The above samples are enough to understand how the messages are structured: the first two octets are the address, the third octet is the command and the fourth octet is always the inverse of the command. In the last column I included the name of the button I pressed, it is not part of the message. For my purposes it doesn't matter which way the octets are read, i.e. we can call the first octet 0x10 or 0x08, depending on if the LSB or the MSB is to the left.

Creating a receiver

To move on to something more useful I created another minimalistic circuit on the breadboard. To decode the commands I used a PIC micro, an 18F4550. Of course, this is overkill if you only want to decode the commands, but I had my reasons: First, I wanted to move up a step from the base line micros and thought this would be a good opportunity to do so, second, I will need this chip later on in the real project(s) for the IR receiver.

Schematics of the test circuit

To display the commands I used a cheap, standard 2*16 LCD module. I also used a number of LEDs to make sure everything works as expected:
  • One LED to indicate the circuit is powered up (probably the most important :-) ).
  • One LED flashing at around 0.5Hz in the main cycle, to simulate normal program execution.
  • One LED that lights up during the decoding of a single command.

Test application

The firmware is very simple, given this is my first firmware written in C. In the main cycle it flashes an LED at around 0.5 Hz. The demodulated signal is fed into the INT0 pin of the microcontroller. Every time (after a long idle period) a falling edge is detected the decoding routine is started.
The decoding routine is quite sturdy – I tried to mess up it up by using other IR remotes etc., but so far I haven’t been able to. Below is the listing of the decoding routine:

void high_isr (void)
{
    unsigned int i, j, t1, t2;
    if (INTCONbits.INT0IF){
        LEDpin = 1; //  Activity LED
        t1 = MeasureLowTime();
        t2 = MeasureHighTime();
        if ( ( t1 + t2 ) > START_PULSE_LENGTH )
        {
            Address = 0x00;
            NotAddress = 0x00;
            Command = 0x00;
            NotCommand = 0x00;

            for (i = 0; i < 4; i++)
            {
                for(j = 0; j < 8; j++)
                    {
                        INTCONbits.T0IF = 0;
                        while(~TSOP)
                        {
                            if ( INTCONbits.T0IF )
                                break;
                        }
                        t2 = MeasureHighTime();
                        if ( t2 > ONE_PULSE_LENGTH )
                        {
                            switch (i)
                            {
                                case 0:
                                    Address = Address | (0x01 << j);
                                    break;
                                case 1:
                                    NotAddress = NotAddress | (0x01 << j);
                                    break;
                                case 2:
                                    Command = Command | (0x01 << j);
                                    break;
                                case 3:
                                    NotCommand = NotCommand | (0x01 << j);
                                    break;
                            }
                        }
                    }
            }

            if ( Command != ~NotCommand )
            {
                Address = 0x00;
                Command = 0x00;
            }

            PrepareUI();
            XLCD_CursorTo(1,4);
            XLCD_WriteUChar(Address);
            XLCD_CursorTo(1,13);
            XLCD_WriteUChar(Command);
        }


        LEDpin = 0; //  Activity LED
        INTCONbits.INT0IF = 0;
    }
}


I think I arrived to a stage where I can move my focus on the actual project and can use this setup and routine to deal with the IR code decoding.

This is a small video demonstrating the above: