Servo as input device problem

Has anyone out there tried this servo as an input device hack? I've done the servo modifying already but I can't seem to get the code to work at all. Not sure if it's me (very very likely) or the code itself on this guys blog. Thing-a-day, Day 9: Servo as input device | C i b o M a h t o . c o m

One thing that is perplexing. The status light (which should only be lit during button presses) is lit as soon as the code is uploaded to the board. It makes me think there is something askew code wise but being an arduino newb I can't seem to pin point it.

It looks like a promising hack. My ultimate goal is to hook up a few more servos and create a robotic arm that would play back a series of movements I've physically put it in.

Any ideas out there? Thanks! :-?

Servos (even unpowered) aren't really meant to be driven "in-reverse" by their output shafts; the gearing system in that case is in a "step-up" transmission arrangement, and such use could place excessive strain on the parts, making them prone to damage.

While your idea is an interesting one (and something that has been done with industrial robotics for decades), doing it using servos seems like a recipe for damaged servos. But its your money and time.

That isn't to say that the hack isn't useful; it could definitely be used to monitor the position of a servo for feedback purposes, which is something very useful for a robotic arm. It won't be very accurate, and you might even disrupt the servo electronics with the hack, depending on the quality of the electronics inside the servo. In such a case, it would be better to have external potentiometers or encoders on the joints, and monitor those instead.

On real industrial robots, the way things are done to for teach/playback is the arm is put in a mode where it is still powered, but can sense the forces on its joints, and then assist the move/teaching process itself. You typically see this kind of system on larger hydraulic/pneumatic arms, as larger electric arms use leadscrew-based servomechanisms that can't be driven "in-reverse". Some smaller electric arms that don't use such mechanisms can be taught in this manner as well, but typically they face the same problems of torque and loading that a small hobby servo would being driven in reverse, so you don't typically see it. With hydraulics and pneumatics, you don't face this problem, and the mechanisms can assist with the teaching, making a super-heavy and large arm easy for a single person to move around.

As far as your code not working, post the code you are using (if you have changed anything from the original), as well as a schematic or hook-up diagram for your servos; then we can tell you what, if anything, is wrong (you might also want to post what servos you used, plus any pictures of the hack process you may have taken - are you sure you connected to the right spot/wire inside the servo?)...

:slight_smile:

cr0sh,
I understand what you're saying about the added stress to the gears from driving the servo from the horn. I hadn't considered that. Once I get this together though I'm planning on "programming" it just once this way and running the same motion forever after that. It's a risk I'm willing to take I guess. The servo I'm using does have a Karbonite geartrain so that might buy me a little extra luck in the strength dept.

I haven't change the code from what I found on his blog and I'm using a Hitec HS-322HD. I do get a variable reading from .40 to 2 volts on the wire I hooked up in the servo through a multimeter test. I've put together a little schematic to show how I've hooked up the rig on my bread board.

And the code used.

/******************************************************************************************************
 *  Using a servo for input and output
 *
 * by Matt Mets
 * Created 9 Feb. 2008
 *  
 * This demonstrates using the servo motor as an input device to record its own positions, which can
 * then be played back to animate whatever you are controlling.
 * 
 * Servo control code thanks to:
 *    Servo control from an analog input
 *    http://itp.nyu.edu/physcomp/Labs/Servo
 *    by Tom Igoe
 *    additions by Carlyn Maw
 *    Created 28 Jan. 2006
 *    Updated 7 Jun. 2006
 * 
 *  The minimum (minPulse) and maxiumum (maxPuluse) values
 *  will be different depending on your specific servo motor.
 *  Ideally, it should be between 1 and 2 milliseconds, but in practice,
 *  0.5 - 2.5 milliseconds works well for me.
 *  Try different values to see what numbers are best for you.
 * 
 *  This program uses the millis() function to keep track of when the servo was 
 *  last pulsed.  millis() produces an overflow error (i.e. generates a number
 *  that's too big to fit in a long variable) after about 5 days. if you're
 *  making a program that has to run for more than 5 days, you may need to 
 *  account for this.
 ******************************************************************************************************/
 
 
/***** Variable Definitions ***************************************************************************/
int servoPin =        4;      // Servo motor control line
int analogPin =       1;      // Line from servo potentimeter
int recordButtonPin = 7;      // Record button
int playButtonPin =   10;     // Playback button
int statusPin =       13;     // Status LED
 
// Adjust these to fit your servo
int minPulse = 500;    // Minimum servo position (us)
int maxPulse = 2200;  // Maximum servo position (us)
int refreshTime = 20; // the time needed in between pulses (ms)
 
// Measure the minimum and maximum output voltages of the potentiometer and record them here.
// Note: These should be measured over the full range of the servos motion.
#define CALIBRATION_LOW_VOLTAGE   0.40  // Minimum potentiometer voltage (V)
#define CALIBRATION_HIGH_VOLTAGE  2.00  // Maximum potentiometer voltage (V)
 
// These are derived from the measured voltages above, are are computed during the setup routine
int inputOffset = 0;    // Input offset (ADC counts)
float inputScale = 0;   // Input scale (%)
 
