Introduction: Universal TV Remote - Ardiuino, Infrared

Hello! In this instructable, I'll show you how to build and program your own universal remote that will work with most things that utilize an infrared remote, and that will also "listen" and decode an infrared signal sent by various other remotes.

A bit of background of what inspired me to build this remote - I, like most of you, lose my remotes constantly, and this calamity is quite frustrating, so I figure lets solve it! I have built this remote and discreetly embedded it into my custom built bed frame (I'm also a woodworker) - I can't lose the remote if its a part of my bed frame!


Things you'll need:
-Arduino UNO or Nano - mileage may vary with other boards

-Solderless breadboard (or solderable stripboard if you'd like to make it more permanent)

-Jumperwires of various colors and lengths

-Momentary push buttons (5) (you can add more buttons, but you'll need to use digital pins, as all but 1 of the analog pins are used - you'll need to look into making sure you properly utilize pull up resistors, or pull down resistors, and debounce the push buttons)

-10K Ohm resistor (5) (if you'd like more push buttons, you'll need more of these)

-470 Ohm resistor (2)

-Infrared LED

-Red LED

-Infrared Sensor (I used part number VS1838B, you could use another, just check the pin-out)

(Optional) Soldering Iron, Solder, Solder Flux.

Step 1: Building the Circuit:

1). I always like to start with laying out my components, as this always drives the layout on the breadboard.

-Push buttons

-LEDS: the Red LED and IR LED are wired in tandem, so you can see what the IR LED is doing.


2). Resistors

- The five 10K resistors we have attached to the push buttons are called "pull down" resistors. Pull down resistors make sure that when a push button isn't pressed, the corresponding Arduino pin gets 0 Volts (or at least close to it). For more info on pull down (or pull up) resistors here is an in depth guide:

These resistors may not be completely necessary, but if you are getting "ghost" pushes, it's more than likely caused by capacitive coupling and pull down resistors prevent this.

3). Circuit wires

4). 5V and Ground Wires

Use the provided picture for reference! don't be afraid to change it up for your needs though!

Step 2: Code:

#include const int RECV_PIN = 7; // IR sensor read pin int Button1 = A4;// Farthest Left int Button2 = A3; // 2nd from the left int Button3 = A2; // Middle int Button4 = A1; // 2nd to the right int Button5 = A0; // Farthest to the right int LED = 3; // IR LED & Red LED int val = 0; // Changing value IRsend irsend; IRrecv irrecv(RECV_PIN); decode_results results;

