Reliable IR Receiver code for Duemilanove

Does any please have some simple, reliable, working code for an interrupt-driven IR receiver (IR remote control) for a Duemilanove? Something that will return a unique code for each button pressed.

I've tried many examples that I've found online, including the very promising looking one at www.5volt.eu, but without sucess, and I'm very badly hampered by my ignorance of interrupt processing. It's hard enough struggling to learn C programming :frowning:

Any suggestions will be very gratefully received.

Regards to all,
Mark
Ottawa, Canada.

This isn't totally reliable yet, but if it helps you it mostly works for Sony's IR format. You'd have to adjust the timings and possibly invert the routine to support a different protocol. I've had a heck of a time trying to balance having sufficient tolerance to avoid missing valid codes against misidentifying codes when tolerances are too large, although I'm not sure why this is the case given that the pulseIn based routine I tried first worked so well with a tolerance of +- 60 us. At this point it pretty much is more likely to drop a code than give a false code, which is preferable, but not ideal.

It may be more reliable to record the time of each transition to an array in the ISR and then interpret the timings into an IR code within loop(), since it allows the ISR to be much shorter. Perhaps someone will post an example of that strategy.

This routine should be attached with the CHANGE argument to catch both edges of each pulse. Once a complete code is received, the ISR places it in newKey and sets freshKey = true. It is up to the key handling routine in loop() to recognize that a new key is available, handle it, and then clear freshKey.

volatile unsigned int newKey = 0;     // Once a full 12bit code is received, the IR receiving routing parks the code here
volatile unsigned int keyBuffer = 0;  // Buffer to hold the control code as it is being received
volatile boolean freshKey = 0;        // Indicates whether or not a new key in newKey requires handling.
volatile unsigned long lastRise = 0;
volatile unsigned long lastFall = 0;
volatile unsigned int mask = 1;
volatile boolean start = false;


void receiveIR() {
  
  if( freshKey ) return; //ignore new data if there is an unprocessed key waiting in newKey still to prevent collision.
  
  if( digitalRead( irPin ) == HIGH ) {
    lastRise = micros();
    if( lastFall + 2400 <= micros() ) {
      start = true;
      keyBuffer = 0;
      mask = 1;    
    }
    // If the low side is long enough to be a 1 . . .
    else if( start == true && lastFall + 1220 <= micros() /*&& lastFall + 1300 >= micros()*/ ) {
       keyBuffer |= mask;
       mask <<= 1;
    }
    // If the low side is long enough to be a 0 . . .
    else if( start == true && lastFall + 600 <= micros() /*&& lastFall + 700 >= micros()*/ ) {
      mask <<= 1;
    }
    // If it's too short for a 1 or a 0, start over
    else {
      start = false;
      keyBuffer = 0;
      mask = 1;   
    }
  }
  // If the high side is long enough to be a part of a digit . . .
  else if( lastRise + 400 <= micros() ) {
    lastFall = micros();
  }
  else {
      start = false;
      keyBuffer = 0;
      mask = 1;  
  }
  
  // Dump buffer contents into newKey and set freshKey when the buffer reaches 12 bits 
  if( mask >= 4096 ) {
    freshKey = true;
    start = false;
    newKey = keyBuffer;
    keyBuffer = 0;
    mask = 1;    
  }
}

Thanks ajb.

I assume I would use attachInterrupt to call receiveIR on a CHANGE? I gather the timing is tricky and I assume that the remote control would have to be RC5 compliant.

Anyway, I'll give this a try.

Regards,
Mark.

My thanks also ajb. :slight_smile:

I'm currently using something similar . . .
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1234067750/0 but your's looks like it might be more reliable, so I'm going to compare it.

But I do have a question. What's the story on the commented out sections in the else if statements? Is it better to leave them out?

I'm using the remote to my Panasonic TV, with the DBS/CBL button programmed to one of the Sony device codes from the TV manual. I found this sketch first: SIRCS Monitor and then tried different device codes from the manual until I found one that worked and supported the buttons I wanted to use, and used the codes reported by SIRCS Monitor to identify the codes for those buttons.

The Sony protocol consists of a 2.4ms start pulse, followed by 12 bits, LSB first. Each bit starts with a 0.6ms gap, then a 0.6ms pulse for 0, or a 1.2ms pulse for 1. The next code is transmitted about 45ms after the previous code began, so there's a gap of about 21-28ms between codes. You can see the waveform graphed here: http://www.kucher.org/projects/tvcontrol/

But I do have a question. What's the story on the commented out sections in the else if statements? Is it better to leave them out?

So, the routine should be looking for IR pulses that are ~2400us (start pulse), and then either a ~600us pulse or a ~1200us pulse. Since the receiver I'm using is active low, the routine looks at the low pulses on the input which indicate that the receiver is detecting IR. So I started by having the routine detect if the pulse was 2300-2500us, 1100-1300us, or 500-700us and rejecting outside of all of those ranges. Hence why you see two tests in each if(). Due to the sloppiness in making microsecond measurements with non-blocking code, though, and the delay between the IR receiver receiving IR and the ISR actually getting to the point where it checks against micros(), I found I got better results with the method I posted where pulse length is simply compared against a floor of 2400, 1220, or 600us. In particular I was getting a lot of false ones, which indicates that the delay in the routine is significant enough to push "0" delays into "1" territory. Any pulse shorter than 600 us is rejected and causes the receiving routine to reset, as does any gap shorter than 600us. The micros() bug is also an issue in this code, as I understand it, although I'm not sure how much of an impact it may be having in practice. (It's not clear to me how often the micros() overflow bug occurs, having not read enough of the datasheet to understand exactly what's going on in micros() ).

I'm going to continue trying to improve the routine, so if you have any suggestions or epiphanies, I'd be glad to hear them :).

ajb,

If I understand you correctly, I think the comments are just that, they are not commented out code statements, they just happen to begin with the word "If...".

Also, I had great success last night with a library described at:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556

It's an old thread, but the library seems very well crafted. It didn't initially work for me, but you will see at the end (on page 2) I added a comment about adding #include <WProgram.h> to main sketch. You may want to give this a try. You will need the NECIRrcv file, which I got from:

http://www.easy-upload.nl/index.php/file/164a43b8100a5b3

Hope that works for you, or helps you with your own coding.

Regards,
Mark

If I understand you correctly, I think the comments are just that, they are not commented out code statements, they just happen to begin with the word "If...".

I think it's more likely BroHogan was referring to the lines with

else if( start == true && lastFall + 1220 <= micros() /&& lastFall + 1300 >= micros()/ ) {

...

else if( start == true && lastFall + 600 <= micros() /&& lastFall + 700 >= micros()/ ) {

You are quite right. My mistake :frowning:

Thanks,
Mark.