Arduino infrared problems

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

I have a panasonic remote here, and the power button seems to output 2 sets of data, but the problem is, I don't understand what its doing. The values seem to change each time I press the button :o

This is the code I am using:

// 0.1 by pmalmsten http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1176098434
// 0.2 by farkinga
// 0.3 by farkinga - adds cool behaviors

#define IR_BIT_LENGTH 12    // number of bits sent by IR remote
#define BIT_1 1000       // Binary 1 threshold (Microseconds)
#define BIT_0 400           // Binary 0 threshold (Microseconds)
#define BIT_START 2000      // Start bit threshold (Microseconds)

#define IR_PIN 7            // Sensor pin 1 wired through a 220 ohm resistor
#define LED_PIN 10           // first LED output
#define POWER_PIN 11        // second LED output, corresponds to power button

#define DEBUG 1             // Serial connection must be started to debug

int runtime_debug = 0;      // flag to output raw IR pulse data
int output_key = 0;         // flag to print decoded key integers
int power_button = 0;       // flag to indicate if power LED is on
int power_level = 128;      // value (0-255) for power LED intensity

void setup() {
  pinMode(LED_PIN, OUTPUT);      //This shows when we're ready to recieve
  pinMode(POWER_PIN, OUTPUT);      //This is the "power on" indicator
  pinMode(IR_PIN, INPUT);
  digitalWrite(LED_PIN, LOW);
  Serial.begin(9600);
}

void loop() {
  digitalWrite(LED_PIN, HIGH);
  int key = get_ir_key();
  
  digitalWrite(LED_PIN, LOW);  // turn LED off while processing response
  do_response(key);
  delay(100);                  // short delay to cancel duplicate keypresses
}

/*
  wait for a keypress from the IR remote, and return the
  integer mapping of that key (e.g. power button on remote returns 
  the integer 1429)
*/

int get_ir_key() 
{
  int pulse[IR_BIT_LENGTH];
  int bits[IR_BIT_LENGTH];  

  do {} //Wait for a start bit
  while(pulseIn(IR_PIN, LOW) < BIT_START);

  read_pulse(pulse, IR_BIT_LENGTH);
  pulse_to_bits(pulse, bits, IR_BIT_LENGTH);
  return bits_to_int(bits, IR_BIT_LENGTH);
}

/* 
  respond to specific remote-control keys with different behaviors
*/

void do_response(int key)
{  
  switch (key)
  {
    case 1437:  // record button
      Serial.println("toggle debug pulse");
      runtime_debug = 1 - runtime_debug;
      break;
    case 1498:  // display button
      Serial.println("Toggle key output");
      output_key = 1 - output_key;
      break;
    case 1429:  // power button
      Serial.println("Power");
      power_button = 1 - power_button;
      set_power();
      break;
    case 1424:  // channel up button
      Serial.println("Channel Up");
      break;      
    case 1425:  // channel down button
      Serial.println("Channel Down");
      break;
    case 3342:  // up rocker/pause
      power_level+=1;
      set_power();
      break;
    case 3343:  // down rocker/stop
      power_level-=1;
      set_power();
      break;      
    case 3344:  // left rocker/rewind
      if (power_level < 50)
      {
        power_level-=3;
      }
      else
      {
        power_level-=10;
      }
      set_power();
      break;
    case 3345:  // right rocker/fast forward
      if (power_level < 50)
      {
        power_level+=3;
      }
      else
      {
        power_level+=10;
      }
      set_power();
      break;
    case 3352:  // play button
      blip_power();
      break;      
    default:
      if (output_key)
      {
        Serial.print("Key ");
        Serial.print(key);
        Serial.println(" not programmed");
      }
      break;
  }
}

/*
  use pulseIn to receive IR pulses from the remote.
  Record the length of these pulses (in ms) in an array
*/

void read_pulse(int pulse[], int num_bits)
{
  for (int i = 0; i < num_bits; i++)
  {
    pulse[i] = pulseIn(IR_PIN, LOW);
  }
}

/*
  IR pulses encode binary "0" as a short pulse, and binary "1"
  as a long pulse.  Given an array containing pulse lengths,
  convert this to an array containing binary values
*/

void pulse_to_bits(int pulse[], int bits[], int num_bits)
{
  if (DEBUG || runtime_debug) { Serial.println("-----"); }
  
  for(int i = 0; i < num_bits ; i++) 
  {
    if (DEBUG || runtime_debug) { Serial.println(pulse[i]); }
    
    if(pulse[i] > BIT_1) //is it a 1?
    {
      bits[i] = 1;
    }  
    else if(pulse[i] > BIT_0) //is it a 0?
    {
      bits[i] = 0;
    } 
    else //data is invalid...
    {
      Serial.println("Error");
    }
  }
}

/*
  convert an array of binary values to a single base-10 integer
*/

int bits_to_int(int bits[], int num_bits)
{
  int result = 0;
  int seed = 1;
  
  //Convert bits to integer
  for(int i = 0 ; i < num_bits ; i++) 
  {              
    if(bits[i] == 1) 
    {
      result += seed;
    }
    
    seed *= 2;
  }
  
  return result;
}

/*
  set the brightness of the "power LED" depending on the power_level
  variable.
*/

void set_power()
{
  power_level = constrain(power_level, 0, 200);
  
  analogWrite(POWER_PIN, power_level * power_button);
  
  // if the power level is above the max or below the min...
  if ((power_level == 200) || (power_level == 0))
  {
    blink_led();
  }
}

/* 
  make LED blink rapidly
*/

void blink_led()
{
  for (int i = 0; i < 5; i++)
  {
    analogWrite(LED_PIN, 0);
    delay(50);
    analogWrite(LED_PIN, 1);
    delay(50);
  }
}

