Arduino: How to Process Infrared Signals from a Remote Control
Infrared remote controls are ubiquitous: TVs, IOT devices, toys. With an Arduino and an IR receiver, it is a matter of minutes to setup receiving IR commands and process them to control your Arduino application.
This blog posts show you the essential steps to process any IR commands from one of your remotes.
Setup
The Hardware requirements for this project are minimal:
- Arduino Uno or variant
- IR Remote Receiver Module
- Any remote
To connect the devices, follow these instructions. To identify the right pins, see also Arduino Pin Layout.
- Connect IR sensor Y Pin to any Arduino analog pin
- Connect IR sensor R Pin to Arduino 5V power out
- Connect IR sensor G Pin to any Arduino ground pin
Library Choice
Following my guideline, I checked the available libraries. For a basic ATmega328P Arduino Uno, the best library is Arduino-IRremote. It’s in active development, has good documentation, and you can find useful examples. I installed the library using the platform IO package manager.
Once installed, the first step is to identify the concrete protocol your remote uses.
Identify the Protocol
From the official example sketch, create the following program:
#include <Arduino.h>
#include <IRremote.h>
#define IR_PIN 3;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
IrReceiver.begin(IR_PIN, ENABLE_LED_FEEDBACK);
}
void loop() {
if (IrReceiver.decode()) {
IrReceiver.printIRResultShort(&Serial);
Serial.println();
IrReceiver.printIRResultAsCVariables(&Serial);
}
IrReceiver.resume();
}
Then, upload the program, connect to the serial monitor and press some keys on your remote. You should see output like this:
Protocol=NEC Address=0x4 Command=0x11 Raw-Data=0xEE11FB04 32 bits LSB first
uint16_t address = 0x4;
uint16_t command = 0x11;
uint32_t data = 0xEE11FB04;
In rare cases, the signal might not get processed correctly. In this case, try another remote control.
Receive and Process IR Commands
When you know the protocol, it’s time to create the real program. Start your source code file with the following statements, and be sure to define the protocol before including the Header file.
#define DECODE_NEC 1
#include <IRremote.h>
int IR_RECEIVE_PIN = 11;
In the setup()
part of your program, you initialize the IrReceiver
event.
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN);
In the loop
part, you wait for the IrReceiver.decode()
interrupt to happen. Then, you can access the received data with type IRData
. Its definition is this:
struct IRData {
decode_type_t protocol; ///< UNKNOWN, NEC, SONY, RC5, ...
uint16_t address; ///< Decoded address
uint16_t command; ///< Decoded command
uint16_t extra; ///< Used by MagiQuest and for Kaseikyo unknown vendor ID
uint8_t numberOfBits; ///< Number of bits received for data (address + command + parity) - to determine protocol length if different length are possible (currently only Sony).
uint8_t flags; ///< See definitions above
uint32_t decodedRawData; ///< up to 32 bit decoded raw data, formerly used for send functions.
irparams_struct *rawDataPtr; /// pointer of the raw timing data to be decoded
};
To print out the relevant data, use this:
void loop() {
if (IrReceiver.decode()) {
Serial.print("COMMAND is ");
Serial.println(IrReceiver.decodedIRData.command);
Serial.print("ADDRESS is ");
Serial.println(IrReceiver.decodedIRData.address);
Serial.print("RAW DATA is ");
Serial.println(IrReceiver.decodedIRData.decodedRawData);
IrReceiver.resume();
}
}
You are almost done. Upload this program to your Arduino, then send commands from the remote. The output of the program will be similar to this:
COMMAND is 18
ADDRESS is 4
RAW DATA is 3977444100
COMMAND is 64
ADDRESS is 4
RAW DATA is 3208706820
Now, use this program to test each key of the remote, and make a note of their command ID. For example, in the above example, the COMMAND 18
is recognized when I press the key 2
, and the COMMAND 64
is the arrow up button.
Defining Application Logic
The final part is easy. Inside the loop()
method, use IrReceiver.decode()
to check if new IR commands were received:
void loop() {
if (IrReceiver.decode()) {
// handle cases
}
}
Inside the method body, you define the custom logic that is triggered. I suggest to use a switch
statement that determines the currently pressed key, and then to handle each case with a dedicated case
statements. The basic structure is as follows. Important: At the end of the statement, be sure to call IrReceiver.resume()
in order to listen for new key events.
switch (IrReceiver.decodedIRData.command) {
case (17): {
// handle key 17
break;
}
case (18): {
// handle key 18
break;
}
IrReceiver.resume();
}
In my case, I detect the various numbers of the remote control to print them on a screen. And I also process direction commands that are used to move a wheeled vehicle.
void processIR() {
switch (IrReceiver.decodedIRData.command) {
case (17): {
printCharOnMatrix('1');
break;
}
case (18): {
printCharOnMatrix('2');
break;
}
case (19): {
printCharOnMatrix('3');
break;
}
// ....
default : {
printCharOnMatrix('@');
break;
}
}
IrReceiver.resume();
}
Optimization
By default, all IR vendor protocol - see complete list of protocols - will be compiled into your program. If you know the exact protocol, just include this with a preprocessor directive like shown here:
#define DECODE_NEC 1
#include <Arduino.h>
#include <IRemote.h>
And for fine tuning, there are many compile options activated by preprocessors definitions, see the official documentation.
Conclusion
Thanks to the excellent Arduino-IRremote library, parsing IR commands can be setup in minutes. This article showed you how to setup the library and start a program to sample the commands of your remote. We then saw how to implement custom control logic for each of the IR commands. Finally, I gave you some hints how to optimize the overall program by including only the required IR protocols.