2 motors controlled by one joystick

TimCoyne:
Hi,

I solved this problem a few months ago with the help of a very clever member of the Arduino community...

Search for this thread:

Differential Steering using a joystick / programming question....

I hope that helps.

Tim

Here it is:

http://forum.arduino.cc/index.php?topic=172581.0

I solved this problem a few months ago with the help of a very clever member of the Arduino community...

One potentiometer controls the left wheel, and the other controls the right. There is currently no reverse, although by remapping the pots to say, -10 to 245 and using an if statement that could easily be handled.

Did you actually provide for reverse?

zoomkat:

One potentiometer controls the left wheel, and the other controls the right. There is currently no reverse, although by remapping the pots to say, -10 to 245 and using an if statement that could easily be handled.

Did you actually provide for reverse?

I actually never understood what he was saying in that statement, either - my algorithm, IIRC, as written will automagically handle reverse (I think).

Its actually very simple. A pot has a range from 0 - 1023, and being that you want to control a motor or servo, you will most likely want full forward, full reverse and stop.
Well forward is equal to anything above 512(mid point of pot), and reverse is anything below 512. Now you also want to stop the motor, and that done when your at 512.
So you will need an IF statement.

psudo:

int XSpeed = analogRead( XpotPin );

if ( XSpeed > (512 + deadzone) ) //go forward
...

else if ( XSpeed < (512 - deadzone) ) //go reverse 
...

else //XSpeed = 512 +- deadzone, stop

deadzone is needed because your pot will almost never perfectly be centered at 512, so some slack is needed

Its actually very simple.

Can you post the combined working code? The problem is solvable to a certain extent, but I haven't seen single code (that I understand) that provides forward/reverse and left/right from a joystick, especially if the stick is pushed in a diagonal direction. I currently do any actual testing with pots and servos as my hardware is currently disconnected and in various places.

I'll post the full working code later when I get home.

Ok, I have here the simplest form of single joystick control. It by default assumes that the motor pins have pull down resistors, but if you don't have pull down resistors, then just uncomment the other lines.

/*
Simple motor control with square limit joystick

      Y(1023)
    |---------|
    |---------|
X(0)|---512---|X(1023)
    |---------|
    |---------|
       Y(0)
       
 Left Motor Forward/Reverse = LMF/LMR
 Right Motor Forward/Reverse = RMF/RMR
*/

const byte LMF = 3;
const byte LMR = 5;
const byte RMF = 6;
const byte RMR = 9;

const byte Xpot = A0;
const byte Ypot = A1; 

volatile unsigned int X = 512, Y = 512; // pot values default 512 for center
unsigned int Xspeed = 0, Yspeed = 0; //motor speed default 0 for full stop
const int Xdeadzone = 5, Ydeadzone = 5; //amount of slack needed for pot values

