Timers and Stepper speed changes with joystick

Hi folks:

The experts among us are very tired of solving timer and delay issues and I understand. However, I have a bit of a twist on the issue and could use some help.

I have a pan/tilt device (actually four) with Arduino, stepper motors and on one, a zoom servo. I'll put my very cumbersome code below.

My twist is this: I need to have smooth camera pans so I have a slow speed programmed to the joystick. I set up a routine that if the joystick (-500 to 500) passes ±450, the motors kick into high speed so that if I need a quick shot of the audience it doesn't take 20 seconds for the camera to swing 150°. The problem is if the operator gets a little heavy handed, the camera switches speeds suddenly. Ideally, I would program in a one or two second delay (counter or milliseconds) for the speed change, so if the joystick gets past 450 for a moment but doesn't stay, it is still smooth. If the joystick is railed for more than the delay time, it can kick into high speed. If the operator leans heavily on the joystick for a moment, it will not jolt into the high speed. If the operator KEEPS the joystick railed, however, it will react and go at its highest speed.

I have a routine in the code to kick into this high speed when the joystick is past ±450, but I have commented it out. I tried to use a counting variable "j," but it didn't seem to be working, partly because I'm unclear where the j=1 reset should be, and don't know whether to use an "if joystick is >450" function or "while joystick >450" function to count up j=++j.

Any ideas?


#include <AccelStepper.h> //accelstepper library
AccelStepper stepper(1, 8, 9); //  pulses Digital 8 (STEP), direction Digital 9 (CCW)
AccelStepper stepperY(1, 10, 11);  //  pulses Digital 10 (STEP), direction Digital 11 (CCW)

// include the Servo library
#include <Servo.h>
Servo myServo;  // create a servo object

/*Author's notes
   This version is for Camera 3 with Servo

   This version (111) will use
   max motor speed * .5
   high speed at end of joystick
   High Speed disabled 2/13/22  Too bouncy

   Using Pololu A4988 at 1/16th step
   MS1  MS2  MS3
     L   L    L   Full Step
     H   L    L   Half Step
     L   H    L   Quarter Step
     H   H    L   Eighth Step
     H   H    H   Sixteenth Step
*/


//Pins
const byte Analog_X_pin = A0; //x-axis readings
const byte Analog_Y_pin = A1; //y-axis readings
const byte homeSetpin = 2; //set home to 0
const byte homeGopin = 3; //go home switch
int redPin = 4; //Red LED pin
int const potPin = A2; // analog pin used to connect the potentiometer for the servo
int servoPin = 12; // Output servoAngle to servo
const byte initializeButton = 7; //Button 3


//Motor data
int enPin = 5;
int enPinY = 6;
int stepPin = 8; // Pan Direction
int dirPin = 9; // Step
int stepPinY = 10;  //Tilt direction
int dirPinY = 11;  //Tilt step
int accelRate = 2000;  //mostly for Go Home routine
int motSpeed = 0;
int motSpeedY = 0;
int speedMult = 1;
int speedMultY;
int valAway = 100;  //  Value away from center before Joystick works
int valAwayServo = 15; // Value away from center before servo moves
int servoRange = 40; // Max deviation of servo from 90
int valAwayFix;
int valAwayFixY;
int speedShift = 450;
int maxSpeed_val = 1000;
int maxSpeed_valY = 1000;
int startPos = 0;
int stepsPerRev = 3200;
int newPos = 20;
int yButtonOld = 1;
int yMotorState = 1;
int xMotorState = -1;
int hiSpeed = 950;
int xSpeedFactor = 2;
int ySpeedFactor = 4;

//  Boundary Limits
long highMax = 1200;
long lowMax = -1200;
long highMaxY = 700;
long lowMaxY = -700;

int safePos = 0;


//Variables
int Analog_X = 0; //x-axis value
int Analog_Y = 0; //y-axis value
int homeSet_Val; //switch value
int homeGo_Val ; //switch value
int initializeButton_Val = 1; //joystick button switch
int Analog_X_AVG = 0; //x-axis value average
int Analog_Y_AVG = 0; //y-axis value average
int Analog_S_AVG = 512; //servo pot value average
int potVal;  // variable to read the value from the analog pin
int servoAngle;   // variable to hold the servoAngle for the servo motor
int servoCenter = 90; //Servo at rest

