Learn How to Decode a TV Remote Using Arduino and IR Sensor (Video)
Do you know that you could use your TV remote control as an input device to your DIY project? Better yet, are you aware that could repurpose your TV remote control to switch ON/OFF the lights in your house? Well, wonder no more because, in this article, you are going to learn how to easily decode your TV remote control and use it as wireless input control for your DIY project.
How a TV Remote Control Works
A TV remote control uses wireless communication technology to control the TV remotely. It simply works by generating an encoded or rather modulated infrared (IR) signal which is then transmitted by an IR LED to the TV. A microcontroller inside the remote control is responsible for creating the encoded signal. The TV, on the other hand, has an IR photo sensor that receives and decodes or demodulates the IR signal to output digital waveforms. These waveforms are readable to a microcontroller. Each button on the remote control is encoded differently to distinguish it from the rest. That is why the “Channel” button cannot be used to turn the TV ON. Similarly, TV remotes from different brands and for different devices are encoded differently. That is why your hometheater remote will not work on your TV.
The signal encoding method utilized in the design of IR remote control devices is known as pulse width modulation (PWM). PWM, as the name implies, refers to generating a signal at high frequency with varying ON and OFF times. To that end, the IR LED on the remote control is pulsed at high frequencies (38kHz – 56kHz) and then a bunch of pulses are likewise pulsed on and off. As a result, the encoding of a button is accomplished, which helps to differentiate the signal from other ambient IR signals. Each button is also modulated differently to distinguish it from the rest.
How to Decode the IR Remote Control
The signal from an IR remote control can be decoded in different ways. An oscilloscope can be used by hooking the terminals of the IR LED to the input channel. Alternatively, you can use an IR sensor with Arduino, which is way cheaper (less than $25) compared to a $1000 oscilloscope. In this article, we shall learn how to decode an IR remote control using the second approach. Afterwards, the decoded signals can be used to clone the remote control. Alternatively, the remote can be repurposed as a wireless input device for your DIY project.
IR Circuit Diagram
Parts
- 1 IR photosensor (PNA4602)
- 3 Connector wires
- 1 LED
- Current limiting resistor (220ohms)
- Breadboard
Code
This Arduino Sketch for decoding the IR remote is available in the public domain.
/* Raw IR decoder sketch!
This sketch/program uses the Arduno and a PNA4602 to
decode IR received. This can be used to make a IR receiver
(by looking for a particular code)
or transmitter (by pulsing an IR LED at ~38KHz for the
durations detected
Code is public domain, check out www.ladyada.net and adafruit.com
for more tutorials!
*/
// We need to use the 'raw' pin reading methods
// because timing is very important here and the digitalRead()
// procedure is slower!
//uint8_t IRpin = 2;
// Digital pin #2 is the same as Pin D2 see
// http://arduino.cc/en/Hacking/PinMapping168 for the 'raw' pin mapping
#define IRpin_PIN PIND
#define IRpin 2
// for MEGA use these!
//#define IRpin_PIN PINE
//#define IRpin 4
// the maximum pulse we'll listen for - 65 milliseconds is a long time
#define MAXPULSE 65000
// what our timing resolution should be, larger is better
// as its more 'precise' - but too large and you wont get
// accurate timing
#define RESOLUTION 20
// we will store up to 100 pulse pairs (this is -a lot-)
uint16_t pulses[100][2]; // pair is high and low pulse
uint8_t currentpulse = 0; // index for pulses we're storing
uint16_t averageLow = 0;
uint16_t averageHigh = 0;
void setup(void) {
Serial.begin(9600);
Serial.println("Ready to decode IR!");
}
void loop(void) {
uint16_t highpulse, lowpulse; // temporary storage timing
highpulse = lowpulse = 0; // start out with no pulse length
// while (digitalRead(IRpin)) { // this is too slow!
while (IRpin_PIN & (1 << IRpin)) {
// pin is still HIGH
// count off another few microseconds
highpulse++;
delayMicroseconds(RESOLUTION);
// If the pulse is too long, we 'timed out' - either nothing
// was received or the code is finished, so print what
// we've grabbed so far, and then reset
if ((highpulse >= MAXPULSE) && (currentpulse != 0)) {
Serial.println(highpulse);
Serial.println(currentpulse);
Serial.println(MAXPULSE);
printpulses();
currentpulse=0;
return;
}
}
// we didn't time out so lets stash the reading
pulses[currentpulse][0] = highpulse;
// same as above
while (! (IRpin_PIN & _BV(IRpin))) {
//while (!IRpin_PIN & (1<<IRpin)){
// pin is still LOW
lowpulse++;
//delayMicroseconds(RESOLUTION);
if ((lowpulse >= MAXPULSE) && (currentpulse != 0)) {
printpulses();
currentpulse=0;
return;
}
}
pulses[currentpulse][1] = lowpulse;
// we read one high-low pulse successfully, continue!
currentpulse++;
}
void printpulses(void) {
Serial.println("\n\r\n\rReceived: \n\rOFF \tON");
for (uint8_t i = 0; i < currentpulse; i++) {
Serial.print(pulses[i][0] * RESOLUTION, DEC);
Serial.print(" usec, ");
averageHigh +=pulses[i][0] *RESOLUTION;
Serial.print(pulses[i][1] * RESOLUTION, DEC);
Serial.println(" usec");
averageLow +=pulses[i][1] * RESOLUTION;
}
Serial.print("Number of Pulses: ");
Serial.println(currentpulse);
Serial.print("Print average of Low Pulses: ");
Serial.println(averageLow/currentpulse);// print the average
Serial.print("Print average of High Pulses: ");
Serial.println(averageHigh/currentpulse);// print the average
// print it in a 'array' format
Serial.println("int IRsignal[] = {");
Serial.println("// ON, OFF (in 10's of microseconds)");
for (uint8_t i = 0; i < currentpulse-1; i++) {
Serial.print("\t"); // tab
Serial.print(pulses[i][1] * RESOLUTION / 10, DEC);
Serial.print(", ");
Serial.print(pulses[i+1][0] * RESOLUTION / 10, DEC);
Serial.println(",");
}
Serial.print("\t"); // tab
Serial.print(pulses[currentpulse-1][1] * RESOLUTION / 10, DEC);
Serial.print(", 0};");
}
Procedure
Once you have gathered the components and loaded the sketch on the Arduino IDE, follow these steps to decode your TV remote control.
- Create the simple TV remote decoder circuit
On your breadboard, create the circuit illustrated in the schematic. Pay attention to the value of the current limiting resistor to avoid burning the LED (if the value is too small) or having a very dim light (if the value is too big).
- Load sketch onto the Arduino Uno
This code is microcontroller-specific because it uses the ‘raw’ pin reading method. Therefore, if you are using a different Arduino other than the Uno, you will need to check the ‘raw’ pin mapping.
- Open the serial monitor of the Arduino IDE
If you have successfully loaded the sketch onto the Arduino, open the serial monitor. You will see a prompt saying: Ready to decode IR! as illustrated below.
- Point the IR remote to the IR receiver and press any key
If a signal has been received, the LED will blink and data will be displayed on the serial monitor as shown below. The data represents the decoded PWM-encoded IR signal.
- Press another key to compare data
Press another button to compare the data generated. You will note that the data is different for each button, which cannot be detected by how the LED blinks. This is why encoding is important because it also prevents ambient IR signals from affecting the circuit.
How the Code Works
Every step of the code is explained using comments to make it easy for any user to understand how it functions. Nonetheless, there are a few technical parts of the code may need further explanation. The first part involves the use of the ‘raw’ data pin to get input from the IR sensor. Normally, this is accomplished by directly manipulating registers in the microcontroller. In our case, we are interested in using digital pin 2 as input, which is a pin in PORTD of the microcontroller. Therefore, to access the pin as an input in ‘raw’ format, we use the input register for PORT D which is identified as PIND and through bit manipulation, read the status of Pin 2. Note that all ports in a microcontroller are inputs by default unless declared otherwise. Therefore, we do not need to use the data direction register for port D (DDRD) to explicitly declare the pin 2 as input.
#define IRpin_PIN PIND
#define IRpin 2
In order to read pin 2, we use the input register for PORT D with some bit manipulation techniques to avoid affecting other pins. To read the high status of pin 2, we use the following code:
while (IRpin_PIN & (1 << IRpin)) {...}
The while loop uses “ANDing” and Shifting to read the high status of pin 2. Logically, the code starts by shifting a 1 two positions to the left and then ANDing with PIND. This gives either a 1 for pin high (true) or a 0 for pin low (false). A set of code to compute the duration of the high position is executed. Similarly, to know whether the pin is low in order to execute the subsequent code for calculating the duration the low signal, the same while loop condition is used and then inverted as shown below. However, this time, the _BV() macro is used but you can still use the standard method.
while (!(IRpin_PIN & _BV(IRpin))) {...}
As far as computing the timings is concerned, the first while loop computes the duration of the OFF state while the second loop computes the duration of the ON state of the PWM IR signal.
Finally, the printpulses() function arranges data into array and prints it nicely on the serial monitor window.
How the Circuit Works
The circuit is as you can see in the schematic diagram. The VCC pin is connected to arduino’s 5V supply and the ground pin to ground. The data pin is connected to pin 2 of the arduino. This is where the microcontroller reads the input signal as decoded by infrared sensor. When the signal is high the pin is low and vice versa. The LED connected between the VCC and data pin uses this phenomenon to turn ON/OFF. When the pin is pulled to ground by an IR signal, the LED turns ON because Pin 2 acts as ground. Otherwise, the pin remains high thus keeping the LED in OFF state.