Using An Infrared Library
Using An Infrared Library
https://learn.adafruit.com/using-an-infrared-library
Overview 3
About IR libraries 3
Sending IR Codes 17
• Hardware Issues
• Loading the Software
• Sending and Receiving in the Same Program
Most consumer electronic devices such as TV, cable box, DVD players and other
devices use infrared signals for remote control. Each manufacturer has its own
protocols for encoding the data so that signals intended for one device do not
interfere with another. In an earlier tutorial by LadyAda () she describes the inner
working of reading IR signals from a remote and creating your own IR signals using an
IR LED. Her tutorial gives you a behind-the-scenes peek at how IR works but it's a bit
difficult to wrestle with all those technical details in your project.
If you're like me, you have no idea how NeoPixels work, nor the inner workings of I2C
or SPI communications but you don't need to because we have libraries for that. A
good code library isolates you, the application programmer, from the hardware details
and the inner workings of devices and provide you with an API that makes it easy to
use the hardware without knowing or caring what's going on behind the scenes.
In this tutorial we will show you how to use IRLib for receiving, decoding, and sending
IR signals in your Arduino based project. We will show you how to change colors on a
NeoPixel, control a servo using IR remote and send signals to your TV or cable box
from an Arduino. Future tutorials will include an IR control mouse, and Internet of
things remote control and controlling a robot arm.
About IR libraries
IR signals consists of a series of modulated pulses called "marks" separated by
intervals called "spaces". Typically there is a long mark and space at the beginning of
each signal that serves as a header. Then by varying the timing of marks and spaces,
a sequence of bits is transmitted. If you had to store the precise timing of the entire
signal it would take an array of up to 100 16 bit integers. In order to compare the data
received to see if it was what you wanted, you would similarly need to store large
arrays of data.
Fortunately the signals are sent according to very specific protocols that allow you to
take this received timing data and turn it into a single binary number of up to 32 bits.
The IR library collects the timing information in a buffer and then turns it into a single
32 bit value. You can then easily compare that value to the one you want.
The gold standard of IR libraries is "LIRC" or Linux Infrared Remote Control which can
be found at http://www.lirc.org/ (). It consists of drivers and a large database of
information on hundreds of various remote controls. If we are using a Linux based
system it's definitely the way to go. We will not be discussing that library here as we
intend to focus on Arduino based systems.
In August 2009 Ken Shirrff published "IRremote" on his blog () and released it on
GitHub (). Then in January 2013 I released IRLib based on Ken's earlier work. This
revision reorganize the code making it easier to add new protocols using object-
oriented programming design in C++.
In September 2016 we released a major rewrite of IRLib called IRLib 2.0. It was not
fully backward-compatible with the original IRLib. This tutorial has been updated to
use IRLib 2.0. The previous IRLib 1.x will no longer be supported. Recently IRLib 2.03
was released which added support for 32-bit SAMD 21 processors such as those used
in Arduino Zero, Adafruit Feather M0, and the upcoming Circuit Playground Express.
This repository consists of a total of five libraries each of which must be in your
arduino/libraries/ folder. So for example it should be installed as follows…
• arduino/libraries/IRLib2
• arduino/libraries/IRLibFreq
• arduino/libraries/IRLibProtocols
• arduino/libraries/IRLibRecv
• arduino/libraries/IRLibRecvPCI
• arduino/libraries/IRLib2_master
◦ IRLib2
◦ IRLibFreq
◦ IRLibProtocols
◦ IRLibRecv
◦ IRLibRecvPCI
Here’s a tutorial () that walks through the process of correctly installing Arduino
libraries.
Hardware Needed
IRLib runs on 8-bit AVR based Arduino boards such as Uno, Leonardo, Mega and
Micro. It also runs on the Leonardo portion of the Arduino Yun. We have recently
added support for 32 bit ARM SAMD 21 processors as used in the Arduino Zero,
Feather M0, and Circuit Playground Express. However we do not support the Arduino
Due or other Arduino-like systems. Unfortunately at this time it does not run on
ATtiny85 base systems such as Adafruit Trinket and Adafruit Gemma but support for
that is in the job jar and should be available in a stripped-down version for those
platforms in the near future. At this writing, it has not been tested on the Adafruit
Trinket Pro however since it is based on the same ATmega328 processor as the Uno,
it should work fine. So the first thing you need is Arduino Uno or other compatible
board.
You will need an IR receiver. Such as the TSOP38238 shown on the right column
under featured products. This device combines an IR sensitive photocell, a 38 kHz
Finally you will need an IR remote such as you use for controlling your TV, cable box,
or DVD player. All of our examples will use the Adafruit Mini Remote Control shown on
the right however we will show you how to detect what protocol your own TV remote
is using and if it is a protocol supported by IRLib you can use it instead.
Connecting the IR receiver is very simple. Connect the left-hand pin to any digital
input pin on your Arduino. In our examples we will use pin 2. Connect the center pin
to ground and the right-hand pin to +5v.
Note that this device has a bandpass filter tuned to 38 kHz which is the typical
frequency for most protocols. However some protocols use frequencies from 36 kHz
all the way up to 57 kHz. The filter however is not extremely specific and we have had
good success receiving anywhere from 36-40 kHz using a 38 kHz receiver. The
Panasonic_Old protocol however uses 56 kHz. The TSOP38238 sold by Adafruit has
difficulty decoding that frequency. I have however had good success with the receiver
sold by Radio Shack at 56 kHz even though it is a 38 kHz device. Radio Shock did not
These devices are made by Vishay and come in a variety of package styles,
frequencies, and AGC methods. If the Adafruit device does not work for you and you
need 56 kHz you can refer to the following guide.
More information on receivers as well as schematics for using multiple receivers can
be found in the IRLib manual section 1.4.3.
Decoding IR Data
Load the following sketch. It is a slightly modified version of "dump" sketch from the
examples folder of the library. All of the example sketches are in the folder "IRLib2/
examples".
#include "IRLibAll.h"
void setup() {
Serial.begin(9600);
delay(2000); while (!Serial); //delay for Leonardo
myReceiver.enableIRIn(); // Start the receiver
Serial.println(F("Ready to receive IR signals"));
}
void loop() {
//Continue looping until you get a complete signal received
if (myReceiver.getResults()) {
myDecoder.decode(); //Decode it
myDecoder.dumpResults(true); //Now print results. Use false for less detail
myReceiver.enableIRIn(); //Restart receiver
}
}
After the sketch has loaded, open your serial monitor and make sure it is set to 9600
baud. Aim your IR remote at the receiver and press a button. In this example we press
the "Play/Pause" button on the Adafruit Mini Remote. The results were as follows:
32:m500
Extent=65850
Mark min:450 max:550
The important part of this dump is the first line. This tells us that the protocol detected
was "NEC" which is protocol number "1" in IRLib's supported protocols. The data value
received was the 32-bit hexadecimal value FD807F. The rest of the information is the
raw timing data of the actual marks and spaces received. That information is useful in
trying to understand and supported protocols.
This 32-bit number uniquely identifies the button that you pushed. If we push the
Volume down and Volume up buttons on this remote we would get the
values 0xFD00FF and 0xFD40BF.
Try pressing various buttons on a TV or DVD remote you might have lying around the
house. If the top line says:
this means that IRLib did not understand the protocol used by your remote. Here are
some typical values from other remotes. I got these from the power button on a Sony
DVD player, and the play button on my Scientific Atlantic DVR/Cable Box.
This shows that the DVD player used Sony protocol which is protocol number 2 and
that it is a 20 bit protocol. The cable box uses Panasonic_Old protocol 5 which is 22
bits. Most protocols always use the same number of bits however some such as Sony
have different versions which could use 8, 12, or 15 bits in addition to 20.
You can access the protocol number in My_Decoder.protocolNum, the number of bits
in My_Decoder.bits and the the decoded data value in My_Decoder.value.
At the top of the sketch we created the decoder object as type "IRdecode". This is a
class which incorporates all 11 of the supported protocols. If you're using the library to
control a device such as a servo or turn relays off and on, you probably are going to
be using one remote with one protocol. Once you know which protocol you are using,
you may wish to use a different decoder class that only works for your particular
protocol. It can save valuable program space in your sketch. For example if we were
using the Adafruit Mini Remote which uses NEC protocol would change line #7 to
read:
IRdecodeNEC My_Decoder;
#define UNKNOWN 0
#define NEC 1
#define SONY 2
#define RC5 3
#define RC6 4
#define PANASONIC_OLD 5
#define JVC 6
#define NECX 7
#define SAMSUNG36 8
Here are some protocol specific issues you may have to deal with.
The NEC protocol uses a special sequence of marks and spaces that mean "I held
down the button so you should repeat whatever I sent you last time". It is up to you to
decide do I want to allow repeat codes or do I want to force the operator to push and
release the button each time. IRLib returns a value of 0xFFFFFFFF to tell you that the
special repeat sequence was received. You can ignore that sequence which forces
the user to release and repress the button each time or you can store the previously
received code and process it whenever you see the special repeat message.
The technical specification for Sony protocol says that you should send each code 3
consecutive times per keypress. IRLib takes care of sending three times for you so
you don't need to do anything special. However when receiving Sony, be aware that
you're going to get three copies of the data each time the user presses a button. If
you're busy processing the first sequence you might miss the other two so it won't
matter. But you need to be aware of it in case you're counting the number of
keypresses or some other application.
The RC5 and RC6 protocols invented by Phillips use a special toggle bit to let you
know whether a code was generated by holding down the button or whether this is
an independent keypress. For example I have a TV which uses RC5 protocol and the
code for Volume Up is 0x1010. If I press and hold that button it sends the same code
repeatedly. However if I release the button and press it again I get 0x1810. Every
other keypress the single bit 0x0800 will toggle off and on. You can make sure that
you ignore this feature by masking out that particular bit. When you receive a
decoded value from this protocol you could do:
My_Decoder.value &=0xf7ff;
This will make sure that the toggle bit is always off. The RC6 protocol also has a title
bit which is 0x10000. Therefore to mask it out you would do:
In this very simple example we will change the color of a NeoPixel by pushing buttons
on the remote. We are using a single pixel but you can modify the sketch to control an
entire strip or matrix. For more information on NeoPixels visit this guide in the Adafruit
Learning System (). Here is the code:
#include <Adafruit_NeoPixel.h>
#include <IRLibAll.h>
void setup() {
strip.begin();
strip.show(); // Initialize all pixels to 'off'
myReceiver.enableIRIn(); // Start the receiver
}
void loop() {
if (myReceiver.getResults()) {
myDecoder.decode();
if (myDecoder.protocolNum == NEC) {
switch(myDecoder.value) {
case 0xfd00ff: //Volume Down
strip.setPixelColor(0,255,0,0);//Red
break;
case 0xfd807f: //Play/Pause
strip.setPixelColor(0,0,255,0);//Green
break;
case 0xfd40bf: //Volume Up
We create a NeoPixel strip with one pixel connected to pin 6. Also create a receiver
object connected to pin 11 and a decoder object. Reinitialize the pixel strip and the IR
receiver in the setup routine. Then in the main loop continuously test the receiver to
see if it has received many IR signals. If myReceiver.getResults returns true then we
decode the data. We are using the Adafruit Mini Remote we used earlier. It used the
NEC protocol so we make sure that we actually received NEC protocol data. Then we
use a switch statement to test various 32-bit hex values against the decoded data in
My_Decoder.value.
After we have set the pixel color based on the received value, we need to call
strip.show() to actually change the color and myReceiver.enableIRIn() to reset the
receiver so it can collect another code.
Upload the sketch and try pressing the volume down, play/pause, and volume up
buttons. You should see the pixel change to red, green, or blue. You can easily add
additional case statements and colors or perhaps have one of the cases call an
animation routine to animate the entire strip of pixels. Different buttons on the remote
would select different animation patterns.
You will have to modify this sketch if you are using a different remote control. Change
the protocol type spuch as:
if (myDecoder.protocolNum==SONY)
for example if you are using a Sony remote. And of course you have to substitute the
proper codes in each case statement. The enumerated list of available protocols for
comparing decode_type can be found at approximately line 60 of IRLib.h.
#include <IRLibAll.h>
#include <Servo.h>
void setup() {
myServo.attach(9); // attaches the servo on pin 9 to the servo object
pos = 90; // start at midpoint 90 degrees
Speed = 3; // servo moves 3 degrees each time left/right is pushed
myServo.write(pos); // Set initial position
myReceiver.enableIRIn(); // Start the receiver
}
void loop()
{
if (myReceiver.getResults()) {
myDecoder.decode();
if(myDecoder.protocolNum==MY_PROTOCOL) {
if(myDecoder.value==0xFFFFFFFF)
myDecoder.value=Previous;
switch(myDecoder.value) {
case LEFT_ARROW: pos=min(180,pos+Speed); break;
case RIGHT_ARROW: pos=max(0,pos-Speed); break;
case SELECT_BUTTON: pos=90; break;
case UP_ARROW: Speed=min(10, Speed+1); break;
case DOWN_ARROW: Speed=max(1, Speed-1); break;
case BUTTON_0: pos=0*20; break;
case BUTTON_1: pos=1*20; break;
case BUTTON_2: pos=2*20; break;
case BUTTON_3: pos=3*20; break;
case BUTTON_4: pos=4*20; break;
case BUTTON_5: pos=5*20; break;
case BUTTON_6: pos=6*20; break;
case BUTTON_7: pos=7*20; break;
case BUTTON_8: pos=8*20; break;
case BUTTON_9: pos=9*20; break;
}
myServo.write(pos); // tell servo to go to position in variable 'pos'
Previous=myDecoder.value;
}
myReceiver.enableIRIn();
}
}
NOTE: If using Leonardo, Micro, or Yun or other ATmega32u4 System, See the
special instructions at the end of this page.
Upload the sketch and try pushing the left and right arrow buttons. The servo should
turn left and right. Pushing the enter button should center the servo. Pushing the up
or down arrow buttons will not have any visible effect but it will change the speed of
movement you push left or right. The numbered buttons from zero through nine move
the servo to 10 different fixed positions at 20° intervals.
How It Works
The program creates a receiver on object, a decoder object and a servo object. You
can find more information on the standard Arduino Servo library here (). The setup
function attaches the servo, enables IR input, and initializes several variables.
The loop function gets an IR code and passes it to a switch statement depending on
its value. Each case of the switch statement handles a different function moving the
servo as needed.
There is one bit of overhead we need to take care of because we are using NEC
protocol. That protocol has a unique feature that allows you to see if the button on
the remote has been held down to send repeated instances of the same value. It has
a special sequence of marks and spaces that mean "Repeat what you did last time".
When IRLib sees the special sequence, it returns the value 0xFFFFFFFF. We take care
of that special case by storing the previous value at the bottom of the switch
statement so that if we get a repeat code we can substitute it the next time. NEC
protocol is the only protocol that uses this particular method for detecting repeat
codes. Other protocols have other systems which are explained in detail in the IRLib
documentation.
IRLib uses your Arduino's built in hardware timers to generate an interrupt every 50µs
so it can poll the input pin to see if it has changed. By default it uses TIMER2. The
Arduino servo library also uses hardware interrupts using TIMER1. However the
ATmega32u4 processor does not have TIMER2 so IRLib defaults to TIMER1 on
systems using that processor. You will have to modify "IRLibProtocols/
IRLibHardware.h" to change the default timer. In that file at approximately line 56 you
will see something like this:
You will need to put // in front of #define IR_SEND_TIMER1 to comment out that line.
Then remove the slashes from in front of one of the other two options either TIMER3
or TIMER4_HS. Note that these defines say "IR_SEND_TIMERxx". Later in the file we
copy this value to also be used as the receiving timer. If you're using Leonardo and
you later use IRLib to send IR signals you will need to make note of the numbers after
these defines. Although we can hook a receiver to any digital input pin, IRLib requires
you to use specific output pins depending on which timer you are using. Will cover
that later in the section on sending.
Sending IR Codes
It could also be used to control some home automation devices. Some users have
attempted to control air conditioners and fans however protocols used for air-
conditioners are extremely difficult to implement and we have not directly supported
such protocols because they are very rare.
Typically the output pin of an Arduino cannot supply sufficient current to drive and IR
LED so you will want to implement a simple driver circuit using NPN transistor and a
470 ohm resistor is shown here:
Make sure that you get the polarity of the LED correct. The shorter of the two leads
connects to the transistor and the longer one connects to the positive supply. Note
that the current passing through the LED will in all likelihood will exceed the maximum
continuous current rating. However because the signal is modulated and sending a
sequence of pulses that will only last a few milliseconds the circuit will work fine.
More advanced driver circuit schematics are available in the IRLib manual in section
1.4 Hardware Considerations.
The default timer is TIMER2 on the Arduino Uno and Arduino Mega. On the Leonardo
with is TIMER1. The pin numbers are pin 3 for Uno and use pin 9 for the Leonardo and
Mega. If you have modified the library to use a different timer such as changing the
timer number on a Leonardo to avoid conflicts with the servo library, then you will
need to use a different specific pin. See section 1.4.1 Supported Platforms in the IRLib
users manual for a table which shows the relationship between hardware timers and
pin numbers.
#include <IRLibAll.h>
IRsend mySender;
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.read() != -1) {
//send a code every time a character is received from the serial port
//Sony DVD power A8BCA
mySender.send(SONY,0xa8bca, 20);
}
}
In this somewhat trivial example we send the code to turn on and off a Sony DVD
player every time you type a character into the serial monitor. We create a sending
object mySender. There is no set up except to initialize the serial port. In the loop we
check for incoming characters and if we've got one we send the code.
The send method has three parameters: the protocol type, the data, and the number
of bits.
The IRsend object we created is a generic routine that supports all 11 supported
protocols. However in this case since we are only using one protocol we could've
created the object using:
mySender.send(0xa8bca, 20);
Some protocols such as NEC always use the same number of bits and so you do not
need to specify as an additional parameter. See the the users manual to see if the
extra bits parameter is required.
The users manual also gives you information about how to create decoding and
sending routines that only use a specific subset of the supported protocols. This is
much easier to do in IRLib 2.0 than it was in the original version.
Normally you would only need to call myReceiver.enableIRIn(); once in the setup
routine but if you are both sending and receiving you have to call it after each send.
For a complete example of how to send and receive in the same program, look at the
record.ino sample sketch in the "IRLib2/examples" folder. When you load the sketch,
open your serial monitor, point a remote at the receiver and push a button. The
program will capture that code. Then every time the type of character into the serial
monitor it will repeat that code through the LED.