Hi all:
Background:
I have cobbled together a pan/tilt device for cameras using stepper motors and a joystick. It all works relatively well. Aside from pan and tilt, I have three buttons for Set Home, Go Home and Recalibrate, the latter using code from another user which averages the centering error of an analog joystick as well as de-energizes the steppers. I built a dual joystick box for the pan/tilt steppers and zoom servo, using shielded Cat6 cables to go from the joystick to the Arduino in a box next to the pan/tilt device. The joystick generates -500 to +500 (-ish) and that gets put into a motSpeed that gets multiplied by a few factors to reset the orientation of the joystick and compensate for desired speeds.
Problem:
The numbers from the position of the joystick go into the setSpeed() command, then the steppers get the runSpeed() command. Those commands do not take advantage of the acceleration and deceleration programming that smoothes out the movement. (I am taking advantage of the acceleration programming for the Go Home routine.)
In AccelStepper, the acceleration does not work with the setSpeed() and runSpeed() commands. It seems to only work with the run() command, but then what is the speed set at? (side question: Does the run() command use the setAcceleration() and setMaxSpeed() commands to accelerate to the max speed only if there is time between the acceleration and deceleration times?)
Question:
Can you use acceleration from AccelStepper while getting speed settings from a joystick?
Here is a snippet of the joystick>stepper code for the X axis (full code below):
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.
}
I'm placing my lengthy, probably very overweight code below.
#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);
}
*/
}