Thumbstick (Joystick) and 2 Servos

I think code has been posted that uses millis to slow the movement speed of servos. You might incorporate this into your code, but it probably add another level of complexity. You might also be able to modify the varspeedservo library files to accommodate us values instead of degree values.

I posted some code which uses a joystick to set the speed a servo is moving rather than setting the servo's position. You might want to give it a try to see which method of control you prefer.

c0rsa1r: Is there a way in this code to slow down the movements?

Yes, this will be relatively easy to add. I'll probably work on this later today.

The code I linked to above has a speed setting of sorts. The smaller the constant "POT_TO_SPEED_CONSTANT", the faster the servo will move.

Slowing the speed will likely improve how well your servos move but what you really need is an algorithm which accelerates the servo motion up to some max allowed speed and then decelerates as it reaches the target position.

I've done this sort of constant acceleration servo control in other projects but I haven't done this sort of servo control using an Arduino yet.

I'll likely try writing some constant acceleration code in the near future. I'll post a note here when I do.

I’m not sure how fast you want the servos to move but you can easily change the speed by changing the value of “MAX_SPEED”. To increase the speed of the servos, increase the value of “MAX_SPEED”.

Here’s the speed limited joystick control code.

/* JoystickWithAveraging151219a
 *
 *  by Duane Degn
 *  December 19, 2015
 *
 *  A ring buffers are used to average the
 *  ADC readings from two potentiometers.
 *  This average is used to control two
 *  hobby servos.
 *  The speed of the servos is limited
 *  by the constant "MAX_SPEED".
 *
 */

#include <Servo.h>

// User changeable.
#define SERVO_X_PIN 2                           // User changeable.
#define SERVO_Y_PIN 3                           // User changeable.

// "JOYSTICK_X_PIN" and "JOYSTICK_Y_PIN" need to be assigned to analog pins.
#define JOYSTICK_X_PIN A0                       // User changeable.
#define JOYSTICK_Y_PIN A1                       // User changeable.

const int MIN_PULSE = 900;                      // User changeable.
const int MAX_PULSE = 2100;                     // User changeable.
const int MIN_POT = 0;                      // User changeable.
const int MAX_POT = 1023;                      // User changeable.

const int POWER_OF_TWO_TO_AVERAGE = 4;          // User changeable.
// Changing "POWER_OF_TWO_TO_AVERAGE" changes several other constants.
// The constants "BUFFER_SIZE" and "BUFFER_LIMIT" are calculated based on "POWER_OF_TWO_TO_AVERAGE".

const byte SERVOS_IN_USE = 2;
const int MAX_SPEED = 16;
const byte SERVO_PIN[] = {SERVO_X_PIN, SERVO_Y_PIN};
const byte JOYSTICK_PIN[] = {JOYSTICK_X_PIN, JOYSTICK_Y_PIN};

const long SERVO_PULSE_RANGE = MAX_PULSE - MIN_PULSE; // This needs to be a long for the equations to work correctly.
const int START_PULSE_X = MIN_PULSE + (SERVO_PULSE_RANGE) / 2;  // User changeable.
const int START_PULSE_Y = MIN_PULSE + (SERVO_PULSE_RANGE) / 2;  // User changeable.
const int START_PULSE[] = {START_PULSE_X, START_PULSE_Y};
const int POT_RANGE_X = MAX_POT - MIN_POT;
const int POT_RANGE_Y = MAX_POT - MIN_POT;
const int POT_RANGE[] = {POT_RANGE_X, POT_RANGE_Y};

const int BUFFER_SIZE = 1 << POWER_OF_TWO_TO_AVERAGE; // Do not change.
const int BUFFER_LIMIT = BUFFER_SIZE - 1;             // Do not change.

// Time constants and variables should be unsigned longs.
const unsigned long ANALOG_READ_PERIOD = 5000;  // read pots at 200Hz "ANALOG_READ_PERIOD" must be <= "DEBUG_PERIOD"
const unsigned long DEBUG_PERIOD = 100000;  // update serial at 4Hz "DEBUG_PERIOD" must be <= "SERVO_PERIOD"
const unsigned long SERVO_PERIOD = 20000;  // update servo at 50Hz