void setup(){
  pinMode(LMF, OUTPUT);
  pinMode(LMR, OUTPUT);
  pinMode(RMF, OUTPUT);
  pinMode(RMR, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  X = analogRead(Xpot);
  Y = analogRead(Ypot);

  if (X >= (512 + Xdeadzone))//Forward
  { 
    Xspeed = (X - 512) / 2; // 0 - 255
    if(Y > (512 + Ydeadzone)) //Left
    {
      Yspeed = (Y - 512) / 2;
      analogWrite(LMF, Xspeed - Yspeed); analogWrite(RMF, Xspeed);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW);
    }
    else if (Y < (512 - Ydeadzone)) //Right
    {
      Yspeed = (512 - Y) / 2;
      analogWrite(LMF, Xspeed); analogWrite(RMF, Xspeed - Yspeed);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW);
    }
    else
    {
      analogWrite(LMF, Xspeed); analogWrite(RMF, Xspeed);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW);
    }
  }

  else if (X <= (512 - Xdeadzone))//Reverse
  { 
    Xspeed = (512 - X) / 2;
    if(Y > (512 + Ydeadzone)) //Left
    {
      Yspeed = (Y - 512) / 2;
      //digitalWrite(LMF, LOW); digitalWrite(RMF, LOW);
      analogWrite(LMR, Xspeed - Yspeed); analogWrite(RMR, Xspeed);
    }
    else if (Y < (512 - Ydeadzone)) //Right
    {
      Yspeed = (512 - Y) / 2;
      //digitalWrite(LMF, LOW); digitalWrite(RMF, LOW);
      analogWrite(LMR, Xspeed); analogWrite(RMR, Xspeed - Yspeed);
    }
    else
    {
      //digitalWrite(LMF, LOW); digitalWrite(RMF, LOW);
      analogWrite(LMR, Xspeed); analogWrite(RMR, Xspeed);
    }
  }

  else // X is between 512 +- deadzone
  {
    if(Y > (512 + Ydeadzone)) // zero point turn Left
    {
      digitalWrite(LMF, LOW); analogWrite(RMF, Yspeed);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW);
    }
    else if(Y < (512 - Ydeadzone))// zero point turn Right
    {
      analogWrite(LMF, Yspeed); digitalWrite(RMF, LOW);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW); 
    }
    else
    { // Full stop
      digitalWrite(LMF,LOW);
      digitalWrite(RMF,LOW);
      digitalWrite(LMR,LOW);
      digitalWrite(RMR,LOW);
    }
  }
}

Simple motor control with square limit joystick

I assume this means no diagonal stick movement. Correct?

This use every position, which includes diagonal.

I called it square limit because the older joysticks like the Atari joystick have a square range. But the current ones like those used on ps2 controller, xbox, or anything else that uses a joystick, has a round limit. These I think need to use sine and cosine to get there full range of movement. I'll do some tests to confirm if that's true or not, later.

Atari joystick:

The one I posted earlier is the one I am currently using, and it works for diagonal positions too. The only difference is that the first one uses the map function, whereas this one is straight forward using IF ELSE to take the difference of X and Y positions to go a certain direction.

The first one I posted is neater but a little more confusing, compared to this "simpler" code that breaks it all down. Not as nice but not as confusing.

so this is the working combined code?

HazardsMind:

/*

Simple motor control with square limit joystick

Y(1023)
    |---------|
    |---------|
X(0)|---512---|X(1023)
    |---------|
    |---------|
       Y(0)
       
Left Motor Forward/Reverse = LMF/LMR
Right Motor Forward/Reverse = RMF/RMR
*/

const byte LMF = 3;
const byte LMR = 5;
const byte RMF = 6;
const byte RMR = 9;

const byte Xpot = A0;
const byte Ypot = A1;

volatile unsigned int X = 512, Y = 512; // pot values default 512 for center
unsigned int Xspeed = 0, Yspeed = 0; //motor speed default 0 for full stop
const int Xdeadzone = 5, Ydeadzone = 5; //amount of slack needed for pot values

void setup(){
  pinMode(LMF, OUTPUT);
  pinMode(LMR, OUTPUT);
  pinMode(RMF, OUTPUT);
  pinMode(RMR, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
  X = analogRead(Xpot);
  Y = analogRead(Ypot);

if (X >= (512 + Xdeadzone))//Forward
  {
    Xspeed = (X - 512) / 2; // 0 - 255
    if(Y > (512 + Ydeadzone)) //Left
    {
      Yspeed = (Y - 512) / 2;
      analogWrite(LMF, Xspeed - Yspeed); analogWrite(RMF, Xspeed);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW);
    }
    else if (Y < (512 - Ydeadzone)) //Right
    {
      Yspeed = (512 - Y) / 2;
      analogWrite(LMF, Xspeed); analogWrite(RMF, Xspeed - Yspeed);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW);
    }
    else
    {
      analogWrite(LMF, Xspeed); analogWrite(RMF, Xspeed);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW);
    }
  }

