You are on page 1of 7

Arduino - Analog Reference Adjust

A 10 bit Analog to Digital Conversion (ADC), which is standard on most microcontrollers, returns a
value between 0 - 1023 on any analog signal. A 5V reference will yield ~ 5mV per step. Given our
measured voltage difference of 1.47 (1.87 - 0.40) from the servo feedback wire, the ADC range would
be ~294 steps. That's pretty good but we can do better. We can change the voltage that the arduino
perceives as 'maximum' or 1023 as a way to increase the range on low voltage analog sensors, and we
do this by building a voltage divider on the A-Ref pin.
If we use a lower voltage as a reference for the ADC, we can increase the range that the ADC
realizes. According to the latest arduino analogReference() notes
[http://arduino.cc/en/Reference/AnalogReference], any voltage that is applied to the ARef pin is
divided again by an internal 32K resistor. Their example uses a 2.5V input to the A-Ref, and the
arduino sees 2.2V instead. With a reference high voltage of 2.2V, the conversion resolves to ~2.1mV
per step, and the range of feedback from the hacked servo would be 700 steps. That's more like it. The
call to analogReference(EXTERNAL) will tell the arduino to look at the A-Ref pin for measuring
analog values. Happily enough, the call to analogReference(DEFAULT) will set the analog reference
voltage back to an internal source (~5V or ~3.3V depending on your power supply), so you can move
back and forth to read sensors that need the full 5V range to work ;)
I played around with a few different values (all standard resistance. nothing exotic going on here)
and tested with the arduino ADC to find that under a good power supply (9VDC 1.0A wall glob) I got
the widest range by tying a 3.3K to GND and a 4.7K to +5. You may have different results. go ahead
and play with the values. If you are off, the ADC will spit out 1023 for any reading above the value on
A-Ref. Heck, I bet you could use a potentiometer and dial it in live.

The trouble we're going through is worth it, because the signal that comes out of the internal
potentiometer has a bunch of noise in it. The wider range will help to clear out the bad data. Set up
your board similarly to the way I have it and run the code on the next page.

Microcontroller Test

