Building a Universal Remote Control with Arduino: A Comprehensive Guide

Infrared (IR) remote controls are ubiquitous, serving as the primary interface for controlling a wide array of electronic devices, from televisions and DVD players to air conditioners and home theater systems. This article provides a comprehensive guide to building your own universal, programmable IR remote control using an Arduino microcontroller. This project will allow you to understand the principles of IR communication, learn how to use IR receivers and transmitters, and gain experience in storing data in non-volatile EEPROM. The final result is a prototype that can be modified and extended to suit individual needs.

Introduction to Infrared Communication

Infrared (IR) communication is a widely used wireless technology. Infrared radiation is a form of light, similar to visible light. Because it is light, IR communication requires a direct line of sight between the transmitter and receiver.

A typical IR communication system consists of an IR transmitter and an IR receiver. The transmitter emits light in the IR spectrum. The IR receiver is a photodiode and pre-amplifier that converts the IR light into an electrical signal.

IR light is emitted by the sun, light bulbs, and anything else that produces heat. To avoid interference from these sources, IR signals are modulated.

In IR signal modulation, an encoder on the IR remote converts a binary signal into a modulated electrical signal. This electrical signal is sent to the transmitting LED. The transmitting LED converts the modulated electrical signal into a modulated IR light signal. The modulated IR signal is a series of IR light pulses switched on and off at a high frequency known as the carrier frequency. The carrier frequency used by most transmitters is 38 kHz, because it is rare in nature and thus can be distinguished from ambient noise. The receiver diode detects all frequencies of IR light, but it has a band-pass filter and only lets through IR at 38 kHz.

Read also: Maximize Savings on McGraw Hill Education

The pattern in which the modulated IR signal is converted to binary is defined by a transmission protocol. There are many IR transmission protocols. Logical ‘1’ starts with a 562.5 µs long HIGH pulse of 38 kHz IR followed by a 1,687.5 µs long LOW pulse. This is how the NEC protocol encodes and decodes the binary data into a modulated signal. Each time you press a button on the remote control, a unique hexadecimal code is generated. This is the information that is modulated and sent over IR to the receiver.

Each time a button is pressed on the remote control, a unique hexadecimal code is generated. Different remotes send different codes for the keypresses, so you’ll need to determine the code generated for each key on your particular remote. If you can find the datasheet, the IR key codes should be listed.

Components Required

To build the programmable IR remote, you will need the following components:

  • Arduino board (e.g., Arduino Uno)
  • IR receiver module (e.g., HX1838)
  • IR transmitter LED
  • Red LED
  • 330 Ohm resistor
  • Three push buttons
  • 10K Ohm resistors (3)
  • Breadboard
  • Jumper wires

Circuit Diagram and Connections

The following steps describe how to connect the components to the Arduino:

  1. Power Rails: Run a blue wire from the GND pin of the Arduino to the negative power rail of the breadboard (marked with a blue line). Connect a red wire from the 5V pin of the Arduino to the positive power rail of the breadboard (marked with a red line). In addition, connect both positive power rails of the breadboard with another red wire.

    Read also: Comprehensive Guide to SAT Fee Waivers

  2. IR Receiver: Connect the IR receiver to the breadboard. Run a blue and red wire from the receiver to the corresponding power rails of the breadboard. The negative side is usually marked with (-) sign on the receiver, while the middle pin tends to be the positive pin.

  3. IR Transmitter: Connect the IR transmitter module to the breadboard. Connect the power supply (red and blue wire).

  4. Status LED: Place a red LED on the breadboard and connect the cathode (the shorter pin) of the LED with a blue wire to the negative power rail of the breadboard. Then connect a 330 Ohm resistor to the anode of the LED (the longer pin).

  5. Buttons: Connect three buttons to the breadboard. A 10K Ohm resistor serves as a pull-up resistor and is connected to the positive power rail. The pin on the other side of the button is connected to an input pin on the Arduino. In the normal, open state of the push button, the input is connected to 5V via the resistor and we will read a HIGH signal.

    • Connect the first button (mode switching button) with a yellow wire to pin 4 on the Arduino.
    • Connect the second button (function 1) to pin 5 (cyan wire).
    • Connect the third button (function 2) to pin 6 (green wire).
    • Additional buttons can be connected by replicating the wiring of the existing buttons and extending the code accordingly.

Arduino Code

The Arduino code consists of several parts: library inclusion, pin definitions, variable declarations, helper functions, and the main functions for receiving, storing, and sending IR codes.

Read also: Already Claimed ACT Student Code?

Library Inclusion and Pin Definitions