int averagingBuffer[SERVOS_IN_USE][BUFFER_SIZE];
int averagingBufferY[BUFFER_SIZE];
int bufferIndex = 0;

long bufferTotal[SERVOS_IN_USE];

int servoPosition[] = {START_PULSE_X, START_PULSE_Y};
unsigned long lastDebug;
unsigned long lastServo;
unsigned long lastAnalogRead;

Servo myServo[2];


void setup()
{
  Serial.begin(115200);

  for (int i = 0; i < SERVOS_IN_USE; i++)
  {
    myServo[i].writeMicroseconds(servoPosition[i]); // start servo in center position
    myServo[i].attach(SERVO_PIN[i], MIN_PULSE, MAX_PULSE);
    bufferTotal[i] = 0;
    for (int j; j < BUFFER_SIZE; j++) // Fill buffer with start position.
    {
      averagingBuffer[i][j] = (MAX_POT - MIN_POT) / 2;
      bufferTotal[i] += averagingBuffer[i][j];
    }
  }

  lastDebug = micros();
  lastServo = lastDebug;
  lastAnalogRead = lastDebug;
}

void loop()
{
  checkAnalogReadTime();
}

void checkAnalogReadTime()
{
  if (micros() - lastAnalogRead > ANALOG_READ_PERIOD)
  {
    lastAnalogRead += ANALOG_READ_PERIOD;

    long joystickInput;

    bufferIndex++;
    bufferIndex &= BUFFER_LIMIT;

    for (int i = 0; i < SERVOS_IN_USE; i++)
    {
      joystickInput = analogRead(JOYSTICK_PIN[i]);

      bufferTotal[i] -= averagingBuffer[i][bufferIndex]; // out with the old
      averagingBuffer[i][bufferIndex] = joystickInput;
      bufferTotal[i] += averagingBuffer[i][bufferIndex]; // in with the new
    }

    checkServoTime();
  }
}


void checkServoTime()
// Called from "checkAnalogReadTime" function.
{
  if (micros() - lastServo > SERVO_PERIOD)
  {
    lastServo += SERVO_PERIOD;
    controlServo();
  }
}

void controlServo()
// Called from "checkServoTime" function.
{
  int average;
  int servoTarget;
  boolean debugFlag = checkDebugTime();
  for (int i = 0; i < SERVOS_IN_USE; i++)
  {
    average = bufferTotal[i] >> POWER_OF_TWO_TO_AVERAGE;
    servoTarget = MIN_PULSE + ((average - MIN_POT) * SERVO_PULSE_RANGE / POT_RANGE[i]);
    if (servoTarget > servoPosition[i] + MAX_SPEED)
    {
      servoPosition[i] += MAX_SPEED;
    }
    else if (servoTarget < servoPosition[i] - MAX_SPEED)
    {
      servoPosition[i] -= MAX_SPEED;
    }
    else
    {
      servoPosition[i] = servoTarget;
    }
    myServo[i].writeMicroseconds(servoPosition[i]);
    if (debugFlag)
    {
      debugServo(i, average, servoPosition[i]);
    }
  }
}

boolean checkDebugTime()
// Called from "controlServo" function.
// This method checks to see if it's time to
// display data.
{
  boolean debugFlag = 0;
  if (micros() - lastDebug > DEBUG_PERIOD)
  {
    lastDebug += DEBUG_PERIOD;
    debugFlag = 1;
  }
  return debugFlag;
}

void debugServo(int servoIndex, long average, long servoOutput)
// Called from "controlServo" function.
// Serial output slows down code execution.
// It would probably be a good idea to remove this section of code
// once the program is working as hoped and when serial
// output is now longer desired.
{
    Serial.print(F("servo # "));
    Serial.print(servoIndex, DEC);
    Serial.print(F(": average = "));
    Serial.print(average, DEC);
    Serial.print(F(", position = "));
    Serial.println(servoOutput, DEC);
}

