Controlling a Servo with IR

Setting up the Example

In this example we will control the servo using an IR remote. We can adjust the speed that the servo moves and we can select individual preset angles for positioning the servo.

Here is an illustration showing how to wire up the devices. As usual we have an IR receiver connected to +5v, ground, and pin 11. We also have a servo with three wires. The red wire is +5v. The black or dark brown wire is ground and the remaining wire usually yellow is the signal wire which we have connected to pin 9 although it could be any digital output pin.

NOTE: The above illustration shows the receiver on pin 11 however the sample code uses pin 2. Any digital input pin can be used.

Upload the Code

Below is a version of the servo.ino sketch from the "IRLib2/examples" folder. It has been modified to be used with the Adafruit Mini Remote. If you're using a different remote, you will have to collect information about your codes for various buttons using dump.ino and modify the sketch with the proper protocol name and codes.

Download: file
#include <IRLibAll.h>
#include <Servo.h> 

// You will have to set these values depending on the protocol
// and remote codes that you are using. These are For the Adafruit
// Mini Remote
#define MY_PROTOCOL NEC
#define RIGHT_ARROW   0xfd50af //Move several clockwise
#define LEFT_ARROW    0xfd10ef //Move servo counterclockwise
#define SELECT_BUTTON 0xfd906f //Center the servo
#define UP_ARROW      0xfda05f //Increased number of degrees servo moves
#define DOWN_ARROW    0xfdb04f //Decrease number of degrees servo moves
#define BUTTON_0 0xfd30cf  //Pushing buttons 0-9 moves to fixed positions
#define BUTTON_1 0xfd08f7  // each 20 degrees greater
#define BUTTON_2 0xfd8877
#define BUTTON_3 0xfd48b7
#define BUTTON_4 0xfd28d7
#define BUTTON_5 0xfda857
#define BUTTON_6 0xfd6897
#define BUTTON_7 0xfd18e7
#define BUTTON_8 0xfd9867
#define BUTTON_9 0xfd58a7

IRrecv myReceiver(2); //pin number for the receiver
IRdecode myDecoder;

Servo myServo;  // create servo object to control a servo 
int16_t pos;         // variable to store the servo position 
int16_t Speed;       // Number of degrees to move each time a left/right button is pressed
uint32_t Previous;//handles NEC repeat codes

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.

If the servo behaves erratically, it may be a power supply problem. Some USB ports do not deliver sufficient current to drive the Arduino and move the servo. You may need to add an external 5 volt supply. Here is a video demonstrating this example.

Temporarily unable to load embedded content:
Temporarily unable to load embedded content:
Temporarily unable to load embedded content:

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.

Special Instructions for ATmega32u4 based systems

The example as presented here should work okay on Arduino Uno or Mega however if you are using Arduino Leonardo, Arduino Micro, Arduino Yun or other ATmega32u4 based systems, you will have to make a slight modification to IRLib.

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:

Download: file
#elif defined(__AVR_ATmega32U4__)
	#ifdef CORE_TEENSY
		// it's Teensy 2.0
		//#define IR_SEND_TIMER1	14
		//#define IR_SEND_TIMER3	9
		#define IR_SEND_TIMER4_HS	10
	#else
	/* it's probably Leonardo */
		#define IR_SEND_TIMER1		9
		//#define IR_SEND_TIMER3	5
		//#define IR_SEND_TIMER4_HS	13
	#endif

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.

This guide was first published on Feb 26, 2015. It was last updated on Feb 26, 2015. This page (Controlling a Servo with IR) was last updated on Oct 19, 2019.