//int homeGo_Val_Value = 0; //

//Time variables
unsigned long refMillis;
unsigned long currentMillis;
unsigned long stoppedMillis;
unsigned long speedCount = 200000; //j counter for high speed X
const unsigned long period = 1000; //the value is a number of milliseconds, ie 1 second
int j;  //counter one
int j2; //counter two
long j3; //counter three
long j3Max = 10003; //counter three reset
int speedDiff;
int speedDiffY;
int printCount = 1;
int glacierT = 100;
int dt = 1000;
int ydt = 100; //ybutton delay
unsigned long pauseT;
unsigned long pauseTY;

unsigned long pauseMax = 350;
unsigned long pauseMaxY = 350;



void setup()
{
  //SERIAL
  Serial.begin(9600);
  Serial.println ("Begin Sketch");
  //----------------------------------------------------------------------------
  //PINS
  pinMode(Analog_X_pin, INPUT);
  pinMode(Analog_Y_pin, INPUT);
  pinMode(initializeButton, INPUT);
  pinMode(homeSetpin, INPUT);
  pinMode(homeGopin, INPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPinY, OUTPUT);
  pinMode(stepPinY, OUTPUT);
  pinMode(enPin, OUTPUT);
  pinMode(enPinY, OUTPUT);
  pinMode(redPin, OUTPUT);
  pinMode(potPin, INPUT);

  digitalWrite(enPin, HIGH); // disable motor
  digitalWrite(enPinY, HIGH); // disable motor

  Serial.println("disabled before setup");

  digitalWrite (homeSetpin, HIGH);
  digitalWrite (homeGopin, HIGH);
  digitalWrite (initializeButton, HIGH);
  digitalWrite(redPin, LOW);  // turn off redLED
  myServo.attach(12); // attaches the servo on pin 12 to the servo object


  //----------------------------------------------------------------------------
  InitialValues(); //averaging the values of the 3 analog pins (values from potmeters)
  //----------------------------------------------------------------------------
  //Stepper parameters
  //setting up some default values for maximum speed and maximum acceleration
  stepper.setCurrentPosition(0);
  stepper.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
  stepper.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
  stepper.setSpeed(0);

  // stepperY
  stepperY.setCurrentPosition(0);
  stepperY.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
  stepperY.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
  stepperY.setSpeed(0);



  //----------------------------------------------------------------------------
  refMillis = millis();  //initial start time

  Serial.println ("Let's GO!");
}

//-----------------------------------------
//------------       Void Loop        -----
//-----------------------------------------

