Stepper Go Home routine works until it doesn't

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);
    }
  */
}

Correction/Addendum:
If the camera hits the X boundary in the positive direction, the Go Home routine is anomalous regardless of what you do with the joystick. The joystick only corrects the Go Home problem if it is used after the X boundary in the negative direction.

rcarbaugh

I see you are using serial.Print() lots of times. What have you tried in order to track down the problem?

try

#include <AccelStepper.h> //accelstepperX library
AccelStepper stepperX(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 <Servo.h>// include the Servo library
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
const byte redPin = 4; //Red LED pin
const byte potPin = A2; // analog pin used to connect the potentiometer for the servo
const byte servoPin = 12; // Output servoAngle to servo
const byte initializeButton = 7; //Button 3
const byte enPin = 5;
const byte enPinY = 6;
const byte stepPin = 8; // Pan Direction
const byte dirPin = 9; // Step
const byte stepPinY = 10;  //Tilt direction
const byte dirPinY = 11;  //Tilt step


//Motor data
const int accelRate = 1500;  //mostly for Go Home routine (higher is faster)
int motSpeedX = 0;
int motSpeedY = 0;
const 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 AwayFix_X = 0;
int AwayFix_Y = 0;
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 = 0;
int xSpeedFactor = 2;
int ySpeedFactor = 4;
int8_t directionX = 1;
int8_t directionY = 1;

//  Boundary Limits
const long ToHigh_X = 1500;
const long ToLow_X = -2000;
const long ToHigh_Y = 700;
const long ToLow_Y = -700;
int safePos = 0;

//Variables
int Analog_X = 0; //x-axis value
int Analog_Y = 0; //y-axis 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 = 0;  // variable to hold the servoAngle for the servo motor
const int servoCenter = 90; //Servo at rest
bool joyStickAtMax = false;
bool lastjoyStickAtMax = false;

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


void setup() {
  digitalWrite(enPin, HIGH); // disable motor
  digitalWrite(enPinY, HIGH); // disable motor
  Serial.begin(115200);
  Serial.println ("Begin Sketch");
  //----------------------------------------------------------------------------
  //PINS
  pinMode(Analog_X_pin, INPUT);
  pinMode(Analog_Y_pin, INPUT);
  pinMode(potPin, INPUT);
  pinMode(initializeButton, INPUT_PULLUP);
  pinMode(homeSetpin, INPUT_PULLUP);
  pinMode(homeGopin, INPUT_PULLUP);
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPinY, OUTPUT);
  pinMode(stepPinY, OUTPUT);
  pinMode(enPin, OUTPUT);
  pinMode(enPinY, OUTPUT);
  pinMode(redPin, OUTPUT);
  digitalWrite(redPin, LOW);  // turn off redLED
  myServo.attach(servoPin); // 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
  stepperX.setCurrentPosition(0);
  stepperX.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
  stepperX.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
  stepperX.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!");
  homeSet();
}

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

void loop() {
  if (!digitalRead(initializeButton))InitialValues();
  if (!digitalRead(homeSetpin))homeSet();
  if (!digitalRead (homeGopin))homeGo();
  Analog_X = analogRead(Analog_X_pin);
  Analog_Y = analogRead(Analog_Y_pin);
  checkBoundary();
  servoCheck();

  if (abs(Analog_X - Analog_X_AVG) > valAway)  {  //------------       X Motor Speed    -----
    directionX = abs(Analog_X - Analog_X_AVG) / (Analog_X - Analog_X_AVG);  //yields +1 or -1 factor
    AwayFix_X = valAway * directionX;
    loSpeedX = ((Analog_X - Analog_X_AVG - AwayFix_X) * xMotorState) / xSpeedFactor;  //Set left/right

    // 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
    joyStickAtMax = abs(Analog_X - Analog_X_AVG) > speedShift;
    if (joyStickAtMax) {
      if (!lastjoyStickAtMax) {
        // only in case joystick-position has CHANGED  RIGHT NOW
        // from being not at max to being at max
        highSpeedPosStarted = millis(); // store snapshot of time
      }
      if (millis() - highSpeedPosStarted >= hiSpeedDelay) {
        // only if joystick has been CONTINIOUSLY in max position
        // for more than hiSpeedDelay milliseconds do this:
        motSpeedX = -hiSpeedX * directionX;
      } else motSpeedX = loSpeedX;
    } else motSpeedX = loSpeedX;
    lastjoyStickAtMax = joyStickAtMax;
    stepperX.setSpeed(motSpeedX) ;
    stepperX.runSpeed();
  } else     motSpeedX = 0;

  if (abs(Analog_Y - Analog_Y_AVG) > valAway) {//------------       Y Motor Speed    -----
    directionY = abs(Analog_Y - Analog_Y_AVG) / (Analog_Y - Analog_Y_AVG);
    AwayFix_Y = valAway * directionY;
    motSpeedY = ((Analog_Y - Analog_Y_AVG - AwayFix_Y) * yMotorState) / ySpeedFactor; //determines flightStick or gamerStick
    stepperY.setSpeed(motSpeedY);
    stepperY.runSpeed();
  }  else motSpeedY = 0;  //things to do if joystick is centered
}  // end void loop


void InitialValues() {
  blinkRed();
  motSpeedX = 0;
  motSpeedY = 0;
  stepperX.setSpeed(motSpeedX);
  stepperY.setSpeed(motSpeedY);

  uint16_t tempX = 0;
  uint16_t tempY = 0;
  uint16_t tempS = 0;

  for (uint8_t i = 0; i < 50; i++)  {
    tempX += analogRead(Analog_X_pin);
    tempY += analogRead(Analog_Y_pin);
    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() {  //set home location
  blinkRed();
  stepperX.setCurrentPosition(0);  // Set the current position as zero for now
  Serial.print("set Home - X");
  stepperY.setCurrentPosition(0);  // Set the current position as zero for now
  Serial.print("set Home - Y");
  blinkRed();
}

//-------------------------------------------
//-----------     homeGo()    ------------
//------------------------------------------
void homeGo() {
  // Go Home
  blinkRed();
  directionX = abs (stepper.currentPosition()) / stepper.currentPosition();
  directionY = abs (stepperY.currentPosition()) / stepperY.currentPosition();

  Serial.print (stepperX.currentPosition());
  Serial.print (" X < Going Home > Y ");
  Serial.println (stepperY.currentPosition());

  digitalWrite(enPin, LOW); // enable X motor
  digitalWrite(enPinY, LOW); // enable Y motor
  stepperX.moveTo (0);
  motSpeedX = (maxSpeed_val);
  stepperX.setSpeed(motSpeedX);

  stepperY.moveTo (0);
  motSpeedY = (maxSpeed_val);
  stepperY.setSpeed(motSpeedY);

  while (stepperX.currentPosition() != stepperX.targetPosition())stepperX.runToPosition();
  while (stepperY.currentPosition() != stepperY.targetPosition())stepperY.runToPosition();


  // If move is completed display message on Serial Monitor
  if (stepperX.distanceToGo() == 0)  {
    Serial.println("Now Home!");
    motSpeedX = 0;
    motSpeedY = 0;
  }
  digitalWrite(enPin, HIGH); // disable motor
  digitalWrite(enPinY, HIGH); // disable motor
}

void checkBoundary() {     //Protection from over rotation

  //---------------- X Boundary
  if (stepperX.currentPosition() <= ToLow_X) { // Negative Overrotation
    Serial.print ("Over Rotated X ");
    Serial.println (stepperX.currentPosition());
    safePos = (ToLow_X + 100);
    stepperX.moveTo (safePos);
    stepperX.setSpeed(maxSpeed_val / 4);
    while (stepperX.currentPosition() != stepperX.targetPosition())stepperX.runSpeedToPosition();
  }
  if (stepperX.currentPosition() >= ToHigh_X) { // Positive Overrotation
    Serial.print ("Over Rotated X ");
    Serial.println (stepperX.currentPosition());
    safePos = (ToHigh_X - 100);
    stepperX.moveTo (safePos);
    stepperX.setSpeed(maxSpeed_val / 4);
    while (stepperX.currentPosition() != stepperX.targetPosition())stepperX.runSpeedToPosition();
  }

  //-----------  Y Boundary
  if (stepperY.currentPosition() <= ToLow_Y) { // Negative Overrotation
    Serial.print ("Over Rotated Y ");
    Serial.println (stepperY.currentPosition());
    safePos = (ToLow_Y + 100);
    stepperY.moveTo (safePos);
    stepperY.setSpeed(maxSpeed_val / 4);
    while (stepperY.currentPosition() != stepperY.targetPosition())stepperY.runSpeedToPosition();
  }
  if (stepperY.currentPosition() >= ToHigh_Y) { // Positive Overrotation
    Serial.print ("Over Rotated Y ");
    Serial.println (stepperY.currentPosition());
    safePos = (ToHigh_Y - 100);
    stepperY.moveTo (safePos);
    stepperY.setSpeed(maxSpeed_val / 4);
    while (stepperY.currentPosition() != stepperY.targetPosition())stepperY.runSpeedToPosition();
  }
}

void servoCheck() {
  potVal = analogRead(potPin); // read the value of the potentiometer
  servoAngle = map(potVal, 0, 1023, (servoCenter - servoRange), (servoCenter + servoRange));
  if (abs(potVal - Analog_S_AVG) > valAwayServo)myServo.write(servoAngle);
  else myServo.write(servoCenter);
}

@kolaha

what did you change in the code that you say

(this version)??

Thank you for an option, @kolaha.

@StefanL38, it looks like @kolaha put code at the end of the Go Home routine to disable the motors once they arrived Home. That is problematic, because once the motors are disabled the cameras are free to rotate wherever their cables pull them and they are disabled until one resets Home. Part of the Go Home purpose is to have a shot preset.

rcarbaugh

Hi @Paul_KD7HB:
Thanks for chiming in. I put serial.Print() in between every line of the Go Home routine to trouble shoot. I cleaned it out before I sent the code to the forum.

The Go Home routine sent each print line out to the serial monitor under normal circumstances. After the boundary is hit, however, the code seems to execute every line until be hung right after the
stepperY.setSpeed(motSpeedY);
And before the

All input is blocked until it actually finishes its hiatus and arrives Home.

On further scrutiny, @kolaha also re-enabled the motors at the beginning the Go Home routine. Does that “clear out” any previous commands that the Boundary routine might have added to the motors?

Again, the disable command at the end of the Go Home routine is not an option.

He also has an interesting syntax of the while command. IDK if that is correct, it seems to be missing at least a comma, but I’m pretty new at this:

Thanks all,
rcarbaugh

it seems i have not fully understand the handling of this device. You place it on scenic view spot, power on(camera not aligned) , per joysticks turn to some position and press "setHome" button, then per joysticks turn to other direction/position, start camera, press "goHome" button, wait the camera drive to Home, stop camera. or what?

Hi @kolaha:

Since the pandemic, our NYC church does live stream services. We run four camcorders and an iPhone, all on remote motors and vertical, spring-loaded photography pipes for minimal line-of-sight obstruction. This particular camera is parked to the left of the stage (between the stage and the left wall) and typically does a side shot of the stage, but occasionally swings nearly 90° to the right to shoot the audience. "Home" is set on the side-of-stage shot for two reasons: so the boundary routine will keep the camera from going past 135° in either direction and just shooting the wall, and so that when the "Go Home" routine is called, the camera returns to a useable shot. There is a zoom control on a servo, but the two main uses for that particular camera are full wide.

So, when we arrive to church, the entire sound, video and computer systems are powered up. This energizes the four Arduino systems, too. To save heat on the stepper motors, they are NOT energized until some time later when the "Home" button is pushed. That not only sets the current position to zero, it also then enables the cameras' motors. Then, the operator joysticks the cameras to a common, usable shot and presses Home again to zero that position so that Go Home has a meaningful destination, and the boundaries fall where they are intended to be (since the boundaries are referenced to zero/home).

Here are pictures of the setup. I have changed the little mushroom cap joysticks to better joysticks since these pics, but the zoom servo is still on the mushroom cap.

In the wide shot below, the camera in question is on the pole near the American flag.

Hope that explanation helps.

Thanks.





The construction is big enough to place two endstops(one on each axis) and after powering on every axis will run and hit them and set its null (or going to the stored amount of steps and this will be the Home). This position (intern known as amount of steps from endstop corner) can be overwritten by pressing SetHome. I am sure the program can be written much shorter.

yep, check this out

//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
const byte redPin = 4; //Red LED pin
const byte potPin = A2; // analog pin used to connect the potentiometer for the servo
const byte servoPin = 12; // Output servoAngle to servo
const byte initButPin = 7; //Button 3
const byte enPin = 5;
const byte enPinY = 6;
const byte stepPin = 8; // Pan Direction
const byte dirPin = 9; // Step
const byte stepPinY = 10;  //Tilt direction
const byte dirPinY = 11;  //Tilt step

#include <AccelStepper.h>
AccelStepper stepperX(1, stepPin, dirPin);     // STEP pulses D8, direction D9
AccelStepper stepperY(1, stepPinY, dirPinY);   // STEP pulses D10, direction D11

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

//Motor&Servo data
const int accelRate = 1500;  //mostly for Go Home routine (higher is faster)
const int valAway = 100;  //  Value away from center before Joystick works
const int valAwayServo = 15; // Value away from center before servo moves
const int servoRange = 40; // Max deviation of servo from 90
const int maxSpeed_val = 950;
const byte xSpeedFactor = -2; //here mult and inversion
const byte ySpeedFactor = 4;

//  Boundary Limits
const long ToHigh_X = 1500;
const long ToLow_X = -2000;
const long ToHigh_Y = 700;
const long ToLow_Y = -700;

//Variables
int Analog_X = 0; //x-axis value
int Analog_Y = 0; //y-axis value
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 = 0; // variable to read the value from the analog pin
int servoAngle = 0;  // variable to hold the servoAngle for the servo motor
const int servoCenter = 90; //Servo at rest

//Time variables
unsigned long currentMillis = 0;
const unsigned long period = 1000; //the value is a number of milliseconds, ie 1 second
int dt = 500;
int ydt = 100; //ybutton delay

void setup() {
  digitalWrite(enPin, HIGH); // disable motor
  digitalWrite(enPinY, HIGH); // disable motor
  Serial.begin(115200);
  Serial.println ("Begin Sketch");
  pinMode(Analog_X_pin, INPUT);
  pinMode(Analog_Y_pin, INPUT);
  pinMode(potPin, INPUT);
  pinMode(initButPin, INPUT_PULLUP);
  pinMode(homeSetpin, INPUT_PULLUP);
  pinMode(homeGopin, INPUT_PULLUP);
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPinY, OUTPUT);
  pinMode(stepPinY, OUTPUT);
  pinMode(enPin, OUTPUT);
  pinMode(enPinY, OUTPUT);
  pinMode(redPin, OUTPUT);
  digitalWrite(redPin, LOW);  // turn off redLED
  myServo.attach(servoPin); // attaches the servo on pin 12 to the servo object

  //Stepper parameters  //setting up some default values for maximum speed and maximum acceleration
  stepperX.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
  stepperX.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2
  stepperY.setMaxSpeed(maxSpeed_val); //SPEED = Steps / second
  stepperY.setAcceleration(accelRate); //ACCELERATION = Steps /(second)^2

  InitialValues(); //averaging the values of the 3 analog pins (values from potmeters)
  Serial.println ("Let's GO!");
}

void loop() {//-----------------------------------------//------------       Void Loop        -----
  if (!digitalRead(initButPin))InitialValues();
  if (!digitalRead(homeSetpin))homeSet();
  if (!digitalRead (homeGopin))homeGo();
  Analog_X = analogRead(Analog_X_pin) - Analog_X_AVG;
  Analog_Y = analogRead(Analog_Y_pin) - Analog_Y_AVG;

  if (abs(Analog_X) > valAway) {
    stepperX.setSpeed(Analog_X  / xSpeedFactor) ;  //Set left/right
    if (stepperY.currentPosition() < ToHigh_X && stepperY.currentPosition() > ToLow_X)stepperX.runSpeed();
    else {
      Serial.print ("Over Rotated X ");
      Serial.println (stepperX.currentPosition());
    }
  }

  if (abs(Analog_Y) > valAway) {
    stepperY.setSpeed(Analog_Y / ySpeedFactor);
    if (stepperY.currentPosition() < ToHigh_Y && stepperY.currentPosition() > ToLow_Y)stepperY.runSpeed();
    else {
      Serial.print ("Over Rotated Y ");
      Serial.println (stepperY.currentPosition());
    }
  }

  servoCheck();
}  // end void loop


void InitialValues() {
  blinkRed();
  uint16_t tempX = 0;
  uint16_t tempY = 0;
  uint16_t tempS = 0;

  for (uint8_t i = 0; i < 20; i++)  {
    tempX += analogRead(Analog_X_pin);
    tempY += analogRead(Analog_Y_pin);
    tempS += analogRead(potPin);
    delay(10);
  }
  Analog_X_AVG = tempX / 20;
  Analog_Y_AVG = tempY / 20;
  Analog_S_AVG = tempS / 20;

  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();
}

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

void homeSet() {        //set home location                           //-----------     homeSet()    ------------
  blinkRed();
  digitalWrite(enPin, LOW); // enable X motor
  digitalWrite(enPinY, LOW); // enable Y motor
  stepperX.setCurrentPosition(0);
  stepperY.setCurrentPosition(0);
  Serial.println("set Home");
  blinkRed();
}

void homeGo() {//-------------------------------------------//-----------     homeGo()    ------------
  blinkRed();
  Serial.print (stepperX.currentPosition());
  Serial.print (" X < Going Home > Y ");
  Serial.println (stepperY.currentPosition());

  stepperX.moveTo(0);
  stepperX.setSpeed(maxSpeed_val);
  stepperY.moveTo(0);
  stepperY.setSpeed(maxSpeed_val);

  while (stepperX.currentPosition() != stepperX.targetPosition())stepperX.runToPosition();
  while (stepperY.currentPosition() != stepperY.targetPosition())stepperY.runToPosition();

  Serial.println("Now Home!");  // move is completed display message on Serial Monitor
}

void servoCheck() {
  potVal = analogRead(potPin); // read the value of the potentiometer
  servoAngle = map(potVal, 0, 1023, (servoCenter - servoRange), (servoCenter + servoRange));
  if (abs(potVal - Analog_S_AVG) > valAwayServo)myServo.write(servoAngle);
  else myServo.write(servoCenter);
}

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