Arduino infrared problems

...snip...
I get the same measurement pattern for every button I push on a remote!

..snip..

For what its worth, I'd say what was happening was your circuit was pulling low by default, and measuring the peaks (not the troughs) of the transmission..

this picture would help to illistrate the point:

except for the header, the peaks will always be the same, its the troughs that will vary (signifying 1's and 0's)

David,

I just found your code, I think I am going to use it (maybe it needs some modification) for sending an RC5 signal to a Philips TV. I found a good description of RC5 here: infrared

My TV needs to have it's brightness and contrast adjusted sometimes. I don't have the original remote anymore and so far haven't been able to find a remote that has the button (off button) to leave the main menu. So, I hope to be able to do this by sending code 83 (menu off) from my Arduino with IR tx led and 220ohm resistor to the TV set.

If you think there something I should be aware of before starting with this, please let me know! Thanks in advance!

I will post here if I have results...

Edit: It seems that your code doesn't take care of sending the AGV, CHK and ADDRESS bits as shown in this picture (Philips way of working):

Hello,

i am searching for a RC5 control with the arduino.. but diddn´t find anything yet. Did you manage it ?

thanks jules

Thanks for the code! This was almost exactly what I was looking for to use my old sony VCR guts for my little project. Now I can switch between LED patterns wireless-ly.

I did have to change a wee bit though, so I thought I'd post back here with my tweak. I wanted the getIRKey() function to be run from an interrupt, but it seemed to be hanging the rest of the processing, and never quite getting to the loop(). It looked to me like the while loop at the beginning waited forever for input > 2200. I ended up changing that to "if ... < 2200, return -1", then testing for key values greater than 0 in a wrapper. The wrapper is called via the interrupt. So, here's a bit of the modified code;

int ir_pin = 2;              //Sensor pin wired through a 220 ohm resistor
int start_bit = 2000;   //Start bit threshold (Microseconds)
int bin_1 = 1000;       //Binary 1 threshold (Microseconds)
int bin_0 = 400;        //Binary 0 threshold (Microseconds)

volatile int key = 137;    // key 137 is "0", default to off

void setup() {
  pinMode(ir_pin, INPUT);
  attachInterrupt(0, setOpts, LOW);    // interrupt 0, run setOpts(), interrupt when ir_pin goes LOW
}

void loop() {
  //do pretty flashy lights here
}

void setOpts() {
  int tempKey = getIRKey();
  if(tempKey > 1){
    key = tempKey;
  }
}

int getIRKey() {
  int data[12];
  if(pulseIn(ir_pin, LOW) < 2200) { //Wait for a start bit
    return -1;
  }
  data[0] = pulseIn(ir_pin, LOW);      //Start measuring bits, I only want low pulses
  data[1] = pulseIn(ir_pin, LOW);
  data[2] = pulseIn(ir_pin, LOW);
  data[3] = pulseIn(ir_pin, LOW);
  data[4] = pulseIn(ir_pin, LOW);
  data[5] = pulseIn(ir_pin, LOW);
  data[6] = pulseIn(ir_pin, LOW);
  data[7] = pulseIn(ir_pin, LOW);
  data[8] = pulseIn(ir_pin, LOW);
  data[9] = pulseIn(ir_pin, LOW);
  data[10] = pulseIn(ir_pin, LOW);
  data[11] = pulseIn(ir_pin, LOW);

  for(int i=0;i<11;i++) {              //Parse them             
    if(data[i] > bin_1) {              //is it a 1?
      data[i] = 1;
    }  else {
      if(data[i] > bin_0) {            //is it a 0?
        data[i] = 0;
      } else {
       data[i] = 2;                    //Flag the data as invalid; I don't know what it is!
      }
    }
  }
  
  for(int i=0;i<11;i++) {              //Pre-check data for errors
    if(data[i] > 1) {                  
      return -1;                       //Return -1 on invalid data
    }
  }
  
  int result = 0;  
  int seed = 1;                                      
  for(int i=0;i<11;i++) {              //Convert bits to integer
    if(data[i] == 1) {
      result += seed;
    }
    seed = seed * 2;
  }
  return result;                       //Return key number
}

