The Arduino can interact with almost any device that uses some form of remote control, including TVs, audio equipment, cameras, garage doors, appliances, and toys. Most remote controls work by sending digital data from a transmitter to a receiver using infrared light (IR) or wireless radio technology. Different protocols (signal patterns) are used to translate key presses into a digital signal, and the recipes in this chapter show you how to use commonly found remote controls and protocols.
An IR remote works by turning an LED on and off in patterns to produce unique codes. The codes are typically 12 to 32 bits (pieces of data). Each key on the remote is associated with a specific code that is transmitted when the key is pressed. If the key is held down, the remote usually sends the same code repeatedly, although some remotes (e.g., NEC) send a special repeat code when a key is held down. For Philips RC-5 or RC-6 remotes, a bit in the code is toggled each time a key is pressed; the receiver uses this toggle bit to determine when a key is pressed a second time. You can read more about the technologies used in IR remote controls on the SB-Projects site.
The IR recipes here use a low-cost IR receiver module to detect the signal and provide a digital output that the Arduino can read. The digital output is then decoded by a library called IRremote, which was written by Ken Shirriff and can be installed using the Arduino IDE Library Manager (see Chapter 16).
The same library is used in the recipes in which Arduino sends commands to act like a remote control.
Remote controls using wireless radio technology are more difficult to emulate than IR controls. However, the button contacts on these controls can be activated by Arduino. The recipes using wireless remotes simulate button presses by closing the button contacts inside the remote control. With wireless remotes, you may need to take apart the remote control and connect wires from the contacts to Arduino to be able to use these devices. Components called optocouplers are used to provide electrical separation between Arduino and the remote control. This isolation prevents voltages from Arduino from harming the remote control, and vice versa.
Optocouplers (also called optoisolators) enable you to safely control another circuit that may be operating at different voltage levels from Arduino. As the “isolator” part of the name implies, optoisolators provide a way to keep things electrically separated. These devices contain an LED, which can be controlled by an Arduino digital pin. The light from the LED in the optocoupler shines onto a light-sensitive transistor. Turning on the LED causes the transistor to conduct, closing the circuit between its two connections—the equivalent of pressing a switch.
Arduino responds to IR remote signals using a device called an IR receiver module. Common devices are the TSOP38238, TSOP4838, PNA4602, and TSOP2438. The first three have the same connections, so the circuit is the same; the TSOP2438 has the +5V and GND pins reversed. Check the datasheet for your device to ensure that you connect it correctly.
This recipe uses the IRremote library, which you can install with the Library Manager. Connect the IR receiver module according to your datasheet. The Arduino wiring in Figure 10-1 is for the TSOP38238/TSOP4838/PNA4602 devices.
This sketch will toggle the built-in LED when any button on an infrared remote control is pressed:
/*
IR_remote_detector sketch
An IR remote receiver is connected to pin 2.
The built-in LED toggles each time a button on the remote is pressed.
*/
#include <IRremote.h>
// adds the library code to the sketch
const
int
irReceiverPin
=
2
;
// pin the receiver is connected to
const
int
ledPin
=
LED_BUILTIN
;
IRrecv
irrecv
(
irReceiverPin
);
// create an IRrecv object
decode_results
decodedSignal
;
// stores results from IR detector
void
setup
()
{
pinMode
(
ledPin
,
OUTPUT
);
irrecv
.
enableIRIn
();
// Start the receiver object
}
bool
lightState
=
LOW
;
// keep track of whether the LED is on
unsigned
long
last
=
millis
();
// remember when we last received an IR message
void
loop
()
{
if
(
irrecv
.
decode
(
&
decodedSignal
)
==
true
)
// this is true if a message has
// been received
{
if
(
millis
()
-
last
>
250
)
{
// has it been 1/4 sec since last message?
if
(
lightState
==
LOW
)
lightState
=
HIGH
;
else
lightState
=
LOW
;
lightState
=
lightState
;
// Yes: toggle the LED
digitalWrite
(
ledPin
,
lightState
);
}
last
=
millis
();
irrecv
.
resume
();
// watch out for another message
}
}
If you are using a 3.3V board that is not 5-volt tolerant, you should power the infrared receiver from 3.3V instead of 5V.
The IR receiver converts the IR signal to digital pulses. These are a sequence of ones and zeros that correspond to buttons on the remote. The IRremote library decodes these pulses and provides a numeric value for each key (the actual values that your sketch will receive are dependent on the specific remote control you use).
#include <IRremote.h>
at the top of the sketch makes the library code available to your sketch, and the line IRrecv irrecv(irReceiverPin);
creates an IRrecv
object named irrecv
to receive signals from an IR receiver module connected to irReceiverPin
(pin 2 in the sketch). Chapter 16 has more on using libraries.
You use the irrecv
object to access the signal from the IR receiver. You can give it commands to look for and decode signals. The decoded responses provided by the library are stored in a variable named decode_results
. The receiver object (irrecv
) is started in setup
with the line irrecv.enableIRIn();
. The results are checked in loop
by calling the function irrecv.decode(&decodedSignal)
.
The decode
function returns true
if there is data, which will be placed in the decodedSignal
variable. Recipe 2.11 explains how the ampersand symbol is used in function calls where parameters are modified so that information can be passed back.
If a remote message has been received, the code toggles the LED (flips its state) if it is more than one-quarter of a second since the last time it was toggled (otherwise, the LED will get turned on and off quickly by remotes that send codes more than once when you press the button, and may appear to be flashing randomly).
The decodedSignal
variable will contain a value associated with a key. This value is ignored in this recipe (although it is used in the next recipe)—you can print the value by adding to the sketch the Serial.println
line highlighted in the following code (you will also need to add Serial.begin(9600);
in the setup
function):
if
(
irrecv
.
decode
(
&
decodedSignal
)
=
=
true
)
// this is true if a message has
// been received
{
if
(
millis
(
)
-
last
>
250
)
{
// has it been 1/4 sec since last message
Serial
.
println
(
decodedSignal
.
value
)
;
The library needs to be told to continue monitoring for signals, and this is achieved with the line irrecv.resume();
.
This sketch flashes an LED when any button on the remote control is pressed, but you can control other things—for example, you can use a stepper motor to turn a knob on a lamp or stereo (for more on controlling physical devices, see Chapter 8).
This sketch uses remote control key presses to adjust the brightness of an LED. The code prompts for remote control keys 0 through 4 when the sketch starts. These codes are stored in Arduino memory (RAM), and the sketch then responds to these keys by setting the brightness of an LED to correspond with the button pressed, with 0 turning the LED off and 1 through 4 providing increased brightness:
/*
* RemoteDecode sketch
* Infrared remote control signals are decoded to control LED brightness.
* The values for keys 0-4 are detected and stored when the sketch starts.
* Key 0 turns the LED off; brightness increases in steps with keys 1-4.
*/
#include <IRremote.h>
// IR remote control library
const
int
irReceivePin
=
2
;
// pin connected to IR detector output
const
int
ledPin
=
9
;
// LED is connected to a PWM pin
const
int
numberOfKeys
=
5
;
// 5 keys are learned (0 through 4)
long
irKeyCodes
[
numberOfKeys
];
// holds the codes for each key
IRrecv
irrecv
(
irReceivePin
);
// create the IR library
decode_results
results
;
// IR data goes here
void
setup
()
{
Serial
.
begin
(
9600
);
while
(
!
Serial
);
// Needed for Leonardo and ARM boards
pinMode
(
irReceivePin
,
INPUT
);
pinMode
(
ledPin
,
OUTPUT
);
irrecv
.
enableIRIn
();
// Start the IR receiver
learnKeycodes
();
// learn remote control key codes
Serial
.
println
(
"Press a remote key"
);
}
void
loop
()
{
long
key
;
int
brightness
;
if
(
irrecv
.
decode
(
&
results
))
{
// here if data is received
irrecv
.
resume
();
key
=
convertCodeToKey
(
results
.
value
);
if
(
key
>=
0
)
{
Serial
.
(
"Got key "
);
Serial
.
println
(
key
);
brightness
=
map
(
key
,
0
,
numberOfKeys
-
1
,
0
,
255
);
analogWrite
(
ledPin
,
brightness
);
}
}
}
/*
* get remote control codes
*/
void
learnKeycodes
()
{
while
(
irrecv
.
decode
(
&
results
))
// empty the buffer
irrecv
.
resume
();
Serial
.
println
(
"Ready to learn remote codes"
);
long
prevValue
=
-
1
;
int
i
=
0
;
while
(
i
<
numberOfKeys
)
{
Serial
.
(
"press remote key "
);
Serial
.
(
i
);
while
(
true
)
{
if
(
irrecv
.
decode
(
&
results
))
{
if
(
results
.
value
!=
-
1
&&
results
.
decode_type
!=
UNKNOWN
&&
results
.
value
!=
prevValue
)
{
showReceivedData
();
Serial
.
println
(
results
.
value
);
irKeyCodes
[
i
]
=
results
.
value
;
i
=
i
+
1
;
prevValue
=
results
.
value
;
irrecv
.
resume
();
// Receive the next value
break
;
}
irrecv
.
resume
();
// Receive the next value
}
}
}
Serial
.
println
(
"Learning complete"
);
}
/*
* converts a remote protocol code to a logical key code
* (or -1 if no digit received)
*/
int
convertCodeToKey
(
long
code
)
{
for
(
int
i
=
0
;
i
<
numberOfKeys
;
i
++
)
{
if
(
code
==
irKeyCodes
[
i
])
{
return
i
;
// found the key so return it
}
}
return
-
1
;
}
/*
* display the protocol type and value
*/
void
showReceivedData
()
{
if
(
results
.
decode_type
==
UNKNOWN
)
{
Serial
.
println
(
"-Could not decode message"
);
}
else
{
if
(
results
.
decode_type
==
NEC
)
{
Serial
.
(
"- decoded NEC: "
);
}
else
if
(
results
.
decode_type
==
SONY
)
{
Serial
.
(
"- decoded SONY: "
);
}
else
if
(
results
.
decode_type
==
RC5
)
{
Serial
.
(
"- decoded RC5: "
);
}
else
if
(
results
.
decode_type
==
RC6
)
{
Serial
.
(
"- decoded RC6: "
);
}
Serial
.
(
"hex value = "
);
Serial
.
println
(
results
.
value
,
HEX
);
}
}
This solution is based on the IRremote library; see this chapter’s introduction for details.
The sketch starts the remote control library with the following code:
irrecv
.
enableIRIn
();
// Start the IR receiver
It then calls the learnKeyCodes
function to prompt the user to press keys 0 through 4. The code for each key is stored in an array named irKeyCodes
. After all the keys are detected and stored, the loop
code waits for a key press and checks if this was one of the digits stored in the irKeyCodes
array. If so, the value is used to control the brightness of an LED using analogWrite
.
See Recipe 5.7 for more on using the map
function and analogWrite
to control the brightness of an LED.
The library should be capable of working with most any IR remote control; it can discover and remember the timings and repeat the signal on command.
You can permanently store the key code values so that you don’t need to learn them each time you start the sketch. Replace the declaration of irKeyCodes
with the following lines to initialize the values for each key, and comment out the call to learnKeycodes();
. Change the values to coincide with the ones for your remote (these will be displayed in the Serial Monitor when you press keys in the learnKeyCodes
function):
long
irKeyCodes
[
numberOfKeys
]
=
{
0x18E758A7
,
//0 key
0x18E708F7
,
//1 key
0x18E78877
,
//2 key
0x18E748B7
,
//3 key
0x18E7C837
,
//4 key
};
Recipe 18.1 explains how you can store learned data in EEPROM (nonvolatile memory).
You want to use Arduino to control a TV or other remotely controlled appliance by emulating the infrared signal. This is the inverse of Recipe 10.2—it sends commands instead of receiving them.
This sketch uses the remote control codes from Recipe 10.2 to control a device (your remote codes are likely to differ so make sure you run that recipe’s Solution code and use the values unique to your remote). Five buttons select and send one of five codes. Connect an infrared LED to send the signal as shown in Figure 10-2:
/*
* irSend sketch
* this code needs an IR LED connected to pin 3
* and 5 switches connected to pins 6 - 10
*/
#include <IRremote.h>
// IR remote control library
const
int
numberOfKeys
=
5
;
const
int
firstKey
=
6
;
// the first pin of the 5 sequential pins connected
// to buttons
bool
buttonState
[
numberOfKeys
];
bool
lastButtonState
[
numberOfKeys
];
long
irKeyCodes
[
numberOfKeys
]
=
{
0x18E758A7
,
//0 key
0x18E708F7
,
//1 key
0x18E78877
,
//2 key
0x18E748B7
,
//3 key
0x18E7C837
,
//4 key
};
IRsend
irsend
;
void
setup
()
{
for
(
int
i
=
0
;
i
<
numberOfKeys
;
i
++
)
{
buttonState
[
i
]
=
true
;
lastButtonState
[
i
]
=
true
;
int
physicalPin
=
i
+
firstKey
;
pinMode
(
physicalPin
,
INPUT_PULLUP
);
// turn on pull-ups
}
Serial
.
begin
(
9600
);
}
void
loop
()
{
for
(
int
keyNumber
=
0
;
keyNumber
<
numberOfKeys
;
keyNumber
++
)
{
int
physicalPinToRead
=
keyNumber
+
firstKey
;
buttonState
[
keyNumber
]
=
digitalRead
(
physicalPinToRead
);
if
(
buttonState
[
keyNumber
]
!=
lastButtonState
[
keyNumber
])
{
if
(
buttonState
[
keyNumber
]
==
LOW
)
{
irsend
.
sendSony
(
irKeyCodes
[
keyNumber
],
32
);
Serial
.
println
(
"Sending"
);
}
lastButtonState
[
keyNumber
]
=
buttonState
[
keyNumber
];
}
}
}
You won’t see anything when the codes are sent because the light from the infrared LED isn’t visible to the naked eye.
However, you can verify that an infrared LED is working with a digital camera—you should be able to see it flashing in the camera’s LCD viewfinder.
Arduino controls the device by flashing an IR LED to duplicate the signal that would be sent from your remote control. This requires an IR LED. The specifications are not critical; see Appendix A for suitable components.
The IR library handles the translation from numeric code to IR LED flashes. You need to create an object for sending IR messages. The following line creates an IRsend
object that will control the LED on pin 3 (you are not able to specify which pin to use; this is hardcoded within the library):
IRsend
irsend
;
Depending on which board you are using, the IRremote library may require a different pin for the infrared LED. For example, Teensy 3.x boards use pin 5. See the README for the IRremote library. If your board uses a pin that overlaps with the range of pins used for the buttons (pins 6–10 in the Solution sketch), you may need to change the range of pins.
The code uses an array (see Recipe 2.4) called irKeyCodes
to hold the range of values that can be sent. It monitors five switches to see which one has been pressed and sends the relevant code in the following line:
irsend
.
sendSony
(
irKeyCodes
[
keyNumber
],
32
);
The irSend
object has different functions for various popular infrared code formats, so check the library documentation if you are using one of the other remote control formats. You can use Recipe 10.2 if you want to display the format used in your remote control.
The sketch passes the code from the array, and the number after it tells the function how many bits long that number is. The 0x at the beginning of the numbers in the definition of irKeyCodes
at the top of the sketch means the codes are written in hex (see Chapter 2 for details about hex numbers). Each character in hex represents a 4-bit value. The codes here use eight characters, so they are 32 bits long.
The LED is connected with a current-limiting resistor (see the introduction to Chapter 7).
If you need to increase the sending range, you can use multiple LEDs or select one with greater output.
There are a few ways to do this. If your camera has an infrared remote, use Recipe 10.2 to learn the relevant remote codes and Recipe 10.3 to get Arduino to send those codes to the camera.
If your camera doesn’t have an infrared remote but does have a socket for a wired remote, you can use this recipe to control the camera.
A camera shutter connector, usually called a TRS (tip, ring, sleeve) connector, typically comes in 2.5 mm or 3.5 mm sizes, but the length and shape of the tip may be nonstandard. The safest way to get the correct plug is to buy a cheap wired remote switch for your model of camera and modify that or buy an adapter cable from a specialist supplier (Google “TRS camera shutter”).
You connect the Arduino to a suitable cable for your camera using optocouplers, as shown in Figure 10-3.
This sketch takes 10 pictures, one every 20 seconds:
/*
* camera sketch
* takes 20 pictures with a digital camera
* using pin 4 to trigger focus
* pin 3 to trigger the shutter
*/
int
focus
=
4
;
// optocoupler attached to focus
int
shutter
=
3
;
// optocoupler attached to shutter
long
exposure
=
250
;
// exposure time in milliseconds
long
interval
=
10000
;
// time between shots, in milliseconds
void
setup
()
{
pinMode
(
focus
,
OUTPUT
);
pinMode
(
shutter
,
OUTPUT
);
for
(
int
i
=
0
;
i
<
20
;
i
++
)
// camera will take 20 pictures
{
takePicture
(
exposure
);
// takes picture
delay
(
interval
);
// wait to take the next picture
}
}
void
loop
()
{
// once it's taken 20 pictures it is done,
// so loop is empty
// but loop still needs to be here or the
// sketch won't compile
}
void
takePicture
(
long
exposureTime
)
{
int
wakeup
=
10
;
// camera will take some time to wake up and focus
// adjust this to suit your camera
digitalWrite
(
focus
,
HIGH
);
// wake the camera and focus
delay
(
wakeup
);
// wait for it to wake up and focus
digitalWrite
(
shutter
,
HIGH
);
// open the shutter
delay
(
exposureTime
);
// wait for the exposure time
digitalWrite
(
shutter
,
LOW
);
// release shutter
digitalWrite
(
focus
,
LOW
);
// release the focus
}
It’s not advisable to connect Arduino pins directly to a camera—the voltages may not be compatible and you risk damaging your Arduino or your camera. Optocouplers are used to isolate Arduino from your camera; see the introduction of this chapter for more about these devices.
You will need to check the user manual for your camera to identify the correct TRS connector to use.
You may need to change the order of the pins turning on and off in the takePicture
function to get the behavior you want. For a Canon camera to do bulb exposures, you need to turn on the focus, then open the shutter without releasing the focus, then release the shutter, and then release the focus (as in the sketch). To take a picture and have the camera calculate the exposure, press the focus button, release it, and then press the shutter.
If you want to control aspects of a camera’s operation, have a look at the Canon Hack Development Kit.
Also see The Canon Camera Hackers Manual: Teach Your Camera New Tricks by Berthold Daum (Rocky Nook).
You can control a GoPro camera using an Arduino with WiFi using this library.
It is also possible to control video cameras in a similar fashion using LANC. This library supports this feature.
There is also an Arduino shield for controlling high-end Black Magic Design video equipment, including cameras using their open protocol.
Arduino can trigger the buttons of a remote-controlled switch using an optocoupler. This may be necessary for remotes that use wireless instead of infrared technology. This technique can be used for almost any remote control. Hacking a remote is particularly useful to isolate potentially dangerous AC voltages from you and Arduino because only the battery-operated controller is modified.
Opening the remote control will void the warranty and can potentially damage the device. The infrared recipes in this chapter are preferable because they avoid modifying the remote control.
If you want to use this recipe to control a switch, but you want to keep using the remote control, consider purchasing a spare remote control for hacking. Most manufacturers will be happy to sell you a spare (but make sure you choose the right frequency for the variant of appliance, light, or outlet you want to control). After you receive the spare, you may need to configure the channel that it uses.
Open the remote control and connect the optocoupler so that the photo-emitter (pins 1 and 2 in Figure 10-4) is connected to Arduino and the photo transistor (pins 3 and 4) is connected across the remote control contacts.
This sketch uses momentary contact switches (push and release) to activate the remote’s on and off buttons:
/*
* OptoRemote sketch
* Switches connected to pins 2 and 3 turn a remote device on and off
* using optocouplers.
* The outputs are pulsed for at least half a second when a switch is pressed
*/
const
int
onSwitchPin
=
2
;
// input pin for the On switch
const
int
offSwitchPin
=
3
;
// input pin for the Off switch
const
int
remoteOnPin
=
4
;
// output pin to turn the remote on
const
int
remoteOffPin
=
5
;
// output pin to turn the remote off
const
int
PUSHED
=
LOW
;
// value when button is pressed
void
setup
()
{
pinMode
(
remoteOnPin
,
OUTPUT
);
pinMode
(
remoteOffPin
,
OUTPUT
);
pinMode
(
onSwitchPin
,
INPUT_PULLUP
);
// turn on internal pull-ups
pinMode
(
offSwitchPin
,
INPUT_PULLUP
);
}
void
loop
(){
int
val
=
digitalRead
(
onSwitchPin
);
// read input value
// if the switch is pushed then switch on if not already on
if
(
val
==
PUSHED
)
{
pulseRemote
(
remoteOnPin
);
}
val
=
digitalRead
(
offSwitchPin
);
// read input value
// if the switch is pushed then switch off if not already off
if
(
val
==
PUSHED
)
{
pulseRemote
(
remoteOffPin
);
}
}
// turn the optocoupler on for half a second to blip the remote control button
void
pulseRemote
(
int
pin
)
{
digitalWrite
(
pin
,
HIGH
);
// turn the optocoupler on
delay
(
500
);
// wait half a second
digitalWrite
(
pin
,
LOW
);
// turn the optocoupler off
}
The switches in most remote controls consist of interleaved bare copper traces with a conductive button that closes a connection across the traces when pressed. Less common are controls that contain conventional push switches; these are easier to use as the legs of the switches provide a convenient connection point.
Though the original remote button and the optocoupler can be used together—the switching action is performed if either method is activated (pressing the button or turning on the optocoupler)—the wires tethered to Arduino can make this inconvenient.
The transistor in the optocoupler will only allow electricity to flow in one direction, so if it doesn’t work the first time, try switching the transistor-side connections over and see if that fixes it.
Some remotes have one side of all of the switches connected together (usually to the ground of that circuit). You can trace the connections on the board to check for this or use a multimeter to see what the resistance is between the traces on different switches. If traces have common connections, it is only necessary to connect one wire to each common group. Fewer traces are easier because connecting the wires can be fiddly if the remote is small.
The remote control may have multiple contacts corresponding to each button. You may need more than one optocoupler for each button position to connect the contacts. Figure 10-5 shows three optocouplers controlled from a single Arduino pin.
Another approach to controlling AC line currents is to use an isolated relay that can be switched on and off directly from Arduino pins, such as the Digital Loggers IoT Power Relay, available from Adafruit (part 2935) or SparkFun (part 14236). The IoT Power Relay is a great alternative to the now-discontinued PowerSwitch Tail.
Optocouplers are used in Recipe 10.4, so check out that recipe for an example of how they are used in a circuit.