else if (X <= (512 - Xdeadzone))//Reverse
  {
    Xspeed = (512 - X) / 2;
    if(Y > (512 + Ydeadzone)) //Left
    {
      Yspeed = (Y - 512) / 2;
      //digitalWrite(LMF, LOW); digitalWrite(RMF, LOW);
      analogWrite(LMR, Xspeed - Yspeed); analogWrite(RMR, Xspeed);
    }
    else if (Y < (512 - Ydeadzone)) //Right
    {
      Yspeed = (512 - Y) / 2;
      //digitalWrite(LMF, LOW); digitalWrite(RMF, LOW);
      analogWrite(LMR, Xspeed); analogWrite(RMR, Xspeed - Yspeed);
    }
    else
    {
      //digitalWrite(LMF, LOW); digitalWrite(RMF, LOW);
      analogWrite(LMR, Xspeed); analogWrite(RMR, Xspeed);
    }
  }

else // X is between 512 +- deadzone
  {
    if(Y > (512 + Ydeadzone)) // zero point turn Left
    {
      digitalWrite(LMF, LOW); analogWrite(RMF, Yspeed);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW);
    }
    else if(Y < (512 - Ydeadzone))// zero point turn Right
    {
      analogWrite(LMF, Yspeed); digitalWrite(RMF, LOW);
      //digitalWrite(LMR, LOW); digitalWrite(RMR, LOW);
    }
    else
    { // Full stop
      digitalWrite(LMF,LOW);
      digitalWrite(RMF,LOW);
      digitalWrite(LMR,LOW);
      digitalWrite(RMR,LOW);
    }
  }
}

Hi all,

I have run the code that HazardsMind has posted manually, and as far as I can see it is not doing what it is supposed to do?!

Did anyone actually load this into the Arduino, with a Left DC Motor and Right DC motor, and got it to move forward when you push Y up, backwards when you push Y down, and turn left-right when X is pushed left-right?

I want to use servo and radio functions but the includs are incompatible.
Has some one resolve the problem.
Thanks

I
I am french and I have a same problem with arduino and motorshield adafruit.

I try to control a robot with a joystick.

I can control 2 motors directly and I can get the location of the joystick with a switch cas. But I can't connect both to conduct my robot.

Please help me if you can.

thank a lot,

jp

My code for my robot projet is:
// Librairie Adafruit
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"

Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_MotorShield monShield = Adafruit_MotorShield(); //création de l'objet shield
Adafruit_DCMotor *moteurGauche = monShield.getMotor(1); //création de l'objet moteurGauche par pointeur et repérage du numéro
Adafruit_DCMotor *moteurDroite = monShield.getMotor(2); //création de l'objet moteurDroite par pointeur et repérage du numéro

// Branchement joystick et moteur
const int x = A0;
const int xMin = 0;
const int xMax = 1023;
const int y = A4;
const int yMin = 0;
const int yMax = 1023;
int neut = 20; // zone neutre
int neutn = -20; // zone neutre négative

int zone;
int zone_val;

// Définition des variables
int lecX, lecY, calX, calY, retX, retY, vit;

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
void setup () {
// Initialisation port serie
Serial.begin(9600);
AFMS.begin();
zone_val = 10;
zone = 10;
// Lecture des valeurs en x et y
lecX = analogRead(x);
lecY = analogRead(y);
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void loop() {

// Calibration du joystick en 0,0
calX = analogRead(x)-lecX;
calY = analogRead(y)-lecY;

Serial.print("+++++++> valeur_calX :") && Serial.println (calX);
Serial.print("+++++++> valeur_calY :") && Serial.println (calY);
delay (100);

/*
Serial.print("SENS INITIAL MOTEUR 1 RELEASE++++++> :")&&Serial.println (sensM1);
Serial.print("SENS INITIAL MOTEUR 2 RELEASE++++++> :")&&Serial.println (sensM2);
Serial.print("SENS TEST MOTEUR 3 FORWARD++++++> :")&&Serial.println (sensM3);

delay (1000);

//sensM1 = "FORWARD";
//sensM2 = "FORWARD";
//Serial.print("SENS MOTEUR 1 FORWARD++++++> :") && Serial.println ('sensM1');
//Serial.print("SENS MOTEUR 2 FORWARD++++++> :") && Serial.println ('sensM2');
delay(1000);
*/

// Réatalonnage x et y de 0,1023 à -255,255
//Ré-étalonne la valeur entre 0 et 1023 sur une fourchette entre 0 et 255
retX = map(calX, xMin, xMax, -255 , 255);
retX= constrain(calX, -255, 255);
retY = map(calY, yMin, yMax, -255, 255);
retY = constrain(calY, -255, 255);

Serial.print("=======> valeur_X :") && Serial.println (retX);
Serial.print("=======> valeur_Y :") && Serial.println (retY);
delay (100);

if ((abs(retX)) > (abs(retY)))
{
vit = (abs(retX));
}
else
{
vit = (abs(retY));
}
//++++++++++++++++++

calcul_zone(retX, retY);
Serial.println("POSITION JOYSTICK case valeur :");
Serial.print("======> valeur_X :") && Serial.println (retX);
Serial.print("=======> valeur_Y :") && Serial.println (retY);
delay(100);

Serial.print("ZONE VAL ======> :") && Serial.println(zone);
delay(100);

Serial.println("BONJOUR");
impression(zone);
delay(2000);
ACTION_MOTOR(zone);
//delay(500);
//arret();
//delay(2000);
//Serial.print("ZONE VAL ======> :") && Serial.println(zone_val);
Serial.println("AU REVOIR");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~é
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// CALCUL DE ZONE PAR RAPPORT A LA POSITION JOYSTICK (RetX, RetY);

void calcul_zone(int XX, int YY)
{
if (YY <= neutn)
{
if (XX >= neut){zone = 4;}
if (XX <= neutn){zone = 6;}
if ((XX < neut) && (XX > neutn)) {zone = 5;}
}
if (YY >= neut)
{
if (XX >= neut){zone = 2;}
if (XX <= neutn){zone = 8;}
if ((XX < neut) && (XX > neutn)) {zone = 1;}
}
if ((YY > neutn) && (YY < neut))
{
if (XX >= neut){zone = 3;}
if (XX <= neutn){zone = 7;}
if ((XX < neut) && (XX > neutn)) {zone = 0;}
}
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@à
//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&++++++++++++++++++++++++++++++++++++++++++++++++++
void arret(){
//fonction d'arrêt des deux moteurs
moteurGauche->run(RELEASE);
moteurDroite->run(RELEASE);
}
void defVitesse(int v){
moteurGauche->setSpeed(v); //on redéfinit la vitesse
moteurDroite->setSpeed(v); //des deux moteurs
}
void avance(int v){
//fonction de marche avant
//defVitesse(v);
moteurGauche->setSpeed(v);
moteurDroite->setSpeed(v);
//appel de la fonction pour définir la vitesse
moteurGauche->run(FORWARD);
moteurDroite->run(FORWARD);
}
void recule(int v){
//fonction de marche arrière
defVitesse(v);
moteurGauche->run(BACKWARD);
moteurDroite->run(BACKWARD);
}
void tourneDroite(int v){
//fonction pour tourner à droite sur place
defVitesse(v);
moteurGauche->run(FORWARD);
moteurDroite->run(BACKWARD);
}
void tourneGauche(int v){
//fonction pour tourner à gauche sur place
defVitesse(v);
moteurGauche->run(BACKWARD);
moteurDroite->run(FORWARD);
}
//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&++++++++++++++++++++++++++++++++++++++++++++++++
//§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§
void impression(int zone)
{
switch (zone)
{
case 0:
Serial.println("Robot STOP");
break;
case 1:
Serial.println("Robot AVANCE");
delay(10);
break;
case 2:
Serial.println("Robot AVANCE et TOURNE à GAUCHE");
break;
case 3:
Serial.println("Robot TOURNE à GAUCHE toute");
break;
case 4:
Serial.println("Robot RECULE et TOURNE à GAUCHE");
break;
case 5:
Serial.println("Robot RECULE");
break;
case 6:
Serial.println("Robot RECULE et TOURNE à DROITE");
break;
case 7:
Serial.println("Robot TOURNE à DROITE toute");
break;
case 8:
Serial.println("Robot AVANCE et TOURNE à DROITE");
break;
default:
break;
}
}
//§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§
//:::::::::::::::::::::::::::::::::::::::::::::::::
void ACTION_MOTOR(int zone)
{
switch (zone)
{
case 0:
Serial.println("ACTION MOTOR Robot STOP");
arret();
break;
case 1:
Serial.println("ACTION MOTOR Robot AVANCE");
avance(100);
delay(500);
arret();
break;
case 2:
Serial.println("ACTION MOTOR Robot AVANCE et TOURNE à GAUCHE");
break;
case 3:
Serial.println("ACTION MOTOR Robot TOURNE à GAUCHE toute");
break;
case 4:
Serial.println("ACTION MOTOR Robot RECULE et TOURNE à GAUCHE");
break;
case 5:
Serial.println("ACTION MOTOR Robot RECULE");
break;
case 6:
Serial.println("ACTION MOTOR Robot RECULE et TOURNE à DROITE");
break;
case 7:
Serial.println("ACTION MOTOR Robot TOURNE à DROITE toute");
break;
case 8:
Serial.println("ACTION MOTOR Robot AVANCE et TOURNE à DROITE");
break;
default:
break;
}
}

//:::::::::::::::::::::::::::::::::::::::::::::::::::

My code for my robot projet is:

Please read #7 below and put your code in code tags:

http://forum.arduino.cc/index.php/topic,148850.0.html

@jp31310: I did not go into detail with your code, but I think we are in the same line of thought: I have defined "quadrants" and based on the "quadrant" different logic takes place.

You can find my code here: GitHub - drazha/Son-Dad-Rover-Mk-I: 1st Rover design and project based on Arduino UNO R3 controller and off-the-shelf-components

It is a bit messy and requires refactoring, but the general idea is there...

zoomkat:
Can you post the combined working code? The problem is solvable to a certain extent, but I haven't seen single code (that I understand) that provides forward/reverse and left/right from a joystick, especially if the stick is pushed in a diagonal direction. I currently do any actual testing with pots and servos as my hardware is currently disconnected and in various places.

Does my logic - see posts #4 and #5 at:

http://forum.arduino.cc/index.php?topic=172581.0

...not work? The OP seemed to think it worked.

I mean - if it doesn't work, I'd certainly like to know - but the logic seems sound.

HazardsMind:
deadzone is needed because your pot will almost never perfectly be centered at 512, so some slack is needed

For many (most? all?) of the PS2 style thumbsticks - they have a "built-in" deadzone around the center of the stick (in some cases I have heard it is quite large - almost to the point of making the stick unusable). Just something to keep in mind; it should be possible to figure out what the size of the zone is with some test code.

Alternatively, if you have a display available (serial or maybe an LCD) - you could create a "calibration code" section for the thumbstick like the old PC games used that would instruct you to push the stick up to one corner, push it down to the opposite corner, perhaps move it around in a circle, then center it - at each step instructing you to "press a button" (and don't make this the "stick button" that many of these units have - as that could throw off the reading - though with the deadzone it may be ok - eh, play with it). It might even be possible to do the calibration with an LED and some "intelligent" code (and/or a buzzer or something?).

1 Like

I know that this is an old forum, but for future reference,

#include <Servo.h>
Servo myServo1;
Servo myServo2;
int lXPin = A0;
int lYPin = A1;
int lX;
int lY;
int l;
int r;
void setup() {
  myServo1.attach(3);
  myServo2.attach(4);
}
void loop() {
  lX = analogRead(lXPin);
  lY = analogRead(lYPin);
  l = lX + lY - 90;
  r = lX - lY + 90;
  myServo1.write(r);
  myServo2.write(l);
}