Seems to work for me, I can punch a button any time on the remote and it will reset the value of 'key', even in the middle of a delay(). I still have some work to go on it though, fiddling with my timings and such.

Thanks again.

David,
I just found your code, I think I am going to use it (maybe it needs some modification) for sending an RC5 signal to a Philips TV. I found a good description of RC5 here: infrared

Check my post on implementing an RC5 transmitter (seems like it should do what you want):

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

i tore the ir from a vcr ihad laying around and managed to get a reading from my rca remote, but im havin some trouble, the data isnt the same each time i hit a button on he remote, and i get 16 lines instead of the 11? any thoughts?

pmalmsten:
That is a fantastic piece of code to begin an IR Remote project with... I also started with the BOE Stamp kit and a few of those Panasonic sensors, then got bogged down with SX chips but today is my second day with the Arduino and your sketch (from your second code posting) just works perfectly.

I wonder... Has anyone has thought of creating a library based on it?

My project (for the last couple of years) has been to build a remote controlled attenuator so I can run the direct audio out of my TV/Media Center through an attenuator into the power amps. The attenuator uses a microcontroller (Arduino) to receive the remote control signal and set the volume up down + mute. I have the TI Burr Brown chips that attenuate and with this receiver code I now have a great foundation.

Thanks!

Is anyone still following this thread with interest in developing it further?

I've made some refinements to the code to make it a tiny bit more efficient and added code to the main loop for my needs but there are some other things I'm not so sure about like:

  • Should the code run as an interrupt instead of in the main loop
  • We've declared a start_bit variable of 2000 microseconds but it's never used
  • In the getIRKey() function we have a while(pulseIn(ir_pin, LOW) < 2200) loop that never does anything. It looks like it was set up for the start bit of the remote control's transmission and would have been used with the start_bit variable mentioned above.

Other than that, I wonder if we could extend the code to recognize other remotes and have a setup mode that would allow us to choose Sony, Hitachi, Panasonic, etc...

My version of the code only recognizes power, mute, volume up and volume down but could be extended to recognize other keys. At the moment it sends the state of the volume, mute and on/off to the serial monitor and saves those values to the EEPROM. My next step is to use shiftOut() to send values to a Texas Instruments PGA2311 stereo volume control chip as well as sending the values to a digital display of some sort.

Here's what I have so far:
(I moved all of the fixed variables into constants to reduce the footprint and use a loop to create the data[] array.)

/**
 * Receives signal from IR remote control on pin 2 and displays the function to the serial monitor.
 * Writes state of control to EEPROM for recall when power is turned off.
 * Reads state of control from EEPROM when power is turned on.
 */

#include <EEPROM.h>

//CONSTANTS
#define btnPower   149
#define btnMute    148
#define btnDown    147
#define btnUp      146
#define rate       4
#define start_bit  2000                //Start bit threshold (Microseconds)
#define bin_1      1000                //Binary 1 threshold (Microseconds)
#define bin_0      400                //Binary 0 threshold (Microseconds)
#define ir_pin     2                //Sensor pin 1 wired through a 220 ohm resistor
#define led_pin    13                //"Ready to Recieve" flag, not needed but nice
#define debug      0                 //Serial connection must be started to debug

//VARIABLES
int power  = EEPROM.read(0);  
int mute   = EEPROM.read(1);  
int volume = EEPROM.read(2);
int i      = 0;                    //general purpose counter 



void setup() {
  pinMode(led_pin, OUTPUT);          //This shows when we're ready to recieve
  pinMode(ir_pin, INPUT);
  digitalWrite(led_pin, LOW);          //not ready yet
  Serial.begin(9600);
  if(volume == 255) volume = 0;    //Don't start up at full volume
}