First, include the necessary libraries: EEPROM.h for storing the programmed IR codes and IRremote.h for decoding the IR signals.

#include <EEPROM.h>#include <IRremote.h>

Next, define the pins on the Arduino that are connected to each component.

const int irReceivePin = 11; // IR receiver pinconst int irSendPin = 3; // IR transmitter pinconst int modePin = 4; // Mode switch button pinconst int func1Pin = 5; // Function 1 button pinconst int func2Pin = 6; // Function 2 button pinconst int ledPin = 13; // Status LED pin

Variable Declarations

Declare variables to store the current mode (programming or normal) of the IR remote. The Code struct stores the address (adr) and command (cmd) of the IR codes we are going to receive, store, and send.

int modeReceive = HIGH; // HIGH = sending mode, LOW = receiving modestruct Code { unsigned int adr; unsigned int cmd;};

Helper Functions

Implement helper functions to simplify the code and make it more readable. The isOn() function returns True if a button is LOW, which means it is pressed down.

bool isOn(int pin) { return digitalRead(pin) == LOW;}

The memadr() function is used to calculate the memory address in the EEPROM where we store and read IR command codes from. This function takes a btnId, which is 1 or 2 (for function 1 or 2) and computes the memory address.

int memadr(int btnId) { return (btnId - 1) * sizeof(Code);}

Storing and Sending IR Codes

The store_code() function takes a button id (btnId) and a data block (irData) of received IR data. In the first step, we switch the status led off, to indicate that we are storing data. Then we extract the code address and command from the data block and store it in a Code struct. We write this code struct to the EEPROM with put(), using the memadr() function to calculate the memory address. We need to store the data in EEPROM because otherwise every time power is switched off the Arduino looses all the stored data. After storing the data we wait for 200ms. This is to avoid unnecessary, repeated storage when the programming key remains pressed. And finally, we switch the status led back, to indicate the the programming is complete.

