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.









Muses Shoe, EL Wire and You
Here’s something for the locals. I knew there had to be some fun locked away in the light-up Mardi Gras throws that have become more popular in recent years. Unfortunately, I’m not cute enough to have a Muses shoe thrown to little old me, so I had to wait for somebody to throw it away at the Really Really Free Market held at Art House this past weekend.
A few notes about this. It’s not hard. Not really very impressive, either. There’s a lot that I don’t know as I only had one shoe and one 4ft EL wire that I picked up from Electronics Goldmine a while ago. For those same reasons, I could have documented this much better. The longest part was dealing with my miserable skills with a soldering iron.
Up on the top there, if you peel back the circuit board you’ll see a coil telling you we are dealing with a high voltage AC inverter here (running on two AAs), which is why I did this; I was too lazy to ever order an AC inverter for the EL wire I had. For those that haven’t read up on EL wire, it won’t run on DC current, so these little pocket inverters, usually found in a 9V variety, are the way way to go if you want to, say, mount it on a bike like I did. Be careful, I shocked myself about 15 times messing with this, and though it wasn’t too bad it was because I was clumsy, not because I liked it.
The shoe half is mostly useless to this project. If you notice, I pulled off some of those pins from the shoe end. Don’t bother with that, I thought they’d make it easier to attach the EL wire to the leads from the board, but they didn’t stick on there well.
You need to strip one end of your EL wire a little to use it (the other end is free to dangle). The outer layer is essentially the tint of the wire. Underneath there are two very fragile wires, I’ve seen them called Corona wires or Angel Wires. They are important and frustratingly brittle. Then there’s the relatively fat, sheathed wire in the middle. You’ll need to strip that as well.
A couple of guides recommend a strip of copper tape wrapped around the white wire. That way your brittle angel wires can be soldered to the tape, then the next thing you solder to the tape without worrying about tension on the angel wire. If you’ve got some copper tape DO IT, it will probably make your life much easier. It made my life harder because I checked out Radio Shack, Lowes and Home Depot, and none of them have copper tape. Instead I broke the angel wires a good 6 times until I finally managed to put the thing together intact, taping it all together so they wouldn’t be jostled much. But I’m skipping a step
This part’s a little shaky, but here’s what worked for me. The main rule that I discerned is that one wire from the EL has to be soldered to one of the two outside wires, the other to any of the middle three. Since different parts of the original shoe were supposed to work in certain ways, each one will behave differently. I found my favorite combo to be the fat wire on the lead the corresponds to the leftmost joint on the inverter’s circuit board, with the angel wire going right next to it. That’s the only configuration I found that allowed a sustained glow.
From there I taped it up tight, waterproofed it by sticking it in a ziplock with a hole in one non-zip corner for the wire to escape (and taped that corner too) and packaging-taped it around my bike! There’s a lot more I’d like to learn about this, such as how long of an EL wire it can drive, whether it’ll run two wires just as easily etc., and I don’t even know about battery life just yet, but for once your Mardi Gras beads have some worth.