void loop() {
  //Fetch the key  
  int key = getIRKey();
  
  //Switch to print insructions to Serial Monitor
   switch (key) {     
    case btnPower:
      if(power == 0){
        power = 1;
        Serial.println("Turn On");
      } else {
        power = 0;
        Serial.println("Turn Off");
      }
      EEPROM.write(0, power);
      delay (500); //Debounce switch
      break;
      
    case btnMute:    
      if(mute == 0){
        mute = 1;
        Serial.println("Mute On");
      } else {
        mute = 0;
        Serial.println("Mute Off");
      }
      EEPROM.write(1, mute);
      delay (500); //Debounce switch
      break;
      
    case btnDown:
      if(volume > 0 && volume - rate > 0) {
        volume -= rate;
      } else {
        volume = 0;
      }
      Serial.print("Volume: ");
      Serial.println(volume);
      EEPROM.write(2, volume);
      break;   
      
    case btnUp:
      if(volume < 255 && volume + rate <= 255) {
        volume += rate;
      } else {
        volume = 255;
      }
      Serial.print("Volume: ");
      Serial.println(volume);
      EEPROM.write(2, volume);
      break; 
  }

}


int getIRKey() {
  int data[12];
  digitalWrite(led_pin, HIGH);         //Ok, i'm ready to recieve  
  
  while(pulseIn(ir_pin, LOW) < 2200) { 
    //Wait for a start bit
  }
  
  for(i=0;i<11;i++){
    data[i] = pulseIn(ir_pin, LOW);   //Start measuring bits, I only want low pulses
  }
  
  digitalWrite(led_pin, LOW);

  if(debug == 1) {
    Serial.println("-----");
  }
  
  for(int i=0;i<11;i++) {          //Parse them
    
    if (debug == 1) {
      Serial.println(data[i]);
    }
    
    if(data[i] > bin_1) {          //is it a 1?
      data[i] = 1;
    } else {
      if(data[i] > bin_0) {        //is it a 0?
        data[i] = 0;
      } else {
        data[i] = 2;              //Flag the data as invalid; I don't know what it is!
      }
    }
    
  }

  for(i=0;i<11;i++) {              //Pre-check data for errors
    if(data[i] > 1) {
      return -1;                //Return -1 on invalid data
    }
  }

  int result = 0;
  int seed = 1;
  for(int i=0;i<11;i++) {      //Convert bits to integer
    if(data[i] == 1) {
      result += seed;
    }
    seed = seed * 2;
  }
  return result;             //Return key number
}

THanks for sharing your code guys, I am working on a robot at the moment and am working on using my tv remote to control it (when its not controlling itself that is!)

David,

Thank you for the excellent piece of code. It worked well for me. If I might make a suggestion. When I connected O-scope to the send portion of the code I noticed that i was getting some intermittent gaps between the carrier frequency. It turns out they were some kind of interrupts perhaps from the UART. For a time sensitive code, I placed "noInterrupts()" before it sends out the whole string of data and placed an "interrupts()" after that portion of the code. After placing those functions I was able to recieve the data flawlessly. Thanks again for the great code :slight_smile:

My thanks for the code too!. It worked as-is: copied, compiled uploaded and done!. Works with sony remote, but Samsung remote returned 1 and -1... In fact it is too fast. I get 3 codes per button press. With a delay(50), I get one code per press.

Excellent piece of software, compact and clean.

Update: duh! It says "for sony remote" :slight_smile:

Hi,

I want to use the code inside a while loop. Because I have other code I would like the remote code to remain in a loop while there is still activity in the remote control (sort of to make it more efficient and maybe more responsive)

So my question is: what event would I use in the while loop? I thought of having an interrupt update a flag, but since there are many pulses, there would be many interrupts, and I only need one interrupt per sequence of pulses. Thought about reading HIGH on the input pin, but these are pulses and sometimes will be LOW. What else can I use?

Thanks.

glt: You want to run two different processes at the same time, is that is? One reading the remote, another doing whatever else needs to be done?

Sorry to say, as far as I know multithreading (multiple simultaneous processes) is not possible on the Atmega microcontrollers. It should still be doable though, having the arduino do two things fast enough for it to seem like they are happening at the same time.

What is the other process, other than monitoring the IR-signal, that you need the arduino to do?

If you are looking for a non-blocking IR receiver, I'm using the following code which works pretty well.

//  attachInterrupt(1,IR_ISR,FALLING);  // Add to setup()

