Go Down

Topic: Arduino infrared problems (Read 93198 times) previous topic - next topic

richardw347

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!)

Nauge

#16
Apr 13, 2009, 10:02 pm Last Edit: Apr 13, 2009, 10:03 pm by Nauge Reason: 1
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 :)

glt

#17
Apr 20, 2009, 04:53 am Last Edit: Apr 20, 2009, 05:32 am by glt Reason: 1
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" :-)

glt

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.

PlastBox

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?

BroHogan

#20
Apr 22, 2009, 12:06 am Last Edit: Apr 22, 2009, 12:12 am by BroHogan Reason: 1
If you are looking for a non-blocking IR receiver, I'm using the following code which works pretty well.
Code: [Select]
//  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]

"Data is not information, information is not knowledge, knowledge is not understanding, understanding is not wisdom."
~ Clifford Stoll

glt

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.

BroHogan

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
"Data is not information, information is not knowledge, knowledge is not understanding, understanding is not wisdom."
~ Clifford Stoll

glt

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...


glt

#24
Apr 22, 2009, 04:30 pm Last Edit: Apr 22, 2009, 07:41 pm by glt Reason: 1
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;
 }
}

glt

#25
Apr 23, 2009, 06:49 am Last Edit: Apr 23, 2009, 07:06 am by glt Reason: 1
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:

Code: [Select]

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



nighto

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

Things

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:

Code: [Select]
// 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:
Code: [Select]
-----
543
544
541
537
537
537
543
543
544
543
538
537
-----
544
544
538
538
538
544
544
544
538
537
538
538

Code: [Select]
-----
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 :)



jrfitzny

#28
Apr 08, 2010, 08:12 pm Last Edit: Apr 08, 2010, 10:53 pm by jrfitzny Reason: 1
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?

Jeremyvnc

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!

Go Up