About Servos and Feedback

What is a Servo?

The word 'servo' means more than just those little RC Servo Motors we usually think of. Servo is a general term for a closed loop control system using negative feedback.

The cruise control in a car is one example of a servo system. It measures your speed and feeds that back into a control circuit which adjusts the accelerator to maintain speed.

For the familiar RC Servo motor, the position of the output shaft is measured and fed back to the internal control circuit which adjusts current to the motor to maintain position.

Open and Closed Loops

An "Open Loop" system has no feedback, so there is no way to verify that it is performing as expected. A common expression among control engineers is "You can't control what you can't measure.".
A "Closed Loop" system can use the feedback signal to adjust the speed and direction of the motor to achieve the desired result. In the case of an RC servo motor, the feedback is in the form of a potentiometer (pot) connected to the output shaft of the motor. The output of the pot is proportional to the position of the servo shaft.
The problem with controlling a standard RC servo motor from a microcontroller is that it is 'closed loop' inside the servo motor case, but 'open loop' with respect to your microcontroller. You can tell the servo control circuit how you want the shaft positioned, but you have no way to confirm if or when this actually happens.
The Feedback Servos allow you to close this outer loop by providing the feedback signal to the microcontroller too!

Using Feedback

If a servo motor does what it is told to do, why do we need feedback?

RC servos usually do what they are told to do, but there are many cases where a servo motor might not. These can include:
  • Insufficient motor size
  • Insufficient power supply
  • Physical interference
  • Electrical interference
  • loose connection
In these cases, feedback could alert you to the problem.

But even if the servo is adequately sized and functioning normally, it still takes some time to respond to a position command, and in many applications it is just as important to know when the position is reached.

This following code snippet is from the "Sweep" example in the Servo library. Note the arbitrary 15 millisecond delay after
"myservo.write(val)".
Download: file
void loop() 
{ 
  val = analogRead(potpin);            // reads the value of the potentiometer (value between 0 and 1023) 
  val = map(val, 0, 1023, 0, 179);     // scale it to use it with the servo (value between 0 and 180) 
  myservo.write(val);                  // sets the servo position according to the scaled value 
  delay(15);                           // waits for the servo to get there 
} 
Without feedback, most servo programming has to make some assumptions about how long a particular move will take. Adding fixed-time delays to servo code works OK for simple applications, but can result in slow and/or jerky performance when trying to coordinate multiple servo motions or interactions between servos and other sensors or actuators.

Or worse: If the delays are not long enough, your servos may not reach the desired position in time. This can cause malfunctions and/or damage to your project. Timing problems are a big problem in battery-powered projects because the motors will run slower as the battery power fades.

Reading the feedback

The feedback signal is tapped off the position pot attached to the servo shaft. You can connect the white feedback wire to any of the analog input pins and read the feedback value using analogRead().
Download: file
  int feedback = analogRead(feedbackPin);

Calibrating the feedback

The raw feedback signal is a voltage. In order to convert that voltage into a meaningful position, we need to calibrate it to the servo. By reading the feedback values at two known positions, we can interpolate the expected feedback values for every position in between.

The following bit of code does just that. If you call "calibrate" in your setup function, it will perform the calibration on the two points you specify. These servos operate over a range of about 0 to 180 degrees. For maximum accuracy, you should choose the minPos and maxPos calibration points based on the range of motion required in your project.
Download: file
#include <Servo.h> 
 
Servo myservo;  

// Control and feedback pins
int servoPin = 9;
int feedbackPin = A0;

// Calibration values
int minDegrees;
int maxDegrees;
int minFeedback;
int maxFeedback;
int tolerance = 2; // max feedback measurement error

/*
  This function establishes the feedback values for 2 positions of the servo.
  With this information, we can interpolate feedback values for intermediate positions
*/
void calibrate(Servo servo, int analogPin, int minPos, int maxPos)
{
  // Move to the minimum position and record the feedback value
  servo.write(minPos);
  minDegrees = minPos;
  delay(2000); // make sure it has time to get there and settle
  minFeedback = analogRead(analogPin);
  
  // Move to the maximum position and record the feedback value
  servo.write(maxPos);
  maxDegrees = maxPos;
  delay(2000); // make sure it has time to get there and settle
  maxFeedback = analogRead(analogPin);
}

 
void setup() 
{ 
  myservo.attach(servoPin); 
  
  calibrate(myservo, feedbackPin, 20, 160);  // calibrate for the 20-160 degree range
} 

void loop()
{
}

Using feedback in your code

Now that we have a calibrated feedback signal, we can easily convert between servo position and feedback voltages in our code.

Seeking to a position