#define ir_pin 3                       // IR sensor on pin 3 - ISR1

// key types returned from get_remote()
#define ENTER 12     
#define OK    21
#define POWER 22
#define UP    17
#define DOWN  18
#define RIGHT 19
#define LEFT  20
#define SLEEP 55
#define INFO  59

// vars for IR_ISR() (must be global)
volatile boolean IR_Avail;             // flag set if IR has been  read
volatile unsigned int IR_Return;       // returns IR code received
volatile unsigned long ir_mask;        // Loads variable ir_string 
volatile unsigned int ir_bit_seq;      // Records bit sequence in ir string
volatile unsigned int ir_string;       // Stores ir string

// Functions begin here . . . . . . .
void IR_ISR(){ // This ISR is called for EACH pulse of the IR sensor
  if(ir_bit_seq == 0){                 // it is the long start pulse
    for(int i = 0; i<20; i++){         // see if it lasts at least 2 ms
      delayMicroseconds(100); // 100
      if(digitalRead(ir_pin)) return;  // it's doesn't so get out (low active)
    }  
    ir_bit_seq = 1;                    // mark that the start pulse was received
    ir_mask = 1;                       // set up a mask for the next bits
    return;
  }

  delayMicroseconds(900);              // wait 900 us and test 
  if(!digitalRead(ir_pin)) ir_string = ir_string | ir_mask;  // Stores 1 in bit
  ir_mask = ir_mask << 1;              // shifts ir_mask by one to the left
  ir_bit_seq++;                        // ir_bit_seq is incrimented by one
  if(ir_bit_seq == 12){                // after remote sends 12 bits it's done
    ir_mask = 63;                     // only want the last 6 bits - the command
    ir_string = ir_string & ir_mask;  // only keep last 6 bits
    IR_Return = ir_string;             // final result
    IR_Avail = true;                   // indicate new command received
    //digitalWrite(led_pin,HIGH);
    ir_bit_seq = 0;                    // clean up
    ir_string = 0;
    ir_mask = 0;
    for(int i = 0; i<25; i++){         // delay to stop repeat 10ms / loop ~250ms about right
      delayMicroseconds(10000);        // 16383 is maximum value so need to repeat
    }
  }
}

I wish I could properly credit the guy who posted it originally - it was in Exhibition a few months ago. I made some minor mods to it.

Perhaps it will help.
[edit] Then use something like
if(IR_Avail){
. . .
}

and check IR_Return
[/edit]

I guess I wasn't too clear in my question, but BroHogan answered it. I need a non blocking IR receiver because the code in this thread just sits there and waits for a low pulse. But I like this code very much, can it be made not to block?

Thanks.

The sample I gave you is for a Sony remote BTW.

I've tried making the code in this thread non-blocking by using an ISR on the IR pin. I never had much luck. Maybe some smarter guy can make it work.

What I found is that once the ISR triggers you have to wait and process each pulse using pulsein(). If you get noise, the ISR can hang.

The code I posted doesn't work that way. Each pulse triggers the ISR and the function does not use pulsein() to wait on the pulse.

Again, maybe someone can get GetIRKey working non-blocking. I liked it too - it's a classic. However, I'm pretty satisfied with the code I posted. YMMV

I tried the following to make the code not block:

Interrupt when a pulse is detected. The ISR sets a flag and disables interrupts. The while loop sees that flat is set and calls getIRKey(). After printing, it enables interrupts.

It works but sometimes it gets stuck in the while loop waiting for the long pulse. disabling interrupts inside the ISR doesn't seem to disable interrupts. I can get out of the while loop by pressing the remote. But I can't think of a way to time out the while loop...

I figured I get stuck in the while loop waiting for the long pulse, so even if it times out, the statement remains true (<2200). So I need a way to get out of the loop when it times out, something like <2200&&!=0 but this did not work...

I'll try

int longpulse=1;
while((longpulse=pulseIn(ir_pin))<2200)
{
if longpulse=0 //timeout
{
break;
}
}

Well I modified the code to work as non-blocking. It uses an interrupt service routine to set a flag.