On initial testing, feedback from the internal servo potentiometer works pretty damn good without
too much software tweaking. The smoothing filter that I built in software takes 20 readings, discards
the highest and lowest 6 values, and then averages the remaining 8. I focused on getting readings while
the servo was still in order to test resolution and smoothing functionality. There should be enough code
here for you to get yourself into trouble :). Notes below, code follows after in-line.
Code Notes: ServoWithFeedback_V1
This is the first attempt (well, not the first. I wouldn't post that mess;) and it aims to take a one to

one reading of servo feedback position, store all 180 points into an array, and then compare the array to
new readings as the servo moves to them. Testing for smoothing functionality and sensor drift.

Code Notes: ServoWithFeedback_V2


Here I'm simplifying the initial range acquisition. I take a high and low reading at either extreme of
the range, and us map() to correlate it with degree angles. This works very well. I have set up a sweep
that compares current sensor readings from the feedback wire to the expected value returned by map().
The offset is minimal (-1 to 1) on the rising loop. On the falling loop, the offset is more like -4 to -6.
This is pretty consistent and could be worked around in software. I was able to apply some slight
pressure to the servo horn toward and opposing the direction of travel, and got some nice values
evidencing my applied force.
Here you go, have fun.
/* >Servo With Feedback_V1<
Testing code for servo with hacked feedback from internal pot.
Servo control cable connected to digital 2
Position Feedback cable connected to analog 0
Build a voltage divider on ARef pin with two 10K resistors.
The resulting analog reference will be ~2.2V (see http://arduino.cc/en/Reference/AnalogReference)
This will increase the resolution of the potentiometer reading.
Use the following commands to toggel between reading the servo feedback
and reading any other analog pin that needs to see 5V as a reference
analogReference(EXTERNAL); //sets analog 1023 to voltage on the ARef pin
analogReference(DEFAULT); //sets analog 1023 to 5V or 3.3V depending on power supply
*/
#include
Servo Servo1;
//declair the servo!
int reading[20];
int Feedback[181];
int servoPin1 = 2;
int test;
//general purpose int
int offset = 0;
int noise = 50;
int mean;
int result;
boolean done;
void setup() {
Serial.begin(9600);
// initialize serial output
analogReference(EXTERNAL);
pinMode (servoPin1,OUTPUT);
Servo1.attach(servoPin1,570,2400);
// turn on servo control at digital pin 2
setRange();
// go test the range and set the values

}
void loop() {
Servo1.write(0);
delay(2000);
for (int i=0; i<=180; i+=10){
Servo1.write(i);
delay (50);
test = getFeedback();
offset = test - Feedback[i];
Serial.print(i);
Serial.print(" = ");
Serial.print(test);
Serial.print(" ");
Serial.print(offset);
Serial.print(" ");
Serial.println(Feedback[i]);
}
Serial.println(" ");
}

void setRange(){
Servo1.write(0);
// send servo to 0 degree position
delay(2000);
// give servo enough time to get there
for (int i=0; i<=180; i++){
Servo1.write(i);
// send next degree pulse to servo
delay(50);
// let things settle down
Feedback[i] = getFeedback(); // read the servo feedback
Serial.print(i);
Serial.print(" = ");
Serial.println(Feedback[i]);
}
}
int getFeedback(){
for (int j=0; j<20; j++){
reading[j] = analogRead(0); //get raw data from servo potentiometer
delay(3);
}
// sort the readings low to high in array
done = false;
// clear sorting flag
while(done != true){
// simple swap sorts numbers from lowest to highest
done = true;
for (int j=0; j<20; j++){

if (reading[j] > reading[j + 1]){


test = reading[j + 1];
reading [j+1] = reading[j] ;
reading[j] = test;
done = false;
}
}

// sorting numbers here

}
// for (int j=0; j<20; j++){
//un-comment this for-loop to see the raw ordered data
//
Serial.print(i);
//
Serial.print(" ");
//
Serial.println(reading[j]);
// }
mean = 0;
for (int k=6; k<14; k++){
//discard the 6 highest and 6 lowest readings
mean += reading[k];
}
result = mean/8;
//average useful readings
return (result);
}
// END OF SERVO_WITH_FEEDBACK_V1
/* >Servo With Feedback_V2<
Testing code for servo with hacked feedback from internal pot.
Servo control cable connected to digital 2
Position Feedback cable connected to analog 0
Build a voltage divider on ARef pin with two 10K resistors.
The resulting analog reference will be ~2.2V (see http://arduino.cc/en/Reference/AnalogReference)
This will increase the resolution of the potentiometer reading.
Use the following commands to toggel between reading the servo feedback
and reading any other analog pin that needs to see 5V as a reference
analogReference(EXTERNAL); //sets analog 1023 to voltage on the ARef pin
analogReference(DEFAULT); //sets analog 1023 to 5V or 3.3V depending on power supply
*/
#include
Servo Servo1;

//declair the servo!

int feedBack;
//used to hold servo feedback value
int mappedPulse;
//used to hold the value mapped between servo range and degree range
int lowEnd;
//servo feedback at 0 degrees
int highEnd;
//servo feedback at 180 degrees
int reading[20];
int servoPin1 = 2;
int test1;
//general purpose int
int test2;
int offset = 0;
int noise = 50;

boolean rangeTest= false;


void setup() {
Serial.begin(9600); // initialize serial output
analogReference(EXTERNAL);
pinMode (servoPin1,OUTPUT);
Servo1.attach(servoPin1,570,2400);
// turn on servo control at digital pin 2
setRange();
//go test the range and set the values
}
void loop() {
Servo1.write(0);
delay(2000);
// wait for it to get there
for (int i=0; i<181; i++){
// loop through degrees going up
Servo1.write(i);
delay(50);
feedBack = getFeedback();
// subroutine smooths data
mappedPulse = map(i,0,180,lowEnd,highEnd); // map degrees to setRange() readings
offset = mappedPulse - feedBack;
// resolution of mapped V actual feedback
printData();
}
for (int i=180; i>0; i--){
// loop through degrees going down
Servo1.write(i);
delay(50);
feedBack = getFeedback();
mappedPulse = map(i,0,180,lowEnd,highEnd);
offset = mappedPulse - feedBack;
printData();
}
}
void printData(){
Serial.print(i);
Serial.print(" = ");
Serial.print(feedBack);
Serial.print(" ");
Serial.print(offset);
Serial.print(" ");
Serial.println(mappedPulse);
}
void setRange(){
Servo1.write(0);
delay(2000);
//wait for servo to get there
lowEnd = getFeedback();
Servo1.write(180);
delay(2000);
//wait for servo to get there

highEnd = getFeedback();
rangeTest = true;
Serial.print("0= ");
Serial.print(lowEnd);
Serial.print(" ");
Serial.print("180= ");
Serial.println(highEnd);
}
int getFeedback(){
int mean;
int result;
int test;
boolean done;
for (int j=0; j<20; j++){
reading[j] = analogRead(0); //get raw data from servo potentiometer
delay(3);
}
// sort the readings low to high in array
done = false;
// clear sorting flag
while(done != true){
// simple swap sort, sorts numbers from lowest to highest
done = true;
for (int j=0; j<20; j++){
if (reading[j] > reading[j + 1]){ // sorting numbers here
test = reading[j + 1];
reading [j+1] = reading[j] ;
reading[j] = test;
done = false;
}
}
}
mean = 0;
for (int k=6; k<14; k++){
//discard the 6 highest and 6 lowest readings
mean += reading[k];
}
result = mean/8;
//average useful readings
return(result);
}
// END SERVO_WITH_FEEDBACK_V2

Reference:
http://www.instructables.com/id/Servo-Feedback-Hack-free/step6/Analog-Reference-Adjust/
http://www.instructables.com/id/Servo-Feedback-Hack-free/step7/Microcontroller-Test/

You might also like