void loop() {
  // put your main code here, to run repeatedly:
  Analog_X = analogRead(Analog_X_pin);
  Analog_Y = analogRead(Analog_Y_pin);
  homeSet();
  homeGo();
  checkBoundary();
  servoCheck();

  initializeButton_Val = digitalRead(initializeButton); //recalibrates joystick
  if (initializeButton_Val == 0)
  {
    InitialValues();
  }

  //if the value is valAway "value away" from the average (midpoint), we allow the update of the speed
  //This is a sort of a filter for the inaccuracy of the reading

  //-----------------------------------------
  //------------       X Motor Speed    -----
  //-----------------------------------------
  if (abs(Analog_X - Analog_X_AVG) > valAway)
  {
    speedMult = abs(Analog_X - Analog_X_AVG) / (Analog_X - Analog_X_AVG);  //yields +1 or -1 factor
    valAwayFix = valAway * speedMult;
    motSpeed = (((Analog_X - Analog_X_AVG) - valAwayFix) * xMotorState) / xSpeedFactor;  //Set left/right
    stepper.runSpeed(); //step the motor (this will step the motor by 1 step at each loop indefinitely)
    //  digitalWrite(enPin, LOW); // enable motor
    stepper.setSpeed(motSpeed);
    stepper.runSpeed();

    /*
      if (abs(Analog_X - Analog_X_AVG) > speedShift)
      {
        j=++j;
        if (j > speedCount);
        {
          motSpeed = (maxSpeed_val / 2) * (motSpeed / (abs(motSpeed)));
          stepper.setSpeed(motSpeed);
          stepper.runSpeed();
        }
      }
      else

      {
        j = 1;
      }
    */

    //----------------X Motor Print-----------------------
    currentMillis = millis();  //
    if (currentMillis - refMillis >= period)  //test whether the period has elapsed
    {
      Serial.print ("Analog_X= ");
      Serial.print (Analog_X);
      Serial.print (" Current Position X =");
      Serial.print (stepper.currentPosition());
      Serial.print (" motSpeed X = ");
      Serial.println (motSpeed);

      refMillis = currentMillis;  //IMPORTANT to save the start time of the speed printout.

    }
    else
    {
      motSpeed = 0;
      printCount = 1; //reset for homeGo routine
    }
  }  //---------------------------end Analog_X val check

  //-----------------------------------------
  //------------       Y Motor Speed    -----
  //-----------------------------------------
  if (abs(Analog_Y - Analog_Y_AVG) > valAway)
  {
    speedMultY = abs(Analog_Y - Analog_Y_AVG) / (Analog_Y - Analog_Y_AVG);
    valAwayFixY = valAway * speedMultY;
    motSpeedY = ((Analog_Y - Analog_Y_AVG - valAwayFixY) * yMotorState) / ySpeedFactor; //determines flightStick or gamerStick
    stepperY.runSpeed();
    stepperY.setSpeed(motSpeedY);
    stepperY.runSpeed();

    /*
       if (abs(Analog_Y - Analog_Y_AVG) > speedShift) // Full speed at end of joystick
       {
         motSpeedY = (maxSpeed_valY/2) * (motSpeedY / (abs(motSpeedY))); //get sign of motor speed
         stepperY.setSpeed(motSpeedY);
         //     stepperY.runSpeed();
         stepperY.run();


       }
    */

    //----------Y motor print  -------------
    currentMillis = millis();  //
    if (currentMillis - refMillis >= period)  //test whether the period has elapsed
    {
      Serial.print ("Analog_ Y= ");
      Serial.print (Analog_Y);
      Serial.print (" Current Position Y =");
      Serial.print (stepperY.currentPosition());
      Serial.print (" motSpeed Y = ");
      Serial.println (motSpeedY);


      refMillis = currentMillis;  //IMPORTANT to save the start time of the speed printout.
    }

  }

  else
  {
    motSpeedY = 0;
  }


  j3 = ++j3;
  if (j3 >= j3Max) //test whether the period has elapsed
  {
    Serial.print (j3);
    Serial.print (" Waiting on Joystick X & Y= ");
    Serial.print (Analog_X);
    Serial.print (" ");
    Serial.print (Analog_Y);
    Serial.print (" motSpeed X&Y = ");
    Serial.print (motSpeed);
    Serial.print (" ");
    Serial.println (motSpeedY);
    Serial.print("potVal: ");
    Serial.print(potVal);
    Serial.print(", servoAngle: ");
    Serial.println(servoAngle);

    j3 = 1; //IMPORTANT to save the start time of the speed printout.
  }

}  // end void loop

//----------------------------------------\
//-----------     ROUTINES    ------------->
//----------------------------------------/