/*
  neat little routine to fade both LEDs in a sequence.  Currently,
  this is called by do_response() when the "play" button is pressed.
*/

void blip_power()
{
  int max_val = 100;
  
  for (int i = 0; i < max_val; i++)
  {
    analogWrite(POWER_PIN, max_val-i);
    analogWrite(LED_PIN, i);
    delay(15);
  }

  for (int i = max_val; i >= 0; i--)
  {
    analogWrite(POWER_PIN, max_val-i);
    analogWrite(LED_PIN, i);
    delay(15);
  }

  set_power();  
}

And I get a few different results from the power button on my remote:

-----
543
544
541
537
537
537
543
543
544
543
538
537
-----
544
544
538
538
538
544
544
544
538
537
538
538
-----
538
538
539
544
543
544
538
537
538
538
544
544
-----
538
538
545
544
538
538
538
538
545
544
544
538

Any idea's how I can make it work with my remote?

Thanks :slight_smile:

I am successfully getting the remote to output a value for each button I press; however, I need to use the output to change the output PWM voltage.

For example: The PWM command for motor control is:
int motor_input = # //the number being a value from 0-255

How do I get the IR output value to change the # in the motor input command?

Use something like this:
motor_input = valueFromIR/MAX_IR_VALUE_POSSIBLE * 255; //scales ir value to 0-255
analogWrite(PWM_OUTPUT,motor_input); uses PWM of Arduino to change motor speed.

Caution: Motors connected directly to the arduino should be very small!

I'm actually using the arduino to drive a MOSFET, which which control the motor. Thanks for the help!

I'm using pmalmsten's code on pg 1 of this thread.

I added the following to the end; it uploads, but doesn't output any PWM voltage

int motor_pin = 10; // define the pin which will send the motor commands
int motor_input = result;

void setup ();

pinMode (motor_pin, OUTPUT);

void loop ();

analogWrite(motor_pin,motor_input);} //uses PWM of Arduino to change motor speed.

Even if I change int_motor pin = (to a value 0-255) it still doesn't output a voltage. Any idea what I'm doing wrong?

If you Serial.println(motor_input);
what value do you get?

Have you tried using an LED+resistor in place of the MOSFET to debug it?
Does the LED come on?

GB

I tried Serial.PrintIn(motor_input)....the serial monitor is only reading the IR values

which shows Key Received: (number from 219-500+); depends on what button I'm pressing.

I'm actually trying the LED + resistor, but it's not coming on. I'll just insert the code, and if you could please troubleshoot it, i'd be in your debt. I put the //////////////// where the IR programming stops and the PWM programming begins

int ir_pin = 2;                        //Sensor pin 1 wired through a 220 ohm resistor
int led_pin = 13;                      //"Ready to Recieve" flag, not needed but nice
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)


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);
}

void loop() {
  int key = getIRKey();                //Fetch the key
  Serial.print("Key Recieved: ");
  Serial.println(key);
}


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
  }
  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);
  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(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



/////////////////////////////////////////////////////////////////////
 
 int motor_pin = 10;  // define the pin which will send the motor commands
 int motor_input = result;
 
 
 void setup ();

  
 pinMode (motor_pin, OUTPUT);
 
 void loop ();

 analogWrite(motor_pin,motor_input); //uses PWM of Arduino to change motor speed.
 
 Serial.println(motor_input);
}

jrfitzny - sorry for the slow response, the email alert got lost in some other stuff.

Is this exactly the code, or have you changed it before posting it here?

Nothing after the ///////////////////////// will be run.
Is that what you meant to happen?

You might want to make a copy of the code, and then trim out the stuff you don't want. It's a bit confusing to decide whether there is a mistake, and it should be executed or not.

Have I misunderstood something?

The only code I can see which would control the motor is after the return statement, so it will never get done.

  return result;                       //Return key number



/////////////////////////////////////////////////////////////////////

 int motor_pin = 10;  // define the pin which will send the motor commands
 int motor_input = result;


 void setup ();

  
 pinMode (motor_pin, OUTPUT);

 void loop ();

 analogWrite(motor_pin,motor_input); //uses PWM of Arduino to change motor speed.

 Serial.println(motor_input);

I suggest tidy up the code, and take all the code after return, delete what you dont want, and put the rest wherever it should be.

You almost certainly don't want this code in getIRKey

 void setup ();

  
 pinMode (motor_pin, OUTPUT);

 void loop ();

pinMode should be set in the setup function.

HTH
GB

Gbulmer,

Did exactly what you said...code now works like a charm!
Thanks for the help :slight_smile:

I am using TSOP 1838 IR receiver sensor to receive the signal,
surprisingly it gives readings with mega8 mcu, but it dosent work
with mega 328 or 168. however it dosent show any error while uploading the code. need support desperatly . . . . . :-?

int ir_pin = 2;    //Sensor pin 1, the data pin
unsigned long data[12];

void setup() {

  pinMode(ir_pin, INPUT);
   Serial.begin(9600);
}

void loop() {
  data[0] = pulseIn(ir_pin, LOW);    //Start measuring bits --the sensor defaults high, goes low on infrared
  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);
  

  Serial.println("-----");    //Send them all
  Serial.println(data[0]);
  Serial.println(data[1]);
  Serial.println(data[2]);
  Serial.println(data[3]);
  Serial.println(data[4]);
  Serial.println(data[5]);
  Serial.println(data[6]);
  Serial.println(data[7]);
  Serial.println(data[8]);
  Serial.println(data[9]);
  Serial.println(data[10]);
  Serial.println(data[11]);
  
}