void setup() {pinMode(Button1,INPUT); pinMode(Button2,INPUT); pinMode(Button3,INPUT); pinMode(Button4,INPUT); pinMode(Button5,INPUT); pinMode(LED,OUTPUT); Serial.begin(9600); irrecv.enableIRIn(); irrecv.blink13(true);} void loop() {{{if (analogRead(Button1)>900)irsend.sendNEC(0xFF02FD,32); // using analog read instead of digital read to avoid captive capacitance issues. also, helps debounce the buttons. // Having analog read at 900 allows for some wiggle room in the values, meaning, the infra signal will be sent even if a full 5V isn’t applied to the pin. //but 900 is high enough to not read erroneously due to capacitive coupling delay(100);}//RGB Strip On&off {if (analogRead(Button5)>900) {for (int i = 0; i < 3; i++) // changing the value in "i < 3" will change the number of the times the signal is repeated immediately. so "i < 2" will repeat the signal twice. // you might need to play around with this number if your TV isn’t responding, generally, 1 or 3 work most, if those don’t, try odd numbers. // you might also need to play with the intra signal delay timing values, for instance, for my TV 10 works, but 30 doesn’t. {irsend.sendSony(0xa90, 12); // Sony TV power code, for my TV, code needs to be sent 3x3, so 3 pulses, three separate times delay(10); // "intra signal delay" for (int i = 0; i < 3; i++) {irsend.sendSony(0xa90, 12); // "12" is the bit number, different protocols call for different bit numbers. NEC is 32, Sony is 12, you can look up the others delay(10); for (int i = 0; i < 3; i++) {irsend.sendSony(0xa90, 12); delay(100);}}}}} {if (analogRead(Button4)>900) {for (int i = 0; i < 2; i++) {irsend.sendSony(0x290, 12); // Sony TV Mute delay(100);}}} {if (analogRead(Button3)>900) {for (int i = 0; i < 3; i++) {irsend.sendSony(0x490, 12); // Sony TV power Volume Up delay(100);}}} {if (analogRead(Button2)>900) {for (int i = 0; i < 3; i++) {irsend.sendSony(0xc90, 12); // Sony TV power Volume Down delay(100);}}} delay(100);} if (irrecv.decode(&results)) // the below portion of the code enables you to interpret Infra red signals from various remotes. { Serial.println(results.value, HEX); // it will generate the procedure "NEC, Sony, Etc.." and a TV code "c90, a90,FF02FD " you’ll need to add 0x to the front of the TV Code switch (results.decode_type) {case DENON: Serial.println("DENON"); break ; case NEC: Serial.println("NEC"); break ; case PANASONIC: Serial.println("PANASONIC"); break ; case SONY: Serial.println("SONY"); break ; case RC5: Serial.println("RC5"); break ; case JVC: Serial.println("JVC"); break ; case SANYO: Serial.println("SANYO"); break ; case MITSUBISHI: Serial.println("MITSUBISHI"); break ; case SAMSUNG: Serial.println("SAMSUNG"); break ; case LG: Serial.println("LG"); break ; case RC6: Serial.println("RC6"); break ; case DISH: Serial.println("DISH"); break ; case SHARP: Serial.println("SHARP"); break ; case WHYNTER: Serial.println("WHYNTER"); break ; case AIWA_RC_T501: Serial.println("AIWA_RC_T501"); break ; default: case UNKNOWN: Serial.println("UNKNOWN"); break;} irrecv.resume();}}

Step 3: Code in Depth: Sending IR Signals

I'll be referring to lines of code by their line number - to follow along, use this link:

First, we need to include the IR Remote Library by z3t0.

Here is a link to the library:

If you need a guide on how to properly download a library and install it in the IDE:

Line 1 includes the library.

Next, we need to declare a few variables, lines 2-12 do this.

We use "cost int" to define variables that won't change, all but one fall in this category.

We use "int" to define variables that will change.

We must use a pin with pulse with modulation (PWM) for our LED pin - any pin that has "~" next to it will suffice, in my code - we use digital pin 3.

Next, we need to do some setup - this code will run only once when the Arduino is powered up or reset.

Notice that we are defining our inputs and outputs (15-20), firing up the serial monitor (21), enabling the IR sensor (22), and telling the Arduino to blink the onboard LED anytime we get a signal in the sensor (23).

Next, we will build out our loop - this code will run repeatedly, going from the top to the bottom a handful of times a second.

At line 25, we use an if statement, this tells the Arduino "look for this specific criteria, if that criteria is met, do this specific thing". In this case, the criteria is analogRead(Button1)>900, or in other words - "Arduino, Look at button1, which we defined as pin A4 earlier, if the analog signal received is greater than 900, please proceed to our next instructions, if not, please move on". There is a bit to unpack here, so lets dive in: an analog signal on the Arduino is a value equal to or less than 5V, with 5V equaling 1023, and 0V equaling 0. Any given voltage between 0 and 5V can be defined by a number, and with a bit of math, we can figure out that number, or vice versa, a voltage. Divide 1024 (we include 0 as a unit) by 5, that gives us 204.8 . For example, we use the number 900, to translate that into voltage, we simply divide 900 by 204.8, giving us ~4.4V. We are telling the Arduino to look for a voltage greater than ~4.4 volts, and if it is, do our next instruction.

Speaking of next instructions (line 25), we see irsend.sendNEC(0xFF02FD,32). This says "Arduino, send a modulated pulse that follows the NEC protocol, specifically the FF02FD signal, and make sure its 32 bits long". This will make our IR LED flicker in way that other devices can understand. Think of it a little like Morse Code, but just with invisible light! There are a lot of different protocols out there, each with hundreds if not thousands of individual signals, and each with their specific bit number - our device will be able to recognize a great amount of these signals, but we will dive into that later!

At line 28, we have our first delay - this is here to prevent unintentional repeat signals, once the button is pushed and the IR signal is sent, we have 100 milliseconds to get our finger off the button. this doesn't sound like a lot of time, but in practice, it seems to work fine. the delay function tells the Arduino "do nothing for X milliseconds" and for reference, their are 1000 milliseconds in a second.

Moving on to our next button at line 29, button5 (I originally had 4 buttons on this remote, added a fifth, so that's why we are out of order). This, in spirit, is the same thing as button 1, but with a few key differences. The first difference you'll see is a for statement - this is essentially another loop - a loop with in another larger loop, loopception. Specifically we have "for (int i = 0; i < 3; i++)", read this as "Arduino, lets start at 0, repeat the following instructions until we get to 3 times". The for function is used because a lot of devices are programmed to look for a repeated signal, and in our case here, 3 times. You can simply change the number 3 into a different number if your device calls for a different repetition schedule. Another key difference with button5 is that it is repeated again, 3 times, or 3x3. In other words, we send the signal 3 times, wait 10 milliseconds, send it again 3 times, wait another 10 milliseconds, and then send it 3 times again. This type of communication is common for powering devices on and off and might just be what your TV or device calls for - the key to this is to play around with all of the variables until you get the desired result. Change the short delay value, change the for repeat value, send 6 batches instead of 3, etc. Devices are programmed with arbitrary signal rules intentionally, imagine if your TV remote sent the same type signal as your sound bar; every time you changed the channel on your TV, your sound bar shut off - that is why there are different signal rules.

The next three buttons are programed with the same principals, at least in part, described above - so we can skip all the way down to line 55.

Step 4: Code in Depth: Receiving IR Signals

At line 55, we begin programming the Arduino to interpret IR signals sent by other remotes - this is necessary so you can figure out the protocols and signals that your remotes utilize. The first line of code at line 55 is if (irrecv.decode(&results) read this as "Arduino, look for an IR code, if you find one, return a true value, if nothing found, return false. When true, record the information into "results"".

Moving on to line 56, we have Serial.println(results.value, HEX) this says "Ardunio, print the results in the serial monitor in a HEX format". Hex, meaning hexadecimal, is a way we can shorten a binary string (just 0's and 1's) into something a little easier to type. For instance 101010010000 is "a90", the code used to power my TV off and on, and 111111110000001011111101 is 0xFF02FD, which controls my RGB strip. You can use the above chart to convert binary into hex, and vice versa, or you can use the following link:

Down to line 57, we have a new function, called a switch case.

Essentially, a switch case allows us to specify different instructions based on the results of a given variable (case). the breakexits the switch statement, and is used at the end of each statement.

We use the switch case here to change how we print in the serial monitor based on the protocols our Arduino senses from the various remotes.

Step 5: Conclusion

If you have a question - please feel free to reach out to me on here! I am happy to try to help you the best I can.

I hope you learned something that you can use to make your life just a bit better!