#define MAX_POSITIONS 50           // Maximum number of positions that can be stored
int positionTable[MAX_POSITIONS];  // Table to hold each position
int positionCount = 0;             // Number of positions recorded
 
 
 
/***** Functions ************************************************************************************/
void setup()
{
  pinMode(analogPin, INPUT);
  pinMode(servoPin, INPUT);
  pinMode(recordButtonPin, INPUT);
  pinMode(playButtonPin, INPUT);
  pinMode(statusPin, OUTPUT);
 
 
  // Calculate the input offset and scale factors, based on hand-measured data
  // 204 = counts per volt
  inputOffset = CALIBRATION_LOW_VOLTAGE * 204;
  inputScale = 1024/((CALIBRATION_HIGH_VOLTAGE - CALIBRATION_LOW_VOLTAGE) * 204);
 
 
  // These are because I don't have a proper voltage rail on my proof of concept board, so I am using
  // pin 3 as a 5V source.  You probably don't need these.
  pinMode(3, OUTPUT);         // This pin powers some other pins so i dont have to wire...
  digitalWrite(3, HIGH);
 
  Serial.begin(9600);
}
 
// Sequence each recorded position
void doPlayBack()
{
  int position;
  long startTime;
  long lastPulse = 0;    // the time in milliseconds of the last pulse
 
  // play back each step for a second
  for(position = 0; position < positionCount; position++)
  {
    startTime = millis();
    while(millis() - startTime < 1000)
    {
      // pulse the servo again if rhe refresh time (20 ms) have passed:
      if (millis() - lastPulse >= refreshTime)
      {
        digitalWrite(servoPin, HIGH);   // Turn the motor on
        delayMicroseconds(positionTable[position]);       // Length of the pulse sets the motor position
        digitalWrite(servoPin, LOW);    // Turn the motor off
        lastPulse = millis();           // save the time of the last pulse
      }
    }
  }
}
 
void loop()
{
  int i;
  long analogValue;    // the value returned from the analog sensor
 
  // When the record button is pressed, try to record the current sensor position
  if(digitalRead(recordButtonPin) == LOW)
  { 
    if(positionCount < MAX_POSITIONS)
    {
      // There is room to store the positions, so do so.
      analogValue = analogRead(analogPin);                    // Read the analog input
      Serial.println(analogValue);
      analogValue = (analogValue - inputOffset) * inputScale; // Scale the compressed input to a full range
      analogValue = analogValue*(maxPulse - minPulse)/1024 + minPulse;  // Precompute the pulse length
 
      positionTable[positionCount++] = analogValue;           // Store in the table
      Serial.println(analogValue);
 
      // Debounce the input button
      digitalWrite(statusPin, HIGH);    
      while(digitalRead(recordButtonPin) == LOW)
        delay(200);
      digitalWrite(statusPin, LOW);
    } else {
      // There is no more room to record positions, so flash the LED in protest.
      for(i = 0; i < 5; i++)
      {
        digitalWrite(statusPin, HIGH);
        delay(100);
        digitalWrite(statusPin, LOW);
        delay(100);        
      }
    }
  }
 
  // User wants to play back the sequence  
  if(digitalRead(playButtonPin) == LOW) {
    digitalWrite(statusPin, HIGH);
    doPlayBack();
    digitalWrite(statusPin, LOW);
  }
}

Instead of usng an actual servo to get a position, using a 5k pot in place of the servo might be a better idea.

You can use the servo to get it's position (feedback), but the servo is just turning a pot inside, tap into the servos pot and read. (hacking into servo required). But as the servo only turns a pot, you can also just turn an external pot with the servo.
If you have another device, that you wanted to mesure the position of, then servo is not a good choice (gears make friction, hard to turn), Use ordinary pot then.

David

Actually the servo has already been hacked, I've tied into the internal potentiometer. Hardware wise I'm pretty sure I'm set and everything is working, code side is where my problem is.

the pot connected to ground and analog-in pin, then just read the pin
you just use any sketch that uses pot as input from the Arduino examples.
Make sure that the servo is not giving more than 5V, only an issue if the driving of the servo is done with more than 5V.

David

In my experience with getting position feedback from a servo pot (below) has been that the servo I was testing input 2v to the internal pot instead of 5v. As such, the measuring had to be from 0v-2.0v for full range. The same data might be easier obtained using a pot than a servo.

http://www.lynxmotion.net/viewtopic.php?f=31&t=3182

the pot connected to ground

Just make sure you get the end of the pot that's tied to the servo's ground :wink:

if the voltage never exceeds 2-3V then:
The arduino has 3.3v out pin, jumper this to the ref pin and use external ref voltage (3.3V) for the analog in

if it goes max to 1.1V then use internal ref.

THis will be applied to all the analog-in pins, so if you have someting that gives 5V to the analog-input pins, you cannot do it.

David

THis will be applied to all the analog-in pins, so if you have someting that gives 5V to the analog-input pins, you cannot do it.

Yes you can do it, it's just that anything that you expect to read 5V full-scale will max-out at 3.3V, i.e a voltage of Aref or more will always read 1023.
But that's no problem, just switch the analogue reference back to 5v before reading such pins and allow a short delay.