Arduino and IR Receiver

In my last post, I used my USB oscilloscope to decode a discarded remote control's IR output. My next goal was to see if I could get my Arduino to read the data coming from the remote I had decoded.

The first challenge was finding an IR receiver. IR receivers are different from a typical IR transistor in that they are tuned to a certain carrier frequency - also called a "center frequency".

The boys and I dismantled
retired DVD player from a company called CyberHome. It was a cheap and junk - it really only lasted a year or so (maybe we watch too many DVDs). :) We had dismantled it and a few circuit boards were left for me to scavenge through. One had a IR receiver that I decided to use for this project.


I used my desoldering tool and soldering iron to remove the component from the circuit board, and also did a little reverse engineering to see how it was wired. I discovered that careful attention had been placed to create adequate filter capacitors and a small current limiting resistor in series with it's power.

I wired it with this circuit on a breadboard and was pleased to find out that the LED flickered each time I pressed a key on the Insignia remote control.


IR1 - unknown (CyberHome DVD player)
C1 - 470uf 16V
C2 - 0.02uf
R1 - 100 ohms
R2 - 1k ohms
LED1 - small green


I then transferred the circuit to a small perf-board and built a small shield to fit onto the Arduino that will allow me to read the output. I used a piece of yellow stranded wire and a single header pin to allow me to connect the output of the IR receiver to whichever I/O pin I wanted to use on the Arduino. Here's some pictures of the completed shield attached to an Arduino Duemilanova 328 from SparkFun.


I then wrote a program to read the data from the IR receiver, first detecting the header and later walking through each of the pulses that I discovered in my last blog entry. Here's a code snippet that shows the basics of reading the data:

lengthHeader = pulseIn(pinData, LOW);
if(lengthHeader > 5000)
{
//step through each of the 32 bits that streams from the remote
byteValue = 0;
for(int i = 1; i <= 32; i++)
{
bit = pulseIn(pinData, HIGH);

//read the 8 bits that are specifically the key code
//use bitwise operations to convert binary to decimal
if (i > 16 && i <= 24)
if(bit > 1000)
byteValue = byteValue + (1 << (i - 17));
}


You may have noticed three important things about this code:

1. I used the "pulseIn" function. This function allows me to measure the length of a pulse coming from the I/O pin in microseconds. This allowed me to differentiate between a long pause or short pause in the signal stream - producing for me 1's and 0's.

2. I used bitwise shift "<<" to convert the binary data into decimal data. I had first used the "pow" function but was disappointed with it's performance, and it didn't seem to accurate report that x to the 0 power equals 1, it was reporting zero. I may have done something wrong - not sure.

3. I only deal with bits 17 through 24. That's because the first two 8-bit bytes are not relevant, so I just want to skip them.

I then returned the results of the decimal data to a serial connection so I could see it on my computer. Sure enough - after a little messing around with setting proper "bounds" and flushing the serial buffer explicitly, I saw great results.

Lastly, I wanted to do something fun with the data results. So, I decided to make a Processing program that would show which button on the remote was being pressed. Here's a screen shot of the resulting UI. As you'll see by the following picture of the remote - they are relatively close in appearance. When the button is pressed, the UI illustrates it by turning the button red.


Here's a short video showing it working!

[coming...]

Here are the code listings. If you do something sim
ilar, be sure to let me know - I would love to hear about your projects too! Thanks for reading!

ARDUINO CODE

//setup variables
int pinData= 12;
unsigned long lengthHeader;
unsigned long bit;
int byteValue;

void setup()
{
pinMode(pinData, INPUT);
Serial.begin(9600);
}

void loop()
{
//look for a header pulse from the IR Receiver
lengthHeader = pulseIn(pinData, LOW);
if(lengthHeader > 5000)
{
//step through each of the 32 bits that streams from the remote
byteValue = 0;
for(int i = 1; i <= 32; i++)
{
bit = pulseIn(pinData, HIGH);

//read the 8 bits that are specifically the key code
//use bitwise operations to convert binary to decimal
if (i > 16 && i <= 24)
if(bit > 1000)
byteValue = byteValue + (1 << (i - 17));
}

//send the key code to the processing.org program
Serial.print(byteValue);
Serial.flush();
}

delay(100);
}


PROCESSING CODE

import processing.serial.*;

//define variables used thourghout the program
Serial arduinoPort;
int rowPressed = 0;
int colPressed = 0;
int code = 0;
String codeText;
PFont font;

void setup()
{
//create the window
size(210, 325);
background(220,220,220);

//write the brand name at the bottom of the window
smooth();
font = loadFont("Calibri-Bold-16.vlw");
textAlign(CENTER);
textFont(font);
fill(0,0,255);
text("I N S I G N I A", 105, 310);

//draw the silk screen colors on the remote
translate(-10, -15);
stroke(0,0,255);
ellipse(184,80,34,34);
ellipse(184,120,34,34);
ellipse(92,120,70,70);
ellipse(46,120,34,34);
ellipse(138,120,34,34);
ellipse(92,80,34,34);
ellipse(92,160,34,34);
rect(167,80,34,40);
rect(75,80,34,80);
rect(46,103,88,34);

//prepare for serial communication
String portName = Serial.list()[1];
arduinoPort = new Serial(this, portName, 9600);
}

void draw()
{
//initialize a few things each time.
rowPressed = 0;
colPressed = 0;
translate(-10, -15);
smooth();

//see if there is a serial message from the arduino
if (arduinoPort.available() > 0)
{
codeText = arduinoPort.readString();
code = int(codeText);
findPressed(code);
}

//use loops to draw the buttons on the remote
for(int col = 1; col <= 4; col++)
for(int row = 1; row <= 7; row++)
{
//draw the buttons different colors
if(row == rowPressed && col == colPressed) //red if pressed
fill(255, 0, 0);
else if((row == 4 && col == 1) || (row >= 5 && col <= 3)) //silk screened blue buttons
fill(0, 0, 255);
else //unpressed buttons
fill(240, 240, 240);
ellipse(col * 46, row * 40, 30, 30);
}
delay(100); //delay to leave the button lit a moment
}

//use the code id from the arduino to map to a button on the remote
void findPressed(int code)
{
switch(code)
{
case 28:
rowPressed = 1;
colPressed = 1;
break;
case 4:
rowPressed = 1;
colPressed = 2;
break;
case 17:
rowPressed = 1;
colPressed = 3;
break;
case 12:
rowPressed = 1;
colPressed = 4;
break;
case 80:
rowPressed = 2;
colPressed = 1;
break;
case 20:
rowPressed = 2;
colPressed = 2;
break;
case 81:
rowPressed = 2;
colPressed = 3;
break;
case 30:
rowPressed = 2;
colPressed = 4;
break;
case 84:
rowPressed = 3;
colPressed = 1;
break;
case 26:
rowPressed = 3;
colPressed = 2;
break;
case 85:
rowPressed = 3;
colPressed = 3;
break;
case 31:
rowPressed = 3;
colPressed = 4;
break;
case 18:
rowPressed = 4;
colPressed = 1;
break;
case 23:
rowPressed = 4;
colPressed = 2;
break;
case 73:
rowPressed = 4;
colPressed = 3;
break;
case 27:
rowPressed = 4;
colPressed = 4;
break;
case 5:
rowPressed = 5;
colPressed = 1;
break;
case 6:
rowPressed = 5;
colPressed = 2;
break;
case 7:
rowPressed = 5;
colPressed = 3;
break;
case 16:
rowPressed = 5;
colPressed = 4;
break;
case 9:
rowPressed = 6;
colPressed = 1;
break;
case 10:
rowPressed = 6;
colPressed = 2;
break;
case 11:
rowPressed = 6;
colPressed = 3;
break;
case 72:
rowPressed = 6;
colPressed = 4;
break;
case 13:
rowPressed = 7;
colPressed = 1;
break;
case 14:
rowPressed = 7;
colPressed = 2;
break;
case 15:
rowPressed = 7;
colPressed = 3;
break;
case 88:
rowPressed = 7;
colPressed = 4;
break;
default:
rowPressed = 0;
colPressed = 0;
}

}