Friday, January 18, 2008

irecho

IR Echo (infra red remote control stuff)

irecho-20080118a.tar.gz

I have been working on this project for right around 10 years now. No, not full time, not part time, some years I may think about it once and not act on it. I finally got around to working on it and actually finishing it.

I have a mid 1980's RCA TV, which at the time was a special order top of the line thing. Wouldnt be surprised if it was one of the early RCA TVs to use an infra red remote. The long and short of it is this TV has outlasted any TV purchased since, a couple of buttons on the TV, power in particular have worn out, but the remote, despite several moves and 20+ years is still working. If I lose the remote though the TV might become a brick. You cannot find this TV on google searches much less the remote. Either due to the rareness of the TV or how early its infra red remote is or isnt, it is not supported by any of the pre-programmed remote codes in universal remotes. Yes some remotes will "record" ir and then retransmit it, but that has limited success.

So my thought was, why not program the remote for a TV it does know about, receive the codes for that TV and then blast the codes for the real TV (that the remote does not know about). Take that one step further and you could just as easily do the same thing to support a non xyz brand device using an xyz branded remote.

The problem I had in this specific case was figuring out the IR protocol/codes for the old tv. I didnt do anything different this time around, yet, this time around I was able to figure out the codes without too much trouble. Generating those codes has been interesting, and most of the problem there was I was trying to do it in C with gcc whose code generation and optimization varied widely.

IR remote control is really not complicated at all.
http://www.sbprojects.com/knowledge/ir/ir.htm
Explains a number of protocols quite well. I used to use the documentation for a program you could run on an hp48 calculator to turn it into a remote control. Basically, an IR led is used and you blink it at a frequency like 40KHz for example. Blink it on and off for so many micro or milliseconds, then leave it off for so many micro or milliseconds, and repeat this, varying the length of the blinking and off periods. The receiver modules I have used do the analog work for you and the output is a square wave, with the blinking on/of period windowed by one output state ("high") and the non-blinking as a ("low"). The job for the microcontroller is to time the high and low periods and decode them based on a protocol. There are some standard protocols as described in the link above.

So for a number of reasons, after trying a few, I chose what the link above calls the Sony SIRC protocol. The IR receiver module I am using is the same or similar to one you can get at Radio Shack (see below). The SB-Projects page says the Sony protocol is 40KHz and the receiver I am using is 40KHz which is probably why I had better luck with that protocol than the RCA protocol (a modern one not the old one). When I used the RCA protocol it only worked between x and y feet from the receiver and you really had to aim right at the receiver. With the sony protocol you can be anywhere in the room and dont really have to aim at the receiver, many walls and other surfaces will reflect the IR and still work.

The Sony protocol starts with a 2.4ms pulse. I poll the ir input pin, wait for a state change, when there is a state change grab the counter from a timer, poll for the next state change, grab the counter, subtract the counters and if the time of the pulse is the right length plus or minus some acceptable slop (aiming at the walls or really close or really far from the receiver) call it good. From there I went into decoding the code, the blank periods should all be 600us plus or minus some slop followed by an on period that is either 1.2ms for an one,or 600us for a zero. The code has 12 bits (24 state changes after the start pulse). If you manage to collect 12 bits without any of the periods between state changes being out of range, call it a good code and process it. Simple.

The LM3S811 eval board runs at 6MHz by default so the 2.4ms start pulse is 0.0024 * 6000000 or 14400 timer ticks. The 1.2ms pulse is half that at 7200 ticks and the 600us periods should be around 3600 ticks. It is really easy to tweak and or rearrange the example to receive other protocols. Likewise change the math above and you can do this on the LM3S6965 or LM3S1968 (or others). If I remember right the LM3S1968 did not have 3.3v and ground and an available input pin within reach of each other to easily solder in a receiver, so I primarily worked with the LM3S6965 and the LM3S811 for this project. I didnt want to dedicate the 6965 to this job so I bought another '811 to dedicate to this job. Actually I bought some msp430 ez430 modules to dedicate to this job but, my guess is, because of the instability of the internal clock I couldnt get it to work. I also struggled getting the uart to work to print out the codes as I was figuring them out and eventually gave up and bought the LM3S811 board.

Decoding is half the fun, I used the same decoding method to finally figure out the protocol/codes for the older RCA remote. To blast the ir, I bought some IR leds from radio shack (part number?). ou have to know or figure out the frequency to blink the led and then bit blast that at a close enough rate to make it work. I figured the old remote was somewhere around 32KHz or some harmonic. I only really cared about it working at point blank range so I could be a little sloppy. The problem is that 6MHz/32000 is not an even number of timer ticks. Even if it were I was struggling trying to get the C compiler to produce clean enough code to get the state changes reasonably close. I eventually gave up and wrote a some hand tuned assembler which worked quite well. But was, of course, tuned for my 6MHz LM3S811


/* hand tuned ir blinker for the '811 */
.thumb_func
.global blinker
blinker:
;@ r0 times, r1 address
2:
mov r2,#0xFF
str r2,[r1]
mov r2,#29
1: sub r2,r2,#1
bne 1b
mov r2,#0x00
str r2,[r1]
mov r2,#29
1: sub r2,r2,#1
bne 1b
sub r0,r0,#1
bne 2b
bx lr



The idea was to pre-calculate the number of on/off blinks I would need, feed this function the number of blinks and the address to write to the IR LED. The code turns on the led, has a tight little loop that counts to 29, two instructions per loop not counting the setup. Then turns off the led, counts to 29 and repeats all of that r0 times. I didnt bother getting anal with the nops to make the whole loop a perfect square wave, the r0 counter is going to make it just a little off. From C I timed it using the system timer, had it do 150 or so blinks and divided by that number and it came out around 181.1 ticks per loop, used that number to pre-calculate the number of loops I would need for each "on" period. For the blank periods of the IR output I know from decoding how long the on plus off period needed to be, so grab the timer tick before blasting the IR, after blasting the IR, wait until the timer has burned both the on and off periods of time before moving on to the next on pulse.



void blastir ( const unsigned short *data, unsigned int len )
{
unsigned int ra,rb,rc;

SetLedx();
ResetTimer();
for(rb=0;rb<4;rb++)
{
rc=GET32(TIMER2_BASE+GPTMTAR);
for(ra=0;ra < len;ra+=2)
{
blinker(data[ra+0],IRLED_BASE+GPIO_DATA+((IRLED_PIN) << 2));
rc-=data[ra+1];
while(GET32(TIMER2_BASE+GPTMTAR) > rc);
}
}
ResetLedx();
}



Again, its actually pretty simple stuff.

One thing to note if you ever try this and perhaps try this with two modern or similar devices. If they use the same code or a code with time periods that are similar you may confuse the target device. The remote transmits some code using protocol A, both your microcontroller and your target device (TV, VCR, whatever) see the code from the remote, your microcontroller turns around and transmits another code using protocol A. The human may still be pressing the button on the remote, now the target device is getting hit with IR from the remote and the IR from your microcontroller and it gets confused and doesnt react to either.

IR Reciever Module. Digikey part number 425-1904-ND
Looks like it might be similar to the module available at radio shack although I found out the hard way that the pinout at Radio Shack is different and you will warm up the module and remove some fingerprints when you touch it to find out it is clearly wired wrong. My Luminary Micro LM3S811 evaluation board survived the torture fortunately. (I bricked the first one I bought when I accidentally programmed a JTAG pin the wrong way).