When it times out because there are no long pulses, it takes 12 seconds to get out of the routine. This can be greatly optimized by specifying the timeout in the pulseIn calls, or using an if statement to skip them all.

Any suggestions to improve the code is appreciated.

One more thing: I removed the resistor from the IR receiver to pin2 and enabled the pull up resistor for pin2. This is how it is specified in the data sheet. The IR receiver is effectively a switch to Gnd when IR is detected and thus you need the pull-up resistor. (The data sheet says that the pull up is optional, but since it is there, I enabled it)

Here is the code:

#define ir_pin 2      //Sensor pin 1 wired through a 220 ohm resistor#define led_pin 13      

int debug = 0;            //Serial connection must be started to debug
int start_bit = 2000;      //Start bit threshold (Microseconds)
int bin_1 = 1000;      //Binary 1 threshold (Microseconds)
int bin_0 = 400;      //Binary 0 threshold (Microseconds)
int longpulse=1;        //If longpulse==0 it means pulseIn() timed out

volatile byte remoteOn = 0;  // ==1, means remote has been pressed

void setup() {
  // pinMode(led_pin, OUTPUT);      //No using
  pinMode(ir_pin, INPUT);
  digitalWrite(ir_pin, HIGH);      //We are not using a resistor in IR detector. Pull up
  pinMode(6, INPUT);            //Testing a button on pin 6 ensures remote code did not block
  digitalWrite(6, HIGH);        //Enable pull up for button. Button is a switch to GND
  Serial.begin(9600);
}

void loop() {
  while (remoteOn==1)
  {
    int key = getIRKey();                //Fetch the key
    Serial.print("Key Recieved: ");
    Serial.println(key);
    remoteOn=0;  //reset flag
    longpulse=1;  //reset longpulse
  }
  // The following code is test code to see if remote code did not block
  if (digitalRead(6)==0)
  {
    Serial.println("Button Pressed");
    delay (100);
  }
  attachInterrupt (0, remoting, RISING);
}

int getIRKey() {
  int data[12];
  while((longpulse=pulseIn(ir_pin, LOW)) < 2200)
  {
    if(longpulse==0) // if timed out
    {
      break;
    }
  }
  
  data[0] = pulseIn(ir_pin, LOW);      //Start measuring bits, I only want low pulses
  data[1] = pulseIn(ir_pin, LOW);
  data[2] = pulseIn(ir_pin, LOW);
  data[3] = pulseIn(ir_pin, LOW);
  data[4] = pulseIn(ir_pin, LOW);
  data[5] = pulseIn(ir_pin, LOW);
  data[6] = pulseIn(ir_pin, LOW);
  data[7] = pulseIn(ir_pin, LOW);
  data[8] = pulseIn(ir_pin, LOW);
  data[9] = pulseIn(ir_pin, LOW);
  data[10] = pulseIn(ir_pin, LOW);
  data[11] = pulseIn(ir_pin, LOW);

  delay(50); // to slow down the loop if needed

  if(debug == 1) {
    Serial.println("-----");
  }
  for(int i=0;i<11;i++) {              //Parse them
    if (debug == 1) {
        Serial.println(data[i]);
    }
    if(data[i] > bin_1) {              //is it a 1?
      data[i] = 1;
    }  else {
      if(data[i] > bin_0) {            //is it a 0?
        data[i] = 0;
      } else {
       data[i] = 2;                    //Flag the data as invalid; I don't know what it is!
      }
    }
  }

  for(int i=0;i<11;i++) {              //Pre-check data for errors
    if(data[i] > 1) {
      return -1;                       //Return -1 on invalid data
    }
  }

  int result = 0;
  int seed = 1;
  for(int i=0;i<11;i++) {              //Convert bits to integer
    if(data[i] == 1) {
      result += seed;
    }
    seed = seed * 2;
  }
  return result;                       //Return key number 
}
 
void remoting()  // The ISR
{
  remoteOn=1;
}

Hello,

A little more general question how to send infrared commands to a DVD player? In other words how to blink the IR-LED propperly :)?

Regards,

Hannes