Arduino: How to Process Infrared Signals from a Remote Control
Tags: arduino, cpp
Published: 2021-10-04 05:56:59

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.

_This article originally appeared at my blog [admantium.com](https://admantium.com/blog/micro13_infra_red_commands/)_.

## 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](https://content.arduino.cc/assets/Pinout-UNOrev3_latest.pdf).

- 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](https://github.com/Arduino-IRremote/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]( https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDump/ReceiveDump.ino), create the following program:

```cpp
#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:

```bash
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.

```cpp
#define DECODE_NEC 1
#include <IRremote.h>

int IR_RECEIVE_PIN = 11;
```

In the `setup()` part of your program, you initialize the `IrReceiver` event.

```cpp
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](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/d27b1e4e7280f7bb335dc57567e95e3881977b62/src/IRremoteInt.h) is this:

```cpp
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:

```cpp
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:

```bash
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:

```cpp
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.

```cpp
  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.

```cpp
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](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino#L14) - will be compiled into your program. If you know the exact protocol, just include this with a preprocessor directive like shown here:

```cpp
#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](https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library).

## Conclusion

Thanks to the excellent [Arduino-IRremote](https://github.com/Arduino-IRremote/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.