Trying a multi-protocol interrupt based IR rcv lib

Hello to all, I'm another new member.

I've searched a bit about IR Receiving recently. Most arduino sketches or AVR code are using the main loop for fetching High or Low pulses time. I haven't found any multi-protocol AVR/Arduino code yet.

I tried the current sketches, and they work ok, but for my purpose, I need to be able to recognize most protocols, live, not asking to press a button multiple times to record it, but decoding it to know it's value. I will gladly release any working code, in the best case making a library for IR receivers.

In order to be able to decode multiple protocols, both High and Low pulses times are required. Sony protocol uses high pulses, Recs-80 uses low pulses, RC5 uses both.

Also, in order to be useful (and to fit my coding standards), the main loop needs to be freed.

Another thing I want to do is to verify the code by testing many protocols at the same time. This can be done by verifying the same array of raw times or having an array for each protocol.

Finally, the pulses time in Microseconds needs to be pretty accurate, but from what I saw, the pulses are at 300 microseconds appart (in the Sharp protocol) or more.

There is a multi-protocol ir receiver code already existing, but it's not for the arduino, and I don't have the knowledge to convert this time critical and interrupt based code to arduino code. So I'll try to make my own version from what I understand.

For the implementation, I think the best thing to do would be to attachInterrupt using CHANGE to monitor both HIGH and LOW times and a flag variable for the current value (High or Low). To get the times, I think micros() should be accurate enough.

My problem right now, (the reason of this post, and this incomplete code), is that the interrupt function is too slow. That means that the interrupt keeps interrupting itself, and my previous time is now bigger than my current time.

Still, I'm currently debugging the RC5 protocol which should be around 800 microseconds before launching another interrupt, so I don't know what is taking so much time, except perhaps a serial print for debugging.

Here's my current code. If there are some who think that this might be useful and are willing to help me get through this, or if this is undoable because of technical reasons that I don't know yet, feel free to help me go in the right direction.

int LedPin = 13;            // For debugging purposes

int InterruptNb = 0;        // Number of the interrupt  
int InterruptPin = 2;       // Matching pin number of the interrupt

int CurrDir;                // High or Low

unsigned long PrevTime;     // What was the time in the previous call
unsigned long CurrTime;     // What was the time in the previous call

unsigned long PrevHigh = 0; // Previous High pulse in Microseconds
unsigned long PrevLow = 0;  // Previous Low pulse in Microseconds
unsigned long CurrHigh = 0; // Current High pulse in Microseconds
unsigned long CurrLow = 0;  // Current Low pulse in Microseconds


void IRDirectionChange (void)
{  
  // First thing, let's get the current time, it's the most time critical element  
  CurrTime = micros ();
  
  // TODO: take care of the overflow every 70 mins...
  if (CurrDir = HIGH) { 
    // The High pulse just ended, let's get it's time
    PrevHigh = CurrHigh;
    CurrHigh = CurrTime - PrevTime; 
  } else { 
    // The Low pulse just ended, let's get it's time
    PrevLow = CurrLow;
    CurrLow = CurrTime - PrevTime;    
  }

  // Let's change direction for the next one
  CurrDir = !CurrDir;
  
  // Let's get ready for the next call
  PrevTime = CurrTime;

  // Let's send the current value to the decoders
  // IRDecode ();

/*
    Serial.print (PrevHigh);
    Serial.print (" ");
    Serial.print (PrevLow);
    Serial.print (" ");
    Serial.print (CurrHigh);
    Serial.print (" ");
    Serial.print (CurrLow);
    Serial.println (" ");  
    */
}


void setup (void)
{

  Serial.begin (115200);
  // First, let's get the current direction
  pinMode (LedPin,OUTPUT);
  pinMode(InterruptPin, INPUT);  

  CurrDir = digitalRead (InterruptPin);

  // Let's get ready to calculate
  PrevTime = micros ();
  // Starting now, every change of direction will be timed by this procedure
  attachInterrupt (InterruptNb,IRDirectionChange,CHANGE);
  
}
  
void loop (void)
{
  // Do anything here...

}


void IRDecode (void)
{
  IR_Decode_Sony ();
  IR_Decode_RC5 ();
  IR_Decode_RC6 ();
// etc.  
}

void IR_Decode_Sony (void)
{
  // Sony: Uses High pulse time
  // We check if we are in a low pulse to know what was the HighTime
  /* Pseudo Code
  if CurrDir = LOW 
    if Sony.Valid = False and CurrHigh < Sony.MaxStart and CurrHigh > Sony.MinStart 
      Sony.Valid = True
      Sony.ArrayIdx = 1
    else if Sony.Valid and CurrHigh < Sony.MAX and CurrHigh > Sony.Min
      Sony.ArrayIdx++
      Add this bit to the Sony Array
    else
      Sony.Valid = False
  */
  
}

