I recently came across the need to read some IR signals from my universal remote. Having hacked around with IR in the past, I had an idea of what I was looking for so I stopped by RadioShack and found one of these:
A Brief Overview of IR
The idea behind transmission of data over infrared is dirt simple. Reading about it just reminds me of how overly-complicated some communication protocols have become. The idea is that you have a transmitter, which is just an LED that emits light in the infrared spectrum and you have a receiver, which is just a phototransistor that changes the current flowing through it based on how much infrared light is hitting it. The transmitter pulses out light and the receiver is able to ‘sense’ those pulses in the exact condition they were in when they left the other circuit. All information is sent in a binary format. In most protocols, you have two different types of pulses, a long and a short. The long usually corresponds to 1 and the short to 0. Here is a diagram of Sony’s IR protocol:
Here you can see that the 1 corresponds to a 1.2 millisecond pulse and the 0 a 0.6 millisecond pulse. So, there is one more detail that slightly complicates the issue. An astute reader may note:
“IR light is everywhere. How does the receiver know the difference between a pulse and noise or ambient light?”
The light coming from the transmitter is modulated at a very high frequency, usually between 38kHz and 40 kHz. It is then demodulated or integrated on the other end and turned into a solid waveform. Think of it like a high pass filter, which it may actually be in implementation, I didn’t look it up. Any naturally occurring IR light is likely to be changing at a low frequency, so this gives us a way to distinguish the signal from the noise. On top of that, many commercial products cover their receiver with a piece of plastic or lens that is coated in a material that only lets in infrared light. This helps further eliminate noise that bright visible light may cause.
The Radioshack module
This thing was extremely easy to hook up. All you need to do is supply a resistor (I used 200 Ohms, with a 3.3V supply) to the power line, GND it, then connect the data line to one of your digital pins (I used pin #2). The module itself contains the demodulation circuit so what you see is a solid signal.
At this point, I decided to hook up my oscilloscope to see what was coming out of this thing. I programmed a free channel on my universal remote for a Sony DVD and started hitting some buttons. This is generally what I saw:
This signal was upside-down from what I had expected. The data pin appears to be idling HIGH and the signal drives it LOW. I verified this on my multimeter just to be sure. After this, I looked it up online and it appears to be the expected behavior. Not really a problem for our software, a pulse is a pulse, it is just a need to know detail for later. Another note, that first long low pulse is what is often called a start bit. It is an extra long, 2.4 millisecond pulse to notify the receiver that some bytes are coming. To read more about the protocol, take a look at this link: http://www.sbprojects.com/knowledge/ir/sirc.htm.
This is usually the point where I go online to see what others have done with the device and see if there is any code I can steal. I looked around for a bit and found this link and this code:
// 0.1 by pmalmsten // 0.2 by farkinga #define IR_BIT_LENGTH 12 #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 DEBUG 0 //Serial connection must be started to debug #define IR_PIN 7 //Sensor pin 1 wired through a 220 ohm resistor #define LED_PIN 9 //"Ready to Recieve" flag, not needed but nice #define POWER_PIN 11 // the red LED that indicates if the power button is pressed. int runtime_debug = 0; int output_key = 0; int power_button = 0; 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); //not ready yet Serial.begin(9600); } void loop() { digitalWrite(LED_PIN, HIGH); //Ok, i'm ready to recieve int key = get_ir_key(); //Fetch the key digitalWrite(LED_PIN, LOW); do_response(key); delay(200); } void do_response(int key) { switch (key) { case 1437: Serial.println("toggle debug pulse"); runtime_debug = 1 - runtime_debug; break; case 1498: Serial.println("Toggle key output"); output_key = 1 - output_key; break; case 1429: Serial.println("Power"); power_button = 1 - power_button; if (power_button) { digitalWrite(POWER_PIN, HIGH); } else { digitalWrite(POWER_PIN, LOW); } break; case 1424: Serial.println("Channel Up"); break; case 1425: Serial.println("Channel Down"); break; default: if (output_key) { Serial.print("Key "); Serial.print(key); Serial.println(" not programmed"); } break; } } void read_pulse(int data[], int num_bits) { for (int i = 0; i < num_bits; i++) { data[i] = pulseIn(IR_PIN, LOW); } } 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"); } } } 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; } 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); }
This seemed to me like a lot of code and logic for such a simple protocol. I could go on about how wasteful I think the code is but honestly, it works and that is what is important. So, I don’t want to make it sound like it is a ‘bad’ implementation. His is actually meant to be general and to debug different protocols. I just want to make it simpler for the processor and the human reader to understand and optimized for this one particular protocol. Here is my implementation, which itself may be more flaky:
#define BITS_PER_MESSAGE 12 #define B_1 1000 //1.2 milliseconds #define B_0 400 //0.6 milliseconds #define START_BIT 2000 //making it a little shorter than the actual 2400 #define IR_PIN 2 //find the threshold time b/w the two pulse sizes #define BIT_THRESH B_0+((B_1-B_0)/2) void setup() { pinMode(IR_PIN, INPUT); delay(1000); Serial.begin(9600); Serial.println("Ready..."); } void loop() { //int where we are storing code, must start at 0 unsigned int code = 0x00; Serial.println("Waiting for first pulse"); //block until start pulse while (pulseIn(IR_PIN, LOW) < START_BIT) {} //read in next 12 bits shifting and masking them into the unsigned int 'code' //remeber boolean is basically 0 [false] or 1 [true] for (byte i = 0; i < BITS_PER_MESSAGE; i++) { code |= (pulseIn(IR_PIN, LOW) > BIT_THRESH) << i; } Serial.println(code); delay(500); }
My general idea was this, calculate a threshold, the midpoint between the 2 bit pulse sizes, and compare every incoming pulse to that. So in this situation, BIT_THRESH is 700. First thing we do is create an unsigned int to store the code. Remember, an integer is 16 bits so it has more than enough space to store the 12 bits of the message. I just made it unsigned just in case but it is not likely that we will reach all the way to the sign bit on the MSB side. The next thing we do in the algorithm is wait for the start bit:
while (pulseIn(IR_PIN, LOW) < START_BIT) {}
It is set to 2000 instead of 2400 so we have a little bit of leeway. You can increase it if you start getting noise but it is not likely to happen. This is just an empty while loop or a blocking conditional. It just holds the program until it’s condition is met. In this case, we find a pulse longer than START_BIT. PulseIn is a really nice function if you aren’t familiar. The first parameter is the pin you are waiting on, and the next parameter describes how the pulse will come. From the reference:
if value is HIGH, pulseIn() waits for the pin to go HIGH, starts timing, then waits for the pin to go LOW and stops timing
Remember that our signal is inverted, so we are waiting for the pin to go LOW. Our pulse is defined as a LOW then HIGH trough. That is why we chose LOW here. Once we get our start bit, we perform the meat of the logic:
for (byte i = 0; i < BITS_PER_MESSAGE; i++) { code |= (pulseIn(IR_PIN, LOW) > BIT_THRESH) << i; }
This one is full of microcontroller jargon but shouldn’t be too hard to figure out. We are going through 12 steps, one for each bit, and calling pulseIn to get the pulse length then comparing that length to BIT_THRESH. That is going to give us a true or false which is in actuality a 1 or 0. After we get a result, we shift the 1 or 0 over by i bits and perform a bitwise OR to set the corresponding bit in the code variable. See here for an explanation of bit logic. So when the result is 0, it effectively skips the number. When it is 1, it sets bit i of the code to 1. This is relying on the fact that the code starts out as all 0 at first. Now that I think about it, it may be more efficient to do something like this:
for (byte i = 0; i < BITS_PER_MESSAGE; i++) { if (pulseIn(IR_PIN, LOW) > BIT_THRESH) code |= (1 << i); }
That way you only do the bitwise OR when you encounter a 1. Honestly consistency is much more important than efficiency in this situation so do whatever works for you. Also, the performance would be completely unnoticeable.
Technically, depending on the protocol, that integer may contain different control messages, devices, etc. But if you are just trying to map a button on your remote to an action in arduino, this should be good enough. If you are concerned about other people using your code with different remotes for different devices, I suggest further reading.
One last thing to note, I just realized that the B_1 and B_0 values don’t match with what the Sony specification says. I just took those values from that guy’s original code and didn’t think about it. It still works so I am not changing it without testing. Just a head’s up.
Hope this helps someone. I will address any questions in the comments section.




13 Comments
thanks for this!
i tested it with a regular ir transistor (no demodulation).. and that also works if you change the settings like so.
#define B_1 900
#define B_0 1400
#define START_BIT 2000
and turned on the pullup tesistor on pin 2.
i used a sony rm-ed009 remote.
This is a beautifully presented explanation (oscilloscope picture especially appreciated). Thanks. . . . I’ve been running arduinos for awhile with a cheap sony remote control, and these little ir receivers really work well.
Thank you for a really well done tutorial/explanation
.
@gijs
Thanks for sharing the results of your experiment. That actually makes sense I think… It is maybe just accumulating that waveform on its own. I would try shining a flashlight at it while testing to see if that interrupts the signal just out of curiosity
@Steve and Jonah
Thanks for the encouraging words
Hi, I am trying to build something as a final project for college, and I’m getting held up without any idea of what to do. I think you did a really great job of explaining this, and I was hoping you had an answer to my issue. I’m trying to build a learning remote for channels up, down, and power. All it has to do is take in an IR signal and store it to be able to send it back out. You wrote out how to take a signal and interpret it, is there a way to tweak this to store and resend the signal? This is my first time ever using a microcontroller.
Thanks
@Kapil
If you have never used a microcontroller before there is some learninng to do. Sorry to sound rude, but I am not really too sure if you have a specific question or if you just want me to do your homework for you. If you need guidance on programming and using microcontrollers, you may want to buy an Arduino (if you don’t already have one) and some simple books on electronics. I would suggest a Forrest Mims book. Also, the Arduino site is a good place to start learning. Read the reference, trouble shoot on the forum.
If you are still willing to finish your project, check this project: http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
but if you give up and don’t think it is worth all the time, you could go to radioshack and get a universal remote. It does exactly what you explained.
Here is a video where i use the script in my synth (-:
Great article. How do you like the Parallax USB scope? I have been thinking about getting a USB scope to compliment by conventional one which doesn’t have any capture capability.
For IR sensors this has been the best site, THANKS! I do have a couple questions though. So you know my experience level, I’ve taken the C programming class for engineers and I’ve taken a 1 unit instrumentation and measurement class for mechanical engineers (using a parallax board to control motors and sensors). Im working this summer on converting the measurement class from basic stamp over to Arduino for an independent study. Im short 1 unit on the programming requirements. (quarter to semester transfer) I’ve been able to use LEDs, switches, photo resistor inputs, and motors (dc, stepper, servo with transistors, relays, diodes, and an h bridge) with the Arduino.
So now that you know my skill level, here is where I’m at:
1) Im not asking for any full programs (seeing as this is an independent study) just a line or two to help guide me.
2) I am writing a program to send out a range of frequencies b/t 34000 and 38000 to determine the distance of an object. A read of 34000 = object close to emitter and receiver, and a read of 38000 = object far from emitter and receiver. I had no problem doing this on a parallax board. But here is my delimmas using Arduino:
A) I cant find any code out there to at least guide me on properly emitting out of a transmitter (there is a ton about reading in a signal from a remote but I don’t need to do this). Currently, Im using the tone command right now and can see that Im sending out signals by looking through my camera phone and it is working, but is this a reliable way to emit a signal?
B) I am using a Radioshack ir receiver 276-640 and its not receiving any of my tone pulses. Im using an LED to determine if the input is high or low and it is reading that it is always high (no input). I’ve played with the delays b/t output signal and input read, and the duration of tone pulses (tone(pin, frequency, duration)) and still not getting any signal.
Heres my latest code:
const int ledPin2 = 2;
const int irSensor = 9;
const int delaytime = 100;
int irLed = 3;
int sensorstate = 0;
void setup(){
pinMode(ledPin2, OUTPUT);
pinMode(irSensor, INPUT);
pinMode(irLed, OUTPUT);
}
void loop(){
tone(irLed, 38000, 50);
delay(delaytime);
sensorstate = digitalRead(irSensor);
delay(100);
if (sensorstate == HIGH){
digitalWrite(ledPin2, HIGH);
delay(delaytime);
}
if (sensorstate == LOW){
digitalWrite(ledPin2, LOW);
delay(delaytime);
}
}
(yes delaytime = 100 is to long but Ive tried many other delays and still nothing)
question 2 is why cant I read an input signal? My wiring is correct though Im positive.
C) I understand that there is noise floating around, but generally, what is the best way to eliminate it?
D) This one has got me to the point of typing this msg. I’ve spent roughly 85 hours so far with the Arduino board and I have digitally and analytically read inputs with ease. Now all of a sudden with the program posted above I am receiving some crazy inputs. First off, I think I may have fried my board due to me soldering a wire onto my old IR receiver and fusing the insides together. One of the leads on the IR receiver broke so I soldered a wire to it. When I plugged it into the board, the tiny chip (relay Im assuming, I dont know) next to the USB B female jack got EXTREMELY HOT real fast. Yes, a short. This is how I know my wiring is now correct b/c when I played with the wiring for my soldered IR receiver every configuration made that little chip get REALLY HOT, so I did a lot of online research to make sure I was correct. The little chip has gotten really hot roughy 5 times.
I’ve sense replaced my ir receiver with the Radioshack 276-640.
But now (back to the crazy inputs) Im experiencing something really goofy. After a few hours of tickering around with wires I have found that my pins are acting crazy. For example, say I have switch cases for digitalRead(pin9, HIGH) or digitalRead(pin9, LOW), I can have a wire plugged into pin 9 (which is pin9) and nothing else (The other end just floating aimlessly into the air). Yet, when run the program and I move the wire around (I found this out by accident of course) my LED light that displays HIGH or LOW flickers. Sometimes the read is HIGH, sometimes the read is LOW. What is going on?!!! Is the wire receiving ambient signals alone?? Ive repeated this process on another pin (pin
and the same thing happens. Have you ever heard of a pin receiving input just through a wire alone or is my board shot? This is why I brought up the question about noise earlier, am I getting noise? Or again, is my board fried?
Besides this independent study I’m currently doing, I have thought of many ideas I could use the IR transmitter and receiver for and I would be very grateful for your input.
Smily not supposed to be there, it was supposed to say, (“pin 8″)
When I stated “delaytime = 100 is to long” I wasn’t speaking about the correct delay. I meant to say “the tone duration = 50 seemed to long for me because I’m spending unneeded excess time on the pulseout but I’ve tried other delays and still nothing”.
@Ted
Sorry for the late response. I can’t actually closely read this huge comment as I don’t have the time or incentives but I can try to help a little:
I am not sure you have your physics correct. It sounds like you are trying to build a distance sensor by measuring the change in frequency of light? I can’t tell what you are talking about? My advice, if you are actually trying to measure the relative distance of an object, is best to get a sensor designed to do so:
http://www.acroname.com/robotics/info/articles/sharp/sharp.html
There are plenty of examples online on how to use these with Arduinos.
As for your circuit problems. Noise control depends on the noise’s source. It could be the power supply, in which case you should use decoupling capacitors. Or it could be from the signal itself, in which some kind of RC filter could be used:
http://en.wikipedia.org/wiki/RC_circuit
You mentioned a pin floating when trying to read it. This is sometimes caused by not setting the pinMode correctly http://arduino.cc/en/Reference/PinMode ? Who knows though.
With the rest of your problems, I can’t help you. There is only so much circuit debugging I can do through reading a comment in a blog post.
One more piece of advice, if you want help from an engineer or programmer, NEVER mention that it is for school!! We have a bad history with people asking us to do their homework o_0
Good Luck
Hi
I am trying for a device to recieve and reproduce the same ir signals of frequency and signal sequnce known. So i think i will have to use a tuner with demodulator.Can you enlighten me?
Regards,
Gobind Gopal
India
5 Trackbacks
[...] Radioshack Infrared receiver Arduino project. Ben writes – This thing was extremely easy to hook up. All you need to do is supply a resistor (I used 200 Ohms, with a 3.3V supply) to the power line, GND it, then connect the data line to one of your digital pins (I used pin #2). The module itself contains the demodulation circuit so what you see is a solid signal. At this point, I decided to hook up my oscilloscope to see what was coming out of this thing. I programmed a free channel on my universal remote for a Sony DVD and started hitting some buttons Filed under: arduino — by adafruit, posted June 7, 2010 at 6:13 am Comments (0) [...]
[...] from his universal remote, so he hooked an infrared receiver up to his Arduino and created this Radioshack infrared receiver. Could be just the thing for creating that remote-controlled [...]
[...] from his universal remote, so he hooked an infrared receiver up to his Arduino and created this Radioshack infrared receiver. Could be just the thing for creating that remote-controlled [...]
[...] an Arduino project using an infrared remote – [Link] Tags: Arduino, IR, Remote Filed in Arduino | 1 views No Comments [...]
[...] Control an Arduino project using an infrared remote – [Link] [...]