Creating a Stable Joystick controller for servo motors

Hi,
For my project I have several servo's I want be controlled by some joysticks Ive got off amazon (See link).

I have managed to control the servos by simply mapping the analogue output from the x and y of the sticks to the pwm value sent to the servo's but what I really want is for the sticks to control the increment of the servo position.

For example, pushing the stick up will increase the position of the servo, then, upon letting the stick return to it neutral position, the servo stays where it is. Then moving the stick down, will decrease the position of the servo.

I have 8 servo's to control, and 4 joysticks.

Ideally it would be nice if the further I push the stick, the greater the rate of increase in the servo's position, though this is not necessary.

Any ideas on how to achieve this? I assume some for loop where once the analogueRead of the stick is over a certain value the pwm value sent to the servo is incremented upwards.

Joysticks - (https://www.amazon.co.uk/gp/product/B077Z8QN3S/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1)

Servo's are savox sh‑0256

For example, pushing the stick up will increase the position of the servo, then, upon letting the stick return to it neutral position, the servo stays where it is. Then moving the stick down, will decrease the position of the servo.

How is the Arduino supposed to know when YOU are moving the joystick down, vs. the return spring moving the joystick down?

When you can answer that, you can see that doing what you want is trivial.

You are currently mapping the joystick reading to a servo position.
Instead, map the joystick reading to a joystick adjustment factor.
That can be added to a variable that holds the servo position and then used to command the servo to move to that position.
You will want to test the bounds to prevent allowing the servo position from getting too large or too small.
You will want some timing delay (millis or delay) to keep the values from moving up or down too fast.

If you show your existing code it would be much easier to suggest changes.

     int analogInput = analogRead(JoystickPin);
     float joystickOffset = (analogInput - 512) / 512.0;  // Reduce range to -1..+1
     static float joystickPosition += joystickOffset;
     if (joystickPosition > 180)
          joystickPosition = 180;
    if (joystickPosition < 0)
        joystickPosition = 0;
    servo.write(joystickPosition);

This is my current code,

Basically it reads the analogue input of the sticks, maps it to the min and max positions of my servos, then sends it down the serial line to 3 other boards (Adafruit trinket m0s) which listen out for their 'module' number (1, 2 or 3 at the moment), and log the next two numbers as the servo positions for the two servos they each control.

const int module_id = 0; //Define this module as node 0. Due to 1 byte limit max number of modules is 225

int d = 10;

int v1 = 90;  
int h1 = 90;
int v2 = 90;  
int h2 = 90;
int v3 = 90;  
int h3 = 90;        //Initialise h and v as the horiz and vert servo position values

int x_pos1 = 512;
int y_pos1 = 512;  
int x_pos2 = 512;
int y_pos2 = 512;
int x_pos3 = 512;
int y_pos3 = 512; //Int x and y stick position values

int x_pin1 = 0;
int y_pin1 = 1;
int x_pin2 = 2;
int y_pin2 = 3;
int x_pin3 = 4;
int y_pin3 = 5;   //Int x and y analogue pin numbers

void setup() {
  Serial.begin(19200); //Initialise Serial Comunications
}

void loop() {
  x_pos1 = analogRead(x_pin1);
  y_pos1 = analogRead(y_pin1);
  x_pos2 = analogRead(x_pin2);
  y_pos2 = analogRead(y_pin2);
  x_pos3 = analogRead(x_pin3);
  y_pos3 = analogRead(y_pin3);

  
  h1 = map(x_pos1, 0, 1023, 30, 150);
  v1 = map(y_pos1, 1023, 0, 30, 150);
  h2 = map(x_pos2, 0, 1023, 30, 150);
  v2 = map(y_pos2, 1023, 0, 30, 150);
  h3 = map(x_pos3, 0, 1023, 30, 150);
  v3 = map(y_pos3, 1023, 0, 30, 150);
    
    send(1, v1, h1);
    delay(d);
    send(2, v2, h2);
    delay(d);
    send(3, v3, h3);
    delay(d);
  }

void send (int id, int horiz, int vert){
  Serial.print(id);Serial.print(" ");
  Serial.print(horiz);Serial.print(" ");
  Serial.print(vert);Serial.print(" ");
}

This Thread has some code to move the servos the way you want.

...R

When you have a bunch of variables with very similar names and that are all acted on very similarly, it is a good opportunity to use arrays:

const int module_id = 0; //Define this module as node 0. Due to 1 byte limit max number of modules is 225


int DelayBetweenMessages = 10;


const byte JoystickCount = 3;
const byte AxisCount = JoystickCount * 2;


//                                     X1,  Y1,  X2,  Y2,  X3,  Y3
float Positions[AxisCount] =         { 90,  90,  90,  90,  90,  90};
const int MinPositions[AxisCount] =  { 30,  30,  30,  30,  30,  30};
const int MaxPositions[AxisCount] =  {150, 150, 150, 150, 150, 150};


const byte AnalogPins[AxisCount] = {A0, A1, A2, A3, A4, A5};


void setup() {
  Serial.begin(19200); //Initialise Serial Comunications
}


void loop() {
  for (byte i = 0; i < AxisCount; i++)
  {
    int analogInput = analogRead(AnalogPins[i]);
    float joystickOffset = (analogInput - 512) / 512.0;  // Reduce range to -1..+1
    Positions[i] += joystickOffset;
    if (Positions[i] > MaxPositions[i])
      Positions[i] = MaxPositions[i];
    if (Positions[i] < MinPositions[i])
      Positions[i] = MinPositions[i];
  }


  send(1, 180 - Positions[1], Positions[0]);
  delay(DelayBetweenMessages);
  send(2, 180 - Positions[3], Positions[2]);
  delay(DelayBetweenMessages);
  send(3, 180 - Positions[5], Positions[4]);
  delay(DelayBetweenMessages);
}


void send (int id, int Y, int X) {
  Serial.print(id); Serial.print(" ");
  Serial.print(Y); Serial.print(" ");
  Serial.print(X); Serial.print(" ");
}

I was able to replicate this exact question with a tutorial that I wrote. The servos I'm using are the MG90S kind, and standard joysticks. I've reduced the jitter by reducing the response to small movements in the joystick. Ive also been able to return the joystick to neutral and have almost no movement in the servos. This is somewhat difficult, but essentially if the movement of the servo only responds to movements in the 0-180 degrees, then this isn't so bad.

Here's a link to the tutorial if you're interested:

Arduino Servo Motor Control with a Joystick

Here's a video demonstration to see if it's what you're looking for:

Arduino Joystick Demo - YouTube

And a snapshot of the implementation:

Here's the snippet of code used:

#include <Servo.h>
#include <math.h>

Servo servo_1; // servo controller (multiple can exist)

int servo_pin = 3; // PWM pin for servo control
int joy_pin_x = A0; // pin for x-dir joystick
int joy_pin_y = A1; // pin for y-dir joystick
int offset_x = 0; // subtracting the initial joystick x-location
int offset_y = 0; // subtracting the initial joystick y-location
int pos = 90;    // servo starting position aligned with joystick
int prev_deg = 0; // bound for reducing jitter
int x_prev = 0; // bound for reducing jitter
int y_prev = 0; // reducing jitter

void setup() {
  servo_1.attach(servo_pin); // start servo control
  Serial.begin(9600);
  servo_1.write(pos); // move to center (joystick neutral)
  Serial.println("Positioned at 90 Degrees");  
  offset_x = analogRead(joy_pin_x); // initial joystick x-val
  offset_y = analogRead(joy_pin_y); // initial joystick y-val
}

void loop() {
  int x_val = analogRead(joy_pin_x)-offset_x; // relative joystick x
  int y_val = analogRead(joy_pin_y)-offset_y; // relative joystick y
  if (abs(x_prev-x_val)<10 and abs(y_prev-y_val)<10){
    // reduce jitter
  } else {
    x_prev = x_val;
    y_prev = y_val;
    float deg = 180-(int(atan2(x_val,y_val)*(180.0/PI))+90); // angle calc
    if (abs(deg-prev_deg)>2 and deg>0 and deg<180){
      servo_1.write(deg); // move servo to joystick location
      delay(abs(deg-prev_deg)*(10.0/6.0));
      prev_deg = deg;
      Serial.println(deg); // print out degree
    }
  }
}