void IR_Decode_RC5 (void)
{
  // RC5: 889 microseconds pulses
  // High pulse + Low pulse for a 0
  // Low pulse + High pulse for a 1
  // For example, 889us low, 1778us high, 889us low = 1,0

  /* Very rough Pseudo Code
  if CurrHigh <> 1778 and CurrHigh <> 889 
    RC5.valid = false

  if CurrLow <> 1778 and CurrLow <> 889 
    RC5.valid = false
  
  if RC5.valid and CurrHigh = 889 and CurrLow = 889 and CurrDir = LOW
    RC5.ArrayIdx++
    Add a 0 to the index
  else if RC5.valid and CurrHigh = 889 and CurrLow = 889 and CurrDir = HIGH
    RC5.ArrayIdx++
    Add a 1 to the index
  
  etc.
  */
  
}

void IR_Decode_RC6 (void)
{

}

except perhaps a serial print for debugging.

Nail and head collision. :wink:

Assume each variable is 8 digits with spaces and CR you have 37 bytes to transfer, that is 370 bits. At a bit rate of 115200 bits per second this payload will take 3.21 mS to print out your debug. You say your interrupts are 300uS appart so you haven't enough time to print out before the next interrupt arrives.

I removed the Serial.print from the interrupt and it's already better. I also had a lot of noise (hardware side) on the input so I cleaned this too.

I also put my values as volatile as it's supposed to be better with interrupts and I'm putting the values in a rotary buffer for now.

I'm debugging with leds so I don't have as much debugging info, but it's not slowing the interrupt as it was before.

Now, I have to figure out what's the best size of my buffer, because the memory is limited, but if it's too small, it might be re-filled before it's even read once. I might try to use "int" instead of "long" and check the time overflow.

Hi SlyCoder. What is the status of your project, please?
Do you have any code to send what you've decoded yet?

:stuck_out_tongue: Just wandered by ......

"Multi-Protocol Infrared Remote Library for the Arduino" By Ken Shirriff http://arcfn.com/ :wink: Decodes values of NEC, Sony SIRC, Philips RC5, Philips RC6, also prints raw signals. Receives & sends supported protocols & raw. It seems many smaller manufacturers & ones that don't have their own protocol tend to use NEC. More protocols can be added. Check the comments https://www.blogger.com/comment.g?blogID=6264947694886887540&postID=7188548062857097922 in particular Wolfgang comment "I added the Panasonic IR protocol" (Sept 17, 2009 12:42 PM) I haven't got his Panasonic protocol working yet but I've just started with arduino/C & C++.

I have only been using it for a day, tested decoding NEC & Sony protocols, both seem flawless so far. Remote thermostat? Remote locking mini fridge? Remote BLINKY LIGHTS !!!!!!**!**11one :smiley: .... I degress sorry :-X

Just ran across your thread while searching for info / description of the Sharp protocol. Anyone know of a detailed description or any info on it? Bit of info at http://www.sbprojects.com/knowledge/ir/sharp.htm or http://tinkerish.com/docs/ir%20remote%20control%20details.pdf

Sorry, I just climbed my way back to earth. My code is working for RC5, and I was working on Sony protocol next. My code is a bit modular so it's pretty easy to add new protocols.

I also noticed that a couple of RC5 codes aren't fixed, Rewind button code on one remote could be the display guide on another and the sleep on a third. I wanted to send keys or string text for the value, but I might have to fall back to the number value of the code.

I looked at Ken Shirriff's code, and it seems pretty simple enough to work, and have a couple of things I probably won't have for some time (raw codes support and sending codes). However, I will attempt to continue mine, since it's interrupt based and I want to free my main loop for other stuff.

If someone want's to look at the code, I zipped my current alpha version which I didn't have time to retest. Don't expect it to work directly as I cleaned it a bit and didn't try to recompile it.

http://pages.infinit.net/silas/arduino/IR_Lib_AlphaVersion.zip

I hope not to fall back out of the earth again, the climb was pretty long this time.

As for what protocol Panasonic uses..

I am struggling with that myself these days. I want to control my Panasonic tv with my Arduino, but I can't find any half-decent info on the IR-protocol in question. So, I made a sketch that outputs raw data from the IR-signal sent by my remote.

From what I gather, we're talking about a 48-bit signal where the first 32 bits are manufacturer and device id, and the last 16 are the command id. The protocol itself is called RECS80 or RECS-80.

"T" equals roughly 430-460 microseconds, and the signal is coded as follows (+ meaning "carrier on" and - meaning "off"):

Header:

  • 8T
  • 4T

digital 0:

  • T
  • T

digital 1:

  • T
  • 3T

Followed by a trailing "0" to allow the receiver to get the last bit of the code.

My problem is that even though the code I currently have generates a signal pretty perfectly equal to the one outputted by my remote, nothing happens! I previously had an older tv which used the NEC protocol so I know my borrowed oscWrite() function actually generates a carrier close enough to 38KHz to make the receiving equipment react.