The following bit of code will seek to a position and return as soon as we reach it. There is no need to add an arbitrary delay to the code because the feedback signal will tell us exactly when we get there!
Download: file
void Seek(Servo servo, int analogPin, int pos)
{
  // Start the move...
  servo.write(pos);
  
  // Calculate the target feedback value for the final position
  int target = map(pos, minDegrees, maxDegrees, minFeedback, maxFeedback); 
  
  // Wait until it reaches the target
  while(abs(analogRead(analogPin) - target) > tolerance){} // wait...
}

Finding out where you are

Another great thing about feedback is: You don't need to write code to remember the last position command you sent to the servo (assuming it got there). If you want to find out what position your servo is in, you can simply ask it!

Once you have calibrated your servo with the calibration function above, this bit of code will tell you the current position (in degrees) of your servo:
Download: file
int getPos(int analogPin)
{
  return map(analogRead(analogPin), minFeedback, maxFeedback, minDegrees, maxDegrees);
}
The ability to simply read the servo position opens up the possibility of using it as an input device as well. The next page will show you how.

Servos as Input Devices

Another neat feature of feedback servos is that they can be used as an input device too! The Servo Record/Play Demo lets you record a series of servo movements, then it will replay them back for you! The recorded positions are saved in EEPROM, so they will be remembered even after resetting or powering down the Arduino

To run this demo, first wire up your Servo as in the Fritzing diagram below:

Components used:
Next, download the example sketch from Github using this button:
// Example code for recording and playing back servo motion with a 
// analog feedback servo
// http://www.adafruit.com/products/1404


#include <Servo.h>
#include <EEPROM.h>

#define CALIB_MAX 512
#define CALIB_MIN 100
#define SAMPLE_DELAY 25 // in ms, 50ms seems good

uint8_t recordButtonPin = 12;
uint8_t playButtonPin = 7;
uint8_t servoPin = 9;
uint8_t feedbackPin = A0;
uint8_t ledPin = 13;

Servo myServo;  
  
void setup() {
  Serial.begin(9600);
  pinMode(recordButtonPin, INPUT);
  digitalWrite(recordButtonPin, HIGH);
  pinMode(playButtonPin, INPUT);
  digitalWrite(playButtonPin, HIGH);
  pinMode(ledPin, OUTPUT);
  
  Serial.println("Servo RecordPlay");
}

void loop() {
 if (! digitalRead(recordButtonPin)) {
   delay(10);
   // wait for released
   while (! digitalRead(recordButtonPin));
   delay(20);
   // OK released!
   recordServo(servoPin, feedbackPin, recordButtonPin);
 }
 
  if (! digitalRead(playButtonPin)) {
   delay(10);
   // wait for released
   while (! digitalRead(playButtonPin));
   delay(20);
   // OK released!
   playServo(servoPin, playButtonPin);
 }
}

void playServo(uint8_t servoPin, uint8_t buttonPin) {
  uint16_t addr = 0;
  Serial.println("Playing");


  myServo.attach(servoPin);
  while (digitalRead(buttonPin)) {    
    uint8_t x = EEPROM.read(addr);
    Serial.print("Read EE: "); Serial.print(x);
    if (x == 255) break;
    // map to 0-180 degrees
    x = map(x, 0, 254, 0, 180);
    Serial.print(" -> "); Serial.println(x);
    myServo.write(x);
    delay(SAMPLE_DELAY);
    addr++;
    if (addr == 512) break;
  }
  Serial.println("Done");
  myServo.detach();
  delay(250);  
}

void recordServo(uint8_t servoPin, uint8_t analogPin, uint8_t buttonPin) {
  uint16_t addr = 0;
  
  Serial.println("Recording");
  digitalWrite(ledPin, HIGH);
  


  
  pinMode(analogPin, INPUT); 
  while (digitalRead(buttonPin)) {
     uint16_t a = analogRead(analogPin);
     
     Serial.print("Read analog: "); Serial.print(a);
     if (a < CALIB_MIN) a = CALIB_MIN;
     if (a > CALIB_MAX) a = CALIB_MAX;
     a = map(a, CALIB_MIN, CALIB_MAX, 0, 254);
     Serial.print(" -> "); Serial.println(a);
     EEPROM.write(addr, a);
     addr++;
     if (addr == 512) break;
     delay(SAMPLE_DELAY);
  }
  if (addr != 512) EEPROM.write(addr, 255);

  digitalWrite(ledPin, LOW);

  Serial.println("Done");
  delay(250);
}

To run the Servo Record/Play Demo Sketch:

  1. Upload servo_recordplay to the arduino
  2. press the top button to start recording. (The LED should light up.)
  3. Press the top button once more to stop recording.
  4. Press the bottom button to replay.
  5. You can press the green button as many times as you want.
  6. To record a new sequence, go back to step 2.
Watch the video below to see it in operation:
You can record up to 512 samples (about 12.8 seconds worth). When you reach the limit, the led will go out and recording will stop automatically.
This guide was first published on Aug 24, 2013. It was last updated on Aug 24, 2013.