void store_code(int btnId, decode_results irData) { digitalWrite(ledPin, LOW); // LED off: storing data Code code; code.adr = irData.address; code.cmd = irData.command; EEPROM.put(memadr(btnId), code); delay(200); // avoid repeated storage digitalWrite(ledPin, HIGH); // LED on: programming complete}

Sending of codes essentially works in reverse. The send_code() function below retrieves the code to send from the EEPROM for the pressed button (btnId). Then we get the code to send from the EEPROM for the pressed button (btnId).

void send_code(int btnId) { digitalWrite(ledPin, HIGH); // LED on: sending Code code; EEPROM.get(memadr(btnId), code); IrSender.sendNEC(code.adr, code.cmd, 0); digitalWrite(ledPin, LOW); // LED off: sending complete}

Main Functions

Start with a toggle_mode() function that switches our remote from programming/receiving mode to sending mode and back. The first line does the toggling of the mode. In the second line, we switch the status led on, if we are in programming mode.

void toggle_mode() { modeReceive = !modeReceive; digitalWrite(ledPin, modeReceive == LOW ? HIGH : LOW); if (modeReceive == LOW) { IrReceiver.resume(); // Enable receiving } else { IrReceiver.disableIRIn(); // Disable receiving } delay(200); // Debounce}

The send() function uses send_code() to the send the IR codes stored for button 1 and 2.

void send() { if (isOn(func1Pin)) { send_code(1); } if (isOn(func2Pin)) { send_code(2); }}

Finally, the last of the main functions is receive(). There we receive the data transmitted from an IR remote and store it for button 1 or 2, depending on which button is currently pressed.

void receive() { if (IrReceiver.decode()) { if (isOn(func1Pin)) { store_code(1, IrReceiver.results); } if (isOn(func2Pin)) { store_code(2, IrReceiver.results); } IrReceiver.resume(); // Receive the next value }}

Setup and Loop Functions

In the setup() function, we establish serial communication, initialize the IrReceiver and set the IO modes for the pins.

void setup() { Serial.begin(9600); IrReceiver.begin(irReceivePin, DISABLE_LED_FEEDBACK); IrSender.begin(irSendPin, DISABLE_LED_FEEDBACK); pinMode(modePin, INPUT_PULLUP); pinMode(func1Pin, INPUT_PULLUP); pinMode(func2Pin, INPUT_PULLUP); pinMode(ledPin, OUTPUT); digitalWrite(ledPin, modeReceive == LOW ? HIGH : LOW);}

After all the hard work above, the main loop is now very simple. We watch the mode button connect to the modePin and if it is pressed we toggle the mode. Next we check if we are in receiving mode via isOn(modeReceive). If that is the case, we receive and potentially store IR data. Otherwise, we send the stored codes.

void loop() { if (isOn(modePin)) { toggle_mode(); } if (modeReceive == LOW) { receive(); } else { send(); }}

Complete Code

#include <EEPROM.h>#include <IRremote.h>const int irReceivePin = 11; // IR receiver pinconst int irSendPin = 3; // IR transmitter pinconst int modePin = 4; // Mode switch button pinconst int func1Pin = 5; // Function 1 button pinconst int func2Pin = 6; // Function 2 button pinconst int ledPin = 13; // Status LED pinint modeReceive = HIGH; // HIGH = sending mode, LOW = receiving modestruct Code { unsigned int adr; unsigned int cmd;};bool isOn(int pin) { return digitalRead(pin) == LOW;}int memadr(int btnId) { return (btnId - 1) * sizeof(Code);}void store_code(int btnId, decode_results irData) { digitalWrite(ledPin, LOW); // LED off: storing data Code code; code.adr = irData.address; code.cmd = irData.command; EEPROM.put(memadr(btnId), code); delay(200); // avoid repeated storage digitalWrite(ledPin, HIGH); // LED on: programming complete}void send_code(int btnId) { digitalWrite(ledPin, HIGH); // LED on: sending Code code; EEPROM.get(memadr(btnId), code); IrSender.sendNEC(code.adr, code.cmd, 0); digitalWrite(ledPin, LOW); // LED off: sending complete}void toggle_mode() { modeReceive = !modeReceive; digitalWrite(ledPin, modeReceive == LOW ? HIGH : LOW); if (modeReceive == LOW) { IrReceiver.resume(); // Enable receiving } else { IrReceiver.disableIRIn(); // Disable receiving } delay(200); // Debounce}void send() { if (isOn(func1Pin)) { send_code(1); } if (isOn(func2Pin)) { send_code(2); }}void receive() { if (IrReceiver.decode()) { if (isOn(func1Pin)) { store_code(1, IrReceiver.results); } if (isOn(func2Pin)) { store_code(2, IrReceiver.results); } IrReceiver.resume(); // Receive the next value }}void setup() { Serial.begin(9600); IrReceiver.begin(irReceivePin, DISABLE_LED_FEEDBACK); IrSender.begin(irSendPin, DISABLE_LED_FEEDBACK); pinMode(modePin, INPUT_PULLUP); pinMode(func1Pin, INPUT_PULLUP); pinMode(func2Pin, INPUT_PULLUP); pinMode(ledPin, OUTPUT); digitalWrite(ledPin, modeReceive == LOW ? HIGH : LOW);}void loop() { if (isOn(modePin)) { toggle_mode(); } if (modeReceive == LOW) { receive(); } else { send(); }}

Using the Universal Remote

The remote has three buttons. The Mode button is used to switch from programming or receiving mode to sending mode. At the start the remote is in sending mode and the status led is off.

To program one of the two function keys you must point your other IR remote toward the receiver diode and press two keys together. The key on the other remote you want to store and the function key on our universal remote. The status LED should be on during programming mode.

To send IR commands, simply press the Mode button (the status LED should be off) and press one of the function keys you have programmed.

Troubleshooting

  • Unsupported Remotes: Some IR remotes may not be supported, preventing you from programming them. Verify this by connecting the Serial monitor, switching to the programming mode (status LED should be on) and check if the IRremote library can decode the signal.
  • Sending Functionality: For debugging the sending functionality, a component tester can be helpful.

Expanding Functionality

  • Adding More Buttons: Replicate the wiring of the existing buttons for the new buttons and extend the code accordingly.
  • Multiple IR LEDs: Connect additional IR LEDs to other output pins or run an IR LED at a higher voltage, requiring a transistor or MOSFET to control the signal input to the LED.
  • Alternative Libraries: While the IrRemote library is great, it does not support every IR communication protocol. For sending, consider using TinyIRSender, which is limited to the NEC protocol, or explore the IRremoteESP8266 library for broader protocol support.

Alternative Arduino Boards

You can use any Arduino board for this project as long as you can connect an infrared (IR) receiver and transmitter, and the required libraries are supported.

Finding IR Codes

You can find the IR codes for your devices by searching online or referring to the device’s user manual. Many manufacturers provide IR code databases that can be used with Arduino libraries. Alternatively, you can use an IR receiver module to capture the codes from an existing remote control.

Controlling Multiple Devices

You can control multiple devices with this IR remote by programming different IR codes for each device and switching between them using buttons or a menu system in your Arduino code.

tags: #universal #remote #control #arduino #code

Popular posts: