Hi Forum:
I have built a pan/tilt device with two steppers, a joystick and an Arduino Leonardo. (There is also a servo for the zoom.) To keep the cords from tangling, I have a boundary set at either extreme of X or Y rotation so that it cannot go around endlessly. If it reaches the boundary it pauses, then backs up 100 steps to a "safePos" position.
The code is written so that when the unit is powered up or rebooted, the motors are not energized. A "Home" button energizes the motors and sets the current position to zero. That Home button is also usable to mark the most common position for that camera, kind of doubling as a shot preset. That's where the boundary limit number is calculated... think ±160° or so from the center position (at least for X).
So an operator presses Home on a wide shot of the stage from the left, swings around to get the audience, then hits the Go Home button to get back to the stage. The Go Home routine is blocking intentionally.
It all works as expected, unless...
If the camera gets to the boundary in either X direction, the Go Home button makes it act really erratically. If it hits the boundary in the positive direction, it pauses and goes back 100 steps as programmed. If you THEN hit Go Home, it takes a 15 or 20 second creep in the positive direction (yes, past the boundary), stops, shudders, then goes home.
If the camera hits the boundary in the negative direction, it stops, backs up 100 steps and stops, as programmed. Then, if you hit Go Home, it goes home but at a snail's pace.
After hitting the boundary in either direction, if you activate the (blocking) Go Home routine, these anomalies happen and you are blocked out until it finally arrives Home. Then, everything works again.
So, the Go Home button works, as long as you stay away from the X boundaries. Incidentally, the joystick works after you hit the boundaries, so long as you don't hit Go Home first. Go Home seems to work after any joystick move, but does not work (as planned) after hitting a boundary. [insert <Doc it hurts when I do this... Well, don't do that!> here]
Something in the Boundary routine changes parameters so that the Go Home routine works abnormally (consistently). But, if you use the joystick immediately after the Boundary routine, then the Go Home will be normal.
I'm inserting the Go Home routine and the Boundary routines here, and below that I am putting my whole code.
Blessings,
rcarbaugh
//-------------------------------------------
//----------- homeGo() ------------
//------------------------------------------
void homeGo()
{
homeGo_Val = digitalRead (homeGopin);
if (homeGo_Val == 0) // Go Home
{
blinkRed();
printCount = 1; //reset for homeGo routine
directionX = abs (stepper.currentPosition()) / stepper.currentPosition();
directionY = abs (stepperY.currentPosition()) / stepperY.currentPosition();
Serial.print (stepper.currentPosition());
Serial.print (" X < Going Home > Y ");
Serial.println (stepperY.currentPosition());
stepper.moveTo (0);
motSpeedX = (maxSpeed_val);
stepper.setSpeed(motSpeedX);
stepperY.moveTo (0);
motSpeedY = (maxSpeed_val);
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!");
motSpeedX = 0;
motSpeedY = 0;
// printCount = printCount + 1;
printCount++;
}
}
//----------------------------------------------------------------------------
// checkBoundary()
//----------------------------------------------------------------------------
void checkBoundary() //Protection from over rotation
{
//---------------- X Boundary
if (stepper.currentPosition() <= lowMax || stepper.currentPosition() >= highMax)
{
stepper.setSpeed(0);
Serial.print ("Over Rotated X ");
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();
// stepper.runToPosition();
// motSpeedX = 0;
}
}
if (stepper.currentPosition() >= highMax) // Positive Overrotation
{
safePos = (highMax - 100);
stepper.moveTo (safePos);
stepper.setSpeed(maxSpeed_val / 4);
// stepper.setMaxSpeed(maxSpeed_val / 2);
while (stepper.currentPosition() != stepper.targetPosition())
{
stepper.runSpeedToPosition();
// stepper.runToPosition();
// motSpeedX = 0;
}
}
//----------- Y Boundary
if (stepperY.currentPosition() <= lowMaxY || stepperY.currentPosition() >= highMaxY)
{
stepperY.setSpeed(0);
Serial.print ("Over Rotated Y ");
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();
motSpeedY = 0;
}
}
if (stepperY.currentPosition() >= highMaxY) // Positive Overrotation
{
safePos = (highMaxY - 100);
stepperY.moveTo (safePos);
stepperY.setSpeed(maxSpeed_val / 4);
while (stepperY.currentPosition() != stepperY.targetPosition())
{
stepperY.runSpeedToPosition();
motSpeedY = 0;
}
}
}
Here's my whole code:
#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- Panasonic HC-V770
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 = 1500; //mostly for Go Home routine (higher is faster)
int motSpeedX = 0;
int motSpeedY = 0;
int directionX = 1;
int directionY;
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 hiSpeedX = 950;
int loSpeedX;
int xSpeedFactor = 2;
int ySpeedFactor = 4;
// Boundary Limits
long highMax = 1500;
long lowMax = -2000;
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
bool joyStickAtMax = false;
bool lastjoyStickAtMax = false;
//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 = 7003; //counter three reset
int speedDiff;
int speedDiffY;
int printCount = 1;
int glacierT = 100;
int dt = 500;
int ydt = 100; //ybutton delay
unsigned long pauseT;
unsigned long pauseTY;
unsigned long pauseMax = 350;
unsigned long pauseMaxY = 350;
long highSpeedPosStarted;
int hiSpeedDelay = 1000;
void setup()
{
//SERIAL
Serial.begin(115200);
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);
checkBoundary();
servoCheck();
//-----------------------------------------
//------------ X Motor Speed -----
//-----------------------------------------
if (abs(Analog_X - Analog_X_AVG) > valAway)
{
/*
directionX = abs(Analog_X - Analog_X_AVG) / (Analog_X - Analog_X_AVG); //yields +1 or -1 factor
valAwayFix = valAway * speedMult;
motSpeedX = (((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(motSpeedX) ;
stepper.runSpeed();
*/
directionX = abs(Analog_X - Analog_X_AVG) / (Analog_X - Analog_X_AVG); //yields +1 or -1 factor
valAwayFix = valAway * directionX;
loSpeedX = (((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)
// ..............Courtesy of Stefan, Arduino Forum ................
if (abs(Analog_X - Analog_X_AVG) < speedShift) {
joyStickAtMax = false;
}
else {
joyStickAtMax = true;
}
if (lastjoyStickAtMax == false && joyStickAtMax == true) {
// only in case joystick-position has CHANGED RIGHT NOW
// from being not at max to being 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 changing to hiSpeedX
if (joyStickAtMax == true) {
if (millis() - highSpeedPosStarted >= hiSpeedDelay) {
// only if joystick has been CONTINIOUSLY in max position
// for more than hiSpeedDelay milliseconds do this:
motSpeedX = hiSpeedX * directionX * (-1);
}
else {
motSpeedX = loSpeedX;
}
}
else {
motSpeedX = loSpeedX;
}
// update lastjoyStickAtMax
lastjoyStickAtMax = joyStickAtMax;
// end Stefan code
stepper.setSpeed(motSpeedX) ;
stepper.runSpeed();
/*
if (abs(Analog_X - Analog_X_AVG) > speedShift)
{
j=++j;
if (j > speedCount);
{
motSpeedX = (maxSpeed_val / 2) * (motSpeedX / (abs(motSpeedX) ));
stepper.setSpeed(motSpeedX) ;
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 (" motSpeedX = ");
Serial.println (motSpeedX);
refMillis = currentMillis; //IMPORTANT to save the start time of the speed printout.
}
else
{
motSpeedX = 0;
printCount = 1; //reset for homeGo routine
}
} //---------------------------end Analog_X val check
//-----------------------------------------
//------------ Y Motor Speed -----
//-----------------------------------------
if (abs(Analog_Y - Analog_Y_AVG) > valAway)
{
directionY = abs(Analog_Y - Analog_Y_AVG) / (Analog_Y - Analog_Y_AVG);
valAwayFixY = valAway * directionY;
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 (" motSpeedY = ");
Serial.println (motSpeedY);
refMillis = currentMillis; //IMPORTANT to save the start time of the speed printout.
}
}
else //things to do if joystick is centered
{
motSpeedY = 0;
homeSet();
homeGo();
initializeButton_Val = digitalRead(initializeButton); //recalibrates joystick
if (initializeButton_Val == 0)
{
InitialValues();
}
}
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 (motSpeedX);
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() -----
//------------------------------------------
//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
void InitialValues()
{
blinkRed();
stepper.setSpeed(0);
stepperY.setSpeed(0);
motSpeedX = 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 * 2);
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(motSpeedX) ;
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(motSpeedY) ;
stepperY.runSpeed();
// myServo.write(servoCenter);
}
}
//-------------------------------------------
//----------- homeGo() ------------
//------------------------------------------
void homeGo()
{
homeGo_Val = digitalRead (homeGopin);
if (homeGo_Val == 0) // Go Home
{
blinkRed();
printCount = 1; //reset for homeGo routine
directionX = abs (stepper.currentPosition()) / stepper.currentPosition();
directionY = abs (stepperY.currentPosition()) / stepperY.currentPosition();
Serial.print (stepper.currentPosition());
Serial.print (" X < Going Home > Y ");
Serial.println (stepperY.currentPosition());
stepper.moveTo (0);
motSpeedX = (maxSpeed_val);
stepper.setSpeed(motSpeedX);
stepperY.moveTo (0);
motSpeedY = (maxSpeed_val);
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!");
motSpeedX = 0;
motSpeedY = 0;
// printCount = printCount + 1;
printCount++;
}
}
// End ============== Alternate Home Go Two from GX60==============
//----------------------------------------------------------------------------
// checkBoundary()
//----------------------------------------------------------------------------
void checkBoundary() //Protection from over rotation
{
//---------------- X Boundary
if (stepper.currentPosition() <= lowMax || stepper.currentPosition() >= highMax)
{
stepper.setSpeed(0);
Serial.print ("Over Rotated X ");
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();
// stepper.runToPosition();
// motSpeedX = 0;
}
}
if (stepper.currentPosition() >= highMax) // Positive Overrotation
{
safePos = (highMax - 100);
stepper.moveTo (safePos);
stepper.setSpeed(maxSpeed_val / 4);
// stepper.setMaxSpeed(maxSpeed_val / 2);
while (stepper.currentPosition() != stepper.targetPosition())
{
stepper.runSpeedToPosition();
// stepper.runToPosition();
// motSpeedX = 0;
}
}
//----------- Y Boundary
if (stepperY.currentPosition() <= lowMaxY || stepperY.currentPosition() >= highMaxY)
{
stepperY.setSpeed(0);
Serial.print ("Over Rotated Y ");
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();
motSpeedY = 0;
}
}
if (stepperY.currentPosition() >= highMaxY) // Positive Overrotation
{
safePos = (highMaxY - 100);
stepperY.moveTo (safePos);
stepperY.setSpeed(maxSpeed_val / 4);
while (stepperY.currentPosition() != stepperY.targetPosition())
{
stepperY.runSpeedToPosition();
motSpeedY = 0;
}
}
}
//----------------------------------------------------------------------------
// 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);
}
*/
}