Anyone got any ideas..? :-[

Code for sending test signal to my tv (note: it sends the signal 4 times in a row, as someone wrote somewhere that his VCR only reacted if the signal was transmitted at least 4, and less than 10 times):

int irPin = 13;
//int data[48] = {0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1};
int data[48] = {0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,1};
//int data[48] = {0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1};

int t = 435;
int sends = 0;

void setup()
{
  pinMode(irPin, OUTPUT);
  Serial.begin(9600);
  Serial.println("System ready!");
}

void loop()
{
  //send preamble
  oscWrite(irPin, t*8);
  delayMicroseconds(t*4);

  //send data
  for(int i = 0; i < 48; i++)
  {
    //space
    oscWrite(irPin, t);

    //data
    if(data[i] == '0')
    {
      delayMicroseconds(t);
    }
    else
    {
      delayMicroseconds(t*3);
    }
  }
  
  //terminate bit
  oscWrite(irPin, t);
  delayMicroseconds(t);
  delay(25);
  sends++;
  if(sends == 4)
  {
    delay(1000);
    sends = 0;
  }  
//  Serial.println("Signal transmitted..");
//  delay(10);
}

void oscWrite(int pin, int time)
{
  for(int i = 0; i < (time / 26) - 1; i++)
  {
    digitalWrite(pin, HIGH);
    delayMicroseconds(13);
    digitalWrite(pin, LOW);
    delayMicroseconds(13);
    //1000/KHz = total delay
  }
}

Code for dumping raw info from a remote (note: uses an IR-demodulator component connected to pin 12. Collects 135 samples, which is usually more than enough to cover one keypress):

int lastState;
unsigned long lastTimestamp;

char code[0];

int irPin = 12;
int tmp;

int data[135];
int i = 0;

void setup() 
{
  lastState = 1;
  pinMode(irPin, INPUT);
  Serial.begin(9600);
  Serial.println("---=== Ready ===---");
} 

void loop() 
{
  tmp = digitalRead(irPin);
  if(tmp != lastState)
  {
    lastState = tmp;
    if(tmp == 1)
    {
      data[i] = (micros() - lastTimestamp);
    }
    else
    {
      data[i] = -(micros() - lastTimestamp);
    }
    i++;
    lastTimestamp = micros();
  }

  if(i == 135)
  {
    int n;
    for(n = 0; n <= 135; n++)
    {
      Serial.print(data[n]);
      Serial.println();
      data[n] = 0;
    }
    i = 0;
  }
}

Not sure if you got your Panasonic code working or not, but I have a panasonic TV and will be working on this shortly. I haven't received my IR LEDs yet so I haven't been able to check any code or anything, but it seems your timing might be slightly off. I found a site that describes the Panasonic protocol, I'll have to post the link in another post since this message board won't let me insert a hyperlink until I've posted 1 "normal" message... >:(

the link for my previous post:
http://users.telenet.be/davshomepage/panacode.htm

Just an FYI, it's much easier to read a description of the protocol than it is to reverse engineer it.

If you do reverse engineer, it is helpful to graph it. I used Maple to graph it. I had a sketch output the points that could be pasted right in the plotting software.

Lastly, if you can find a library it is worth it. This stuff is trickier than it seems.

Dcept905:
I have seen that page already and tried said timings. Thing is, the header of that page is "Panasonic's old infrared remote protocol", and my tv isn't more than 3-4 years old.

With regards to finding a library.. I found this: http://www.arcfn.com/2009/08/multi-protocol-infrared-remote-library.html Ken Shirriff's IR-library. It doesn't support Panasonic though (just everything else DO'H!) so I used the raw output to verify the readings I got from my own hacked together raw-dump sketch.

Here's an example of codes picked up from my remote as well as the timings observed:

1: 01000000000001000000000100000000 0000100000001001
2: 01000000000001000000000100000000 1000100010001001
3: 01000000000001000000000100000000 0100100001001001

Header:
+ 3500 ns
- 1750 ns

0:
+450
-450

1:
+450
-1350

As I said, verified with both my own sketch and the linked library's raw output functionality. I'm starting to think my TV is just weird, that is uses toggle-bits and/or retransmission of the signal or something, because even when I use the linked library's raw receive and send functions, the TV in no way reacts.

Note, it's been a few months since I last worked in this. One thing that strikes me as odd is that when using the TV's remote, I have to press the buttons for a little bit before the TV reacts. There is a red light next to the receiver in the TV that blinks when it's getting an IR-signal, but just quickly clicking the remotes buttons doesn't trigger any behavior in the TV even though the light blinks.

Edit: Obviously, I have used a camera to verify that the IR-led is indeed transmitting something (as opposed to being inserted the wrong way, how stupid would that be! ::))

Hi
There is a link settled with hardware to be next.

:wink:http://www.cirrus.com/jp/pubs/proDatasheet/CS8130_F1.pdf
http://www.vishay.com/docs/81513/81513.pdf
HSDL-1000 pdf, HSDL-1000 Description, HSDL-1000 Datasheet, HSDL-1000 view ::: ALLDATASHEET :::