//-------------------------------------------
//-----------     Initial Values()    -----
//------------------------------------------
void InitialValues()
{

  blinkRed();
  stepper.setSpeed(0);
  stepperY.setSpeed(0);

  motSpeed = 0;
  motSpeedY = 0;

  //  digitalWrite(enPin, HIGH); // disable motor
  //Set the values to zero before averaging
  float tempX = 0;
  float tempY = 0;
  float tempS = 0;

  //----------------------------------------------------------------------------
  //read the analog 50x, then calculate an average.
  //they will be the reference values
  for (int i = 0; i < 50; i++)
  {
    tempX += analogRead(Analog_X_pin);
    delay(10); //allowing a little time between two readings
    tempY += analogRead(Analog_Y_pin);
    delay(10);
    tempS += analogRead(potPin);
    delay(10);

  }
  //----------------------------------------------------------------------------
  Analog_X_AVG = tempX / 50;
  Analog_Y_AVG = tempY / 50;
  Analog_S_AVG = tempS / 50;

  digitalWrite(enPin, HIGH); // disable motor
  digitalWrite(enPinY, HIGH); // disable motor

  //----------------------------------------------------------------------------
  Serial.print("AVG_X: ");
  Serial.println(Analog_X_AVG);
  Serial.print("AVG_Y: ");
  Serial.println(Analog_Y_AVG);
  Serial.print("AVG_S: ");
  Serial.println(Analog_S_AVG);
  Serial.println("Calibration finished, still disabled");
  // two blinks
  blinkRed();
  delay(ydt * 4);
  blinkRed();
}


//-------------------------------------------
//-----------     blinkRed()    -----
//------------------------------------------
void blinkRed() //Red LED Blink
{
  digitalWrite(redPin, HIGH);
  delay(ydt);
  digitalWrite(redPin, LOW);

}


//-------------------------------------------
//-----------     homeSet()    ------------
//------------------------------------------
void homeSet()
{
  homeSet_Val = digitalRead(homeSetpin);
  if (homeSet_Val == 0) //set home location
  {
    blinkRed();
    stepper.setCurrentPosition(0);  // Set the current position as zero for now
    Serial.print("Home - X");
    stepper.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
    stepper.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
    stepper.setSpeed(motSpeed);
    stepper.runSpeed();
    stepperY.setCurrentPosition(0);  // Set the current position as zero for now
    Serial.print(" Home - Y");
    digitalWrite(enPin, LOW); // enable X motor
    digitalWrite(enPinY, LOW); // enable Y motor
    Serial.println(" Motors Enabled by Home Switch");
    stepperY.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
    stepperY.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
    stepperY.setSpeed(motSpeed);
    stepperY.runSpeed();
    //   myServo.write(servoCenter);

  }
}

//-------------------------------------------
//-----------     homeGo()    ------------
//------------------------------------------
void homeGo()
{
  homeGo_Val = digitalRead (homeGopin);

  if (homeGo_Val == 0) // Go Home
  {
    blinkRed();
    speedMult = abs (stepper.currentPosition()) / stepper.currentPosition();
    speedMultY = abs (stepperY.currentPosition()) / stepperY.currentPosition();


    Serial.print (stepper.currentPosition());
    Serial.print (" X < Going Home > Y ");
    Serial.println (stepperY.currentPosition());
    stepper.moveTo (0);
    motSpeed = (maxSpeed_val * .5);
    motSpeedY = (maxSpeed_val * .5);
    stepper.setSpeed(motSpeed);
    stepperY.moveTo (0);
    stepperY.setSpeed(motSpeedY);

    while (stepper.currentPosition() != stepper.targetPosition())
    {
      //     stepper.runSpeedToPosition();
      stepper.runToPosition();

    }

    while (stepperY.currentPosition() != stepperY.targetPosition())
    {
      //     stepperY.runSpeedToPosition();
      stepperY.runToPosition();

    }


  }

  // If move is completed display message on Serial Monitor
  if ((stepper.distanceToGo() == 0 && printCount < 2))
  {
    Serial.println("Now Home!");
    motSpeed = 0;
    motSpeedY = 0;

    printCount = printCount + 1;
  }

}



