The code for this is going to look very similar to PROJ05 - it uses a similar structure to handle IR remote button presses, but with the servo instead of USB HID. We are also going to introduce three new concepts that you might've seen in previous CIRCs - random(), min() and max()

First, let's import both IRLib and Servo:

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

Then, we are going to include all of the remote-specific code. This is going to include #define's for remote values, the receiver pin assignment, the decoder object, and the ir protocol used. 

Download: file
      /* Adafruit Mini Remote */
#define MY_PROTOCOL NEC
#define RIGHT_ARROW   0xfd50af 
#define LEFT_ARROW    0xfd10ef 
#define SELECT_BUTTON 0xfd906f 
#define BUTTON_0 0xfd30cf  
#define BUTTON_1 0xfd08f7 
#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
// pin number for the receiver
IRrecv myReceiver(2); 
IRdecode myDecoder;
// handles nec repeat codes
uint32_t Previous;
    

Next, we are going to create a servo object myServo, a variable to store the servo position and a variable to hold the angle (in degrees) of the servo.

Download: file
      /* Servo */
// create a servo object 
Servo myServo;  
// stores the servo position
int16_t pos;        
// angle (degrees) to move the servo left/right
int16_t Speed;      
    

(Optional) If you're using a laser and are able to control it from the metro (see: assembly), we are going to connect the power to pin 11. We are also going to make a boolean, laserToggle, to turn the laser on and off. laserToggle holds one of two values - true or false. Depending on the button pressed and the current state, we can easily toggle the laser.

Download: file
      /* Laser */
// connect laser PWR to a pin 11
const int laserPin = 11;
// toggle the laser
bool laserToggle = false;
    

Our setup() code needs to set the laser pin as an output, attach a servo to pin 9, set the initial pos to 90, the initial pos to 90 and the initial Speed to 5. Then, we are going to write pos to the servo and start the IR receiver. 

Download: file
      void setup() { 
  // randomizes a seed for random() calls
  randomSeed(analogRead(0));
  // set the laser pin as an output
  pinMode(laserPin, OUTPUT);
  // attach servo to pin 9
  myServo.attach(9);      
  // set initial position
  pos = 90;              
  // set initial speed 
  Speed = 5;             
  // write initial pos to servo at startup
  myServo.write(pos);     
  // Start the IR receiver
  myReceiver.enableIRIn();
} 
    

As previously mentioned, this code is similar to PROJ05:

The loop() is quite complicated, but we'll break it down to make it easier. We are going to first detect if the receiver gets an input from the remote with myReceiver.getResults(). Then, we are going to decode it with a call to myDecoder.decode()

Next, we want to check if the protocol is the same as what's used by the Mini Remote, NEC, by checking if(myDecoder.protocolNum==MY_PROTOCOL). Finally, we are going to detect the repeat codes, and set the current value to the previous decoded value if(myDecoder.value==0xFFFFFFFF {myDecoder.value=Previous;}

Download: file
void loop() 
{ 
    if (myReceiver.getResults()) {
       myDecoder.decode();
       if(myDecoder.protocolNum==MY_PROTOCOL) {
         if(myDecoder.value==0xFFFFFFFF)
           myDecoder.value=Previous;
         switch(myDecoder.value) {

This time, though, we are going to set the position of the servo before writing to it. Hobby servos are incredibly particular with how far they can rotate, and can break if you rotate them too far. Let's prevent this with the min() function. This function will set the position of the servo to pos+Speed, but it'll keep the value underneath or at 180 degrees so that it won't go past that point.

Download: file
case LEFT_ARROW:    
	// move servo 
	pos=min(180,pos+Speed); 
	break;

Similarly if we want to constrain the right side, we set pos to max(0, pos-Speed). Max is the opposite of min, it constrains the lower-ends of the value.

Download: file
case RIGHT_ARROW:   
	pos=max(0,pos-Speed);
	break;

One of the cool things Arduino lets you do is generate a random number. We can generate a random number between 0 and 180 if we call random(0, 180). Let's set one of the buttons to set the pos to call to random. 

Download: file
case BUTTON_0:      
	pos=random(0,180); 
	break;

After the case statements, you'll want to:

1. Write to the servo,

2. Handle the NEC repeat code: Previous=myDecoder.value;

3. Re-enable the IR Receiver

Download: file
myServo.write(pos); 
Previous=myDecoder.value;
myReceiver.enableIRIn();

The full code is below 

/*
   Metro Explorers Guide
   PROJ06 - IR Laser Pet Toy
   by Brent Rubell and Asher Lieber for Adafruit Industries.   Support Open Source, buy Adafruit!

   Note: this sketch requires IRLIB2.x
*/

#include <IRLibAll.h>
#include <Servo.h>

/* Adafruit Mini Remote */
#define MY_PROTOCOL NEC
#define RIGHT_ARROW   0xfd50af
#define LEFT_ARROW    0xfd10ef
#define SELECT_BUTTON 0xfd906f
#define BUTTON_0 0xfd30cf
#define BUTTON_1 0xfd08f7
#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
// pin number for the receiver
IRrecv myReceiver(2);
IRdecode myDecoder;
// handles nec repeat codes
uint32_t Previous;

/* Servo */
// create a servo object
Servo myServo;
// stores the servo position
int16_t pos;
// angle (degrees) to move the servo left/right
int16_t Speed;

void setup() {
  // randomizes a seed for random() calls
  randomSeed(analogRead(0));
  // set the laser pin as an output
  //pinMode(laserPin, OUTPUT);
  // attach servo to pin 9
  myServo.attach(9);
  // set initial position
  pos = 90;
  // set initial speed
  Speed = 5;
  // write initial pos to servo at startup
  myServo.write(pos);
  // Start the IR receiver
  myReceiver.enableIRIn();
}

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:
          // move servo
          pos = min(180, pos + Speed);
          break;
        case RIGHT_ARROW:   
          pos=max(0,pos-Speed);
          break;
        case BUTTON_0:      
          pos=random(0,180); 
          break;
        }
       // tell servo 'move to variable pos' 
       myServo.write(pos); 
       Previous=myDecoder.value;
      }
    myReceiver.enableIRIn();
  }
}

This project isn't working properly

My Servo doesn't properly move

Make sure your servo is attached to Digital Pin 9 on your Metro or Metro Express. Also make sure you have the servo library included in your sketch (at the top of your sketch, you should see #include ). 

I'm using a different remote, should I be doing something differently?

The code for this project only works with the Adafruit Mini Remote. If you want to use another remote, please check this guide for more information.

This guide was first published on Aug 18, 2017. It was last updated on Aug 18, 2017. This page (Code) was last updated on Feb 23, 2020.