I'm speechless DuaneDegn, this is really awesome. I'm trying some settings such as speed = 1 and it does indeed slow down as intented.

What if I set the adc to 12 bit (since I'm a DUE) instead of the standard 10 bit? Is it an advantage?

I'll run a lot of tests today and let you know if something doesn't work as supposed to.

thank you very very much

I'm glad it's working as expected.

I didn't want the servos I'm using in a project to abruptly change speeds so I added an acceleration component to the control algorithm.

The code didn't fit. I'll add it to the next reply.

There are now lots of parameters to set. I started adding instructions on how to set these various parameters but I've run out of time. I'll have to finish the instructions some other time.

The above code should control servos nice and smooth.

The code above will control servos' position or the servos' speed based on the position of the joystick(s). The program can be modified to use as many analog inputs as the Arduino being used supports.

I added a note in the comments about using the code with a DUE.

Let me know if you have any questions or if the program does something unexpected.

As I said, I haven't finished documenting all the parameters but hopefully the variable names will offer a clue on the parameter's purpose.

Here’s the first part of the program.

/* Joystick2ServoAcceleration160117a
 *
 *  by Duane Degn
 *  December 22, 2015
 *  modified January 17, 2016
 *
 *
 *  Program to control servos with either
 *  potentiometer or joystick input.
 *  
 *  A ring buffers are used to average the
 *  ADC readings from two potentiometers.
 *  This average is used to control
 *  speed of two hobby servos.
 *  The servos will now use a constant
 *  acceleration algorithm.
 *  
 *  To increase the speed of the program,
 *  comment out calls to "debugServo".
 * 
 *  Watch for /****** Instructions ******/ /*
 *  notices about how to configure the program.
 *  
 *  Version 22a has a bunch of extra debug statements.
 *  Version 160117a added indicator for which
 *  constants arrays need to be adjusted when
 *  the value of "SERVOS_IN_USE" is changed.
 *  The comment below will be used to mark 
 *  these arrays.
 *  **** Number of elements should match "SERVOS_IN_USE" ****
 */

  // Note about DUE analog inputs.
  // If the DUE's 12-bit inputs are used, the following
  // arrays should be modified.
  // "LOW_CENTER_THRESHOLD", "HIGH_CENTER_THRESHOLD"
  // "MIN_POT" (if non-zero), "MAX_POT" and the
  // value of "POT_TO_SPEED_CONSTANT" should be
  // adjusted (usually multiplied by a factor of four).
  
#include <Servo.h>

// ****** Start of Constants ******

  /****** Instructions ******/
  // The various parameter arrays will need to be
  // modified is additional servos are used.               
const byte SERVOS_IN_USE = 2;   // User changeable.

  /****** Instructions ******/
  // Add pins for servos and joysticks (or pots) as needed.
const byte SERVO_PIN[] = {2, 3}; //**** Number of elements should match "SERVOS_IN_USE" ****
const byte JOYSTICK_PIN[] = {A0, A1}; //**** Number of elements should match "SERVOS_IN_USE" ****

  /****** Instructions ******/
  // Add additional zeros (or ones) as needed.
  // set to one if servo should be reversed
const boolean SERVO_REVERSE_FLAG[] = {0, 0}; //**** Number of elements should match "SERVOS_IN_USE" ****

  /****** Instructions ******/
  // To use a joystick to set a servo's position, set the corresponding element to zero. 
  // An element set to one will indicate the servo's speed will be controlled by the joystick.
  // set to one if joystick provides speed input rather than position input
const boolean SERVO_SPEED_CONTROL_FLAG[] = {0, 0}; //**** Number of elements should match "SERVOS_IN_USE" **** 

  /****** Instructions ******/
  // "LOW_CENTER_THRESHOLD" and "HIGH_CENTER_THRESHOLD" are only used with the speed control
  // algorithm. Pot values between these thresholds will not change a speed controlled
  // servo's position.
  // These values need to be determined experimentally.
const long LOW_CENTER_THRESHOLD[] = {477, 477}; //**** Number of elements should match "SERVOS_IN_USE" ****     
const long HIGH_CENTER_THRESHOLD[] = {515, 515}; //**** Number of elements should match "SERVOS_IN_USE" ****   

  /****** Instructions ******/
  // "POT_TO_SPEED_CONSTANT" are only used with the speed control algorithm.
  // The minimum value of "POT_TO_SPEED_CONSTANT" is 1.
  // Larger values for slower speeds.
const long POT_TO_SPEED_CONSTANT[] = {8, 8}; //**** Number of elements should match "SERVOS_IN_USE" ****     

  /****** Instructions ******/
  // The values below should be determined experimentally.
  // Servo endpoints, the default endpoints used by the Arduino
  // servo library are 544 and 2400. The 544 and 2400
  // setting can damage some servos. The "900" and "2100" are much
  // more conservative endpoint setting.
  // Again these values need to be determined experimentally. 
const long MIN_PULSE[] = {900, 900}; //**** Number of elements should match "SERVOS_IN_USE" ****
const long MAX_PULSE[] = {2100, 2100}; //**** Number of elements should match "SERVOS_IN_USE" ****   

  /****** Instructions ******/
  // The values below should be determined experimentally.
  // Some joysticks do not provide a full range of analog values.
  // 0 and 1023 are common values for "thumb sticks". 
const long MIN_POT[] = {0, 0}; //**** Number of elements should match "SERVOS_IN_USE" ****                          
const long MAX_POT[] = {1023, 1023}; //**** Number of elements should match "SERVOS_IN_USE" ****      

  /****** Instructions ******/
  // The "MAX_SPEED" of each servo may be individually set.
  // This is the amount the pulse length will
  // change each refresh cycle when the servo
  // is moving at its top speed.
const long MAX_SPEED[] = {32, 32}; //**** Number of elements should match "SERVOS_IN_USE" ****

  /****** Instructions ******/
  // The "ACCELERATION" of each servo may be individually set.
  // The minimum value of this parameter is 1.
  // This is the amount the speed of the servo
  // will change with each refresh cycle.
  // If this value is set to the same value 
  // as "MAX_SPEED", the servo will not accelerate
  // and decelerate but it will move with
  // constant speed.
const long ACCELERATION[] = {2, 2}; //**** Number of elements should match "SERVOS_IN_USE" ****

  /****** Instructions ******/
  // It is important some time constants are larger than
  // other time constants.
  // "ANALOG_READ_PERIOD" must be smaller than "SERVO_PERIOD".
  // "SERVO_PERIOD" must be smaller than "DEBUG_PERIOD".
  // To reduce the stutter caused from multiple
  // debug stateements at once, the debug data
  // is staggered. Data from a single servo is
  // displayted
const unsigned long ANALOG_READ_PERIOD = 5000;  // read pots at 200Hz "ANALOG_READ_PERIOD" must be <= "DEBUG_PERIOD"
const unsigned long SERVO_PERIOD = 20000;  // update servo at 50Hz
const unsigned long DEBUG_PERIOD = 5000000;  // update serial at 4Hz "DEBUG_PERIOD" must be >= "SERVO_PERIOD"

  /****** Instructions ******/
  // The constant "POWER_OF_TWO_TO_AVERAGE" sets the size of
  // the ring buffer used to average the pot values.
  // Large buffers will provide smoother motion but large
  // buffers also slow down response times.
  // The minimum value of "POWER_OF_TWO_TO_AVERAGE"
  // is one. If zero is used, the code will need to be
  // modified.
  // Changing "POWER_OF_TWO_TO_AVERAGE" changes several other constants.
  // The constants "BUFFER_SIZE" and "BUFFER_LIMIT" are calculated based on "POWER_OF_TWO_TO_AVERAGE".
const long POWER_OF_TWO_TO_AVERAGE = 4;          // User changeable.

// ****** End of User Changeable Constants ******

const int BUFFER_SIZE = 1 << POWER_OF_TWO_TO_AVERAGE; // Do not change.
const int BUFFER_LIMIT = BUFFER_SIZE - 1;             // Do not change.

// ****** End of Constants ******

// The value of "servoPulseRange" will be calculated
// in the "setup" section of the program. 
// The value of "servoPulseRange" will not change 
// outside the "setup" function.
long servoPulseRange[SERVOS_IN_USE]; 


long averagingBuffer[SERVOS_IN_USE][BUFFER_SIZE];
int bufferIndex = 0;
long servoPosition[SERVOS_IN_USE];
long servoSpeed[SERVOS_IN_USE];

long bufferTotal[SERVOS_IN_USE];
byte servoToDebug = SERVOS_IN_USE - 1;
unsigned long lastDebug;
unsigned long lastServo;
unsigned long lastAnalogRead;

Servo myServo[SERVOS_IN_USE];

Edit (17 January 2016): The latest version with extra instructions won’t fit in this reply. I’ve only included the text of the constant and variable declaration sections.

To see the full code, you need to download the attached file.

Joystick2ServoAcceleration160117a.ino (13.2 KB)

Here’s the rest.

long getSpeedBasedPosition(byte servoIndex, long averageJoystickInput)
{
  long targetSpeed;
  long targetPosition;
  long distanceToStop = 0;
  long stopSpeed;
  
  if (averageJoystickInput < LOW_CENTER_THRESHOLD[servoIndex])
  {
    targetSpeed = (averageJoystickInput - LOW_CENTER_THRESHOLD[servoIndex]) / POT_TO_SPEED_CONSTANT[servoIndex];
    // negative speed proportional to distance from center pot
  }
  else if  (averageJoystickInput > HIGH_CENTER_THRESHOLD[servoIndex])
  {
    targetSpeed = (averageJoystickInput - HIGH_CENTER_THRESHOLD[servoIndex]) / POT_TO_SPEED_CONSTANT[servoIndex];
    // positive speed
  }
  else // pot in dead zone
  {
    targetSpeed = 0;
  }
    
  targetSpeed = constrain(targetSpeed, servoSpeed[servoIndex] - ACCELERATION[servoIndex], servoSpeed[servoIndex] + ACCELERATION[servoIndex]);
  
  if (targetSpeed > 0)
  {
    distanceToStop = MAX_PULSE[servoIndex] - servoPosition[servoIndex];
  }
  else if (targetSpeed < 0)
  {
    distanceToStop = MIN_PULSE[servoIndex] - servoPosition[servoIndex];
  }
  
  stopSpeed = computeMaxSpeedWhileStillStopping(ACCELERATION[servoIndex], distanceToStop);
  
  if (abs(targetSpeed) > abs(stopSpeed))
  {
    targetSpeed = stopSpeed;
  }
  
  servoSpeed[servoIndex] = constrain(targetSpeed, -MAX_SPEED[servoIndex], MAX_SPEED[servoIndex]);
 
  targetPosition = servoPosition[servoIndex] + servoSpeed[servoIndex];

  servoPosition[servoIndex] = constrain(targetPosition, MIN_PULSE[servoIndex], MAX_PULSE[servoIndex]);

}
 
long computeStopDistance(long acceleration, long currentSpeed)
{
  long stopDistance;
  long timeToStop = abs(currentSpeed / acceleration);
 
  stopDistance = currentSpeed * timeToStop / 2.0;
  return stopDistance;
}

long computeMaxSpeedWhileStillStopping(long acceleration, long distance)
{
  long maxSpeed = sqrt(2 * acceleration * abs(distance));
  if (distance < 0)
  {
    maxSpeed *= -1;
  }
  return maxSpeed;
}

long getJoystickBasedPosition(byte servoIndex, long averageJoystickInput)
{
  long targetSpeed;
  long targetPosition = MIN_PULSE[servoIndex] + ((averageJoystickInput - MIN_POT[servoIndex]) * SERVO_PULSE_RANGE[servoIndex] / (MAX_POT[servoIndex] - MIN_POT[servoIndex]));
  long distanceToGo = targetPosition - servoPosition[servoIndex];

  targetSpeed = computeMaxSpeedWhileStillStopping(ACCELERATION[servoIndex], distanceToGo);
  targetSpeed = constrain(targetSpeed, servoSpeed[servoIndex] - ACCELERATION[servoIndex], servoSpeed[servoIndex] + ACCELERATION[servoIndex]);
  servoSpeed[servoIndex] = constrain(targetSpeed, -MAX_SPEED[servoIndex], MAX_SPEED[servoIndex]);  
  servoPosition[servoIndex] = constrain(servoPosition[servoIndex] + servoSpeed[servoIndex], MIN_PULSE[servoIndex], MAX_PULSE[servoIndex]);
}

byte checkDebugTime()
// Called from "controlServo" function.
// This method checks to see if it's time to
// display data and returns the servo
// id number of the servo to debug.
{
  byte debugFlag = SERVOS_IN_USE;
  if (micros() - lastDebug > DEBUG_PERIOD)
  {
    lastDebug += DEBUG_PERIOD;
    servoToDebug++;
    if (servoToDebug == SERVOS_IN_USE)
    {
      servoToDebug = 0;
    }
    debugFlag = servoToDebug;
  }
  return debugFlag;
}

void debugServo(int servoIndex, long average)
// Called from "controlServo" function.
// Serial output slows down code execution.
// It would probably be a good idea to remove this section of code
// once the program is working as hoped and when serial
// output is now longer desired.
{
  Serial.print(F("servo # "));
  Serial.print(servoIndex, DEC);
  Serial.print(F(": average = "));
  Serial.print(average, DEC);
  Serial.print(F(", position = "));
  Serial.print(servoPosition[servoIndex], DEC);
  Serial.print(F(", speed = "));
  Serial.println(servoSpeed[servoIndex], DEC);
}

I had to deleted a bunch of commented out code in order to get this to fit in two posts. Hopefully I didn’t delete anything important.

I’ve attach a full version as a file in an earlier reply. The code embedded in these two replies is the same as the attached code.

Ok DuaneDegn, I'll try the code in a couple of hours.

The acceleration factor is not relevant to me (because the slower the better) but I'll try it and see if there are some issues.

Thank you for your effort

EDIT: Unfortunately it doesn't compile

c0rsa1r: The acceleration factor is not relevant to me (because the slower the better) but I'll try it and see if there are some issues.

By accelerating (and decelerating) the servos, they don't change speed abruptly (unless you set the acceleration value high). By using an acceleration value, the speed of the servo ramps up to speed and also ramps back down when stopping. IMO, this sort of algorithm makes the servos behave much better than when the speed is just set to the max value right as the servo starts moving.

I hope you give this latest version a try. It's working as well as I had hoped.

c0rsa1r: EDIT: Unfortunately it doesn't compile

Sorry about that, I must of deleted something important trying to get the attached code to fit within the two replies. I've attached the full file to the earlier post.

Again thank you, I'm going to try the code this afternoon and I'll update this post

I uploaded a couple videos showing the code attached to reply #18 in action.

Here's the code as attached (position control) in action. Here's the code with the following modification (speed control).

The elements of "SERVO_SPEED_CONTROL_FLAG" were changed from zero:

const boolean SERVO_SPEED_CONTROL_FLAG[] = {0, 0};

To one:

const boolean SERVO_SPEED_CONTROL_FLAG[] = {1, 1};

Many of the constants can be changed to modify the servos' behaviour. The acceleration, speed and endpoints are a few of the parameters which are adjustable.

Hi Duane, I'm back and I'm trying to compile the updated code but it's full of errors when I compile it (/302 /240 stray errors), why? I'm I doing something wrong?

EDIT: Now it compiles (I saved it in ANSI coding, pasted in the Arduino IDE and removed the bad symbols)

I'm about to hard test it right now and I'll post some results, thanks again Duane!

Duane, the code is working flawlessy and it is up and running from 2 weeks always on!

I would ask you if it is possible to slow down the startup positioning.

I'm in speedcontrolled mode (so the servos goes to a position and stops) but if I try to reset the board they go at max speed on start position, can this be slower?

thank you again!

c0rsa1r: Duane, the code is working flawlessy and it is up and running from 2 weeks always on!

I would ask you if it is possible to slow down the startup positioning.

I'm in speedcontrolled mode (so the servos goes to a position and stops) but if I try to reset the board they go at max speed on start position, can this be slower?

thank you again!

It's good to hear the code is working well.

There's not much to be done about how the servos power on. The servos have no way to sensing their position.

There are a few things which would minimize the trouble cause by the fast motion during start up.

If you have a default start up position, the code could be changed so the code's start position coincides with the with the physical start position of the servos.

Another way to minimize the initial start up motion is to power on the servos one at a time.

It's possible to hack a servo and add a fourth wire. This fourth wire connects with the wiper of the internal pot. By monitoring the voltage of this pot, it's supposedly possible to read the position of the servo. If you could read the position of the servo, you could then use this position as the startup location within the code.

I usually have a default position when powering on a robot. I also power on only one servo at a time if I want to minimize the initial sudden movement.

DuaneDegn:
If you have a default start up position, the code could be changed so the code’s start position coincides with the with the physical start position of the servos.

I guess that having both servos at 1500 would be ok (they actually move to this position on startup, just checked, both 1500 and average is 520)…it’s just too fast

UPDATE: After quite a lot of research, I now understand that what I’m trying to do with a bare digital servo and an arduino is impossible.
I can’t add more things because the space available for eeprom (for position storing) or encoders is 0.
I came up with an idea. What if I add a pushbutton that goes from X position to perfect middle slowly? Doing that before shutting down the arduino should limit the initial movement to almost nothing. Is it correct?

Pushbutton reset to initial position (middle) sample code added

Before setup

int buttonPin = 4;
int LED = 13;

In setup

  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(LED, OUTPUT);

In “checkAnalogReadTime” I added this

int buttonValue = digitalRead(buttonPin);
    if (buttonValue == LOW) {

      long averageJoystickInput;

      for (int servoIndex = 0; servoIndex < SERVOS_IN_USE; servoIndex++)
      {
        averageJoystickInput = bufferTotal[servoIndex] >> POWER_OF_TWO_TO_AVERAGE;
        getJoystickBasedPosition(servoIndex, averageJoystickInput);
        if (getJoystickBasedPosition(servoIndex, averageJoystickInput) != 0)
        {
          digitalWrite(LED, HIGH);
          myServo[servoIndex].writeMicroseconds(servoPosition[servoIndex]);
        }
      }
    }
    else {
      digitalWrite(LED, LOW);
    }

It does work nicely, the servos will return to their position (actually it’s not the default position, it’s the actual joystick position) and while returning a LED turn on.

However I have to keep the button pressed until the servos stand still. How can I do a “single press” thing?
Using “while” instead of “if” does actually do that but there’s no speed control, maybe I’m missing something…

Tell me if I'm describing what you want correctly.

You'd like to press a button once in order to set the servos in some default state, right?

Once the button press has been received, the servos should move to some predefined location?

This default state should also be the start position right?

I'll try to find time to add this to the code.

DuaneDegn: Tell me if I'm describing what you want correctly.

You'd like to press a button once in order to set the servos in some default state, right?

Once the button press has been received, the servos should move to some predefined location?

This default state should also be the start position right?

I'll try to find time to add this to the code.

Exactly.

Doing that before the shutdown should prevent the fast initial motion, that would be a nice workaround.

Sorry about forgetting to add the reset button.

I added a button to the code but I haven't tested it yet.

You should only need to press the button once.

You'll want to change set the constant "MOVE_TO_START_BUTTON_PIN" to the pin you plan to use for the button.

The code assumes you'll use an active low button. This can be changed by changing the value of "BUTTON_ACTIVE" from "LOW" to "HIGH".

You can either manually enter the values for "startPosition[]" or you can set these equal to zero. If the value of the array is zero then the program will use the center position of the servo as the start (and reset) positions.

Sorry I don't have time to test this right now. Let me know if there are bugs and I'll try to fix them.

Edit: The code didn't work correctly. Check for code a couple replies down.