//----------------------------------------------------------------------------
//                 checkBoundary()
//----------------------------------------------------------------------------
void checkBoundary() //Protection from over rotation
{
  //---------------- X Boundary
  if (stepper.currentPosition() <= lowMax || stepper.currentPosition() >= highMax)
  {
    stepper.setSpeed(0);
    Serial.print ("Over Rotated ");
    Serial.println (stepper.currentPosition());
    delay (dt);
  }
  if (stepper.currentPosition() <= lowMax) // Negative Overrotation
  {
    safePos = (lowMax + 100);
    stepper.moveTo (safePos);
    stepper.setSpeed(maxSpeed_val / 4);

    while (stepper.currentPosition() != stepper.targetPosition())
    {
      stepper.runSpeedToPosition();
    }
  }
  if (stepper.currentPosition() >= highMax) // Positive Overrotation
  {
    safePos = (highMax - 100);
    stepper.moveTo (safePos);
    stepper.setSpeed(maxSpeed_val / 4);
    while (stepper.currentPosition() != stepper.targetPosition())
    {
      stepper.runSpeedToPosition();

    }
  }

  //-----------  Y Boundary
  if (stepperY.currentPosition() <= lowMaxY || stepperY.currentPosition() >= highMaxY)
  {
    stepperY.setSpeed(0);
    Serial.print ("Over Rotated ");
    Serial.println (stepperY.currentPosition());
    delay (dt);
  }
  if (stepperY.currentPosition() <= lowMaxY) // Negative Overrotation
  {

    safePos = (lowMaxY + 100);
    stepperY.moveTo (safePos);
    stepperY.setSpeed(maxSpeed_val / 4);

    while (stepperY.currentPosition() != stepperY.targetPosition())
    {
      stepperY.runSpeedToPosition();
    }
  }
  if (stepperY.currentPosition() >= highMaxY) // Positive Overrotation
  {
    safePos = (highMaxY - 100);
    stepperY.moveTo (safePos);
    stepperY.setSpeed(maxSpeed_val / 4);
    while (stepperY.currentPosition() != stepperY.targetPosition())
    {
      stepperY.runSpeedToPosition();

    }
  }

}

//----------------------------------------------------------------------------
//                 servoCheck()
//----------------------------------------------------------------------------
void servoCheck()
{
  potVal = analogRead(potPin); // read the value of the potentiometer

  // scale the numbers from the pot
  servoAngle = map(potVal, 0, 1023, (90 + servoRange), (90 - servoRange));


  if (abs(potVal - Analog_S_AVG) > valAwayServo)
  { // set the servo position
    myServo.write(servoAngle);

  }
  else {
    //  servoAngle = 90;
    myServo.write(servoCenter);

  }
  /* while (abs(potVal - Analog_S_AVG) <= valAwayServo)
    {
     myServo.write(servoCenter);
    }
  */
}

Consider a "turbo" button connected to a pin that multiplies the joystick? Somne joysticks have a pushbutton feature when pressed.

Hi xfpd:
That's actually a good idea. I had given it some thought, but the one camera that I really need it on already has all 8 lines in the Cat6 used: 5v, ground, x, y, set home, go home, recalibrate and zoom servo. The other three, though, have a spare line since they have a different remote for the zoom.

Thanks.

first of all speed up serial aoutput by changing from

Serial.begin(9600);

to

Serial.begin(115200);

You want to have a functionality where

if joystick is pushed to the max for longer than 2 seconds switch to a higher speed

So the criterium is a combination of joystick-position and time
Your code should reflect exactly that.
counting up a variable does not use time

It can be done by using non-blocking timing based on function millis()

untested but shows the principle

if (joyStickPosition < 450) {
  JoyStickAtMax = false;
}
else {
  JoyStickAtMax = true;
}


if (lastJoyStickAtMax == false && JoyStickAtMax == true) {
  // only in case joystick-position has CHANGED  RIGHT NOW 
  // from beeing not at max to beeing at max
  highSpeedPosStarted = millis(); // store snapshot of time 
}

// changing back joyStickPosition < 450 after a short time
// and then going up to joyStickPosition GREATER as 450 new
// updates snapshot of time highSpeedPosStarted
// ==> starts a new period of delaying changingto HIGH_Speed

if (JoyStickAtMax == true) {
  if (millis() - highSpeedPosStarted >= 2000) {
    // only if joystick has been CONTINIOULSY in max position 
    // for more than 2000 milliseconds do
    motorSpeed = HIGH_Speed;
  }
  else {
    motorSpeed = slow_Speed;
  }
}
else {
  motorSpeed = slow_Speed;
}

// update lastJoyStickAtMax
lastJoyStickAtMax = JoyStickAtMax; 

best regards Stefan

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.