0

I'm working on a camera slider mount with an X stepper (to move along slider), Y stepper (pan left right) and Z stepper (tilt up down). All of this is being controlled by a PS3 controller (PS3BT library).

I've implemented a feature in which all three steppers move from one saved position to other and then back again.. this keeps repeating infinitely. It works fine - except I can't stop it - it's more of a programming issue I think. Here's the code in question

// MultiStepper instance - at the top somewhere
MultiStepper StepperControl;

void loop(){
    if(PS3.getButtonClick(START)){ // START to start movement between two POS's.
        Serial.print("\n Starting Movement between POS1 and POS2");    
        while(!PS3.getButtonClick(CROSS)){        
          StepperControl.moveTo(POS1); // go to POS1
          StepperControl.runSpeedToPosition();
          delay(200);
          StepperControl.moveTo(POS2); // go to POS2
          StepperControl.runSpeedToPosition();
          delay(200);
        }
      }
}

My issue is that pressed X (cross) on the keypad, doesn't quit out of the loop. What I feel is happening is that the loop is running too fast and all the moveTo() commands are stacking on top of each other. And so, even if I do press X, too many moveTo() commands have already happened and it just keeps running those "queued" commands.

I've also tried putting an if statement inside the while loop that breaks out of the loop when CROSS is pressed - no luck.

// This isn't the entire code, just the parts I think are relevant.

1
  • you need to identify the problem correctly before you can arrive at a solution ..... the description of your problem has nothing to do with the AccelStepper Library - moveTo() ..... remove all the stepper code from your sketch (or start a new sketch) .... keep the PS3 code and use serial.print() to debug your code ..... determine which code you need, to correctly detect which buttons are being pressed
    – jsotola
    Commented Sep 8, 2018 at 1:48

1 Answer 1

1

The while(!PS3.getButtonClick(CROSS)) is only checked once per loop, so your system will complete a move to POS1, wait 200ms, complete a move to POS2, and wait another 200ms before it can check the controller for another button press.

runSpeedToPosition() is "blocking", which means NOTHING else can happen while it is doing it's task.

Instead, you probably want to avoid blocking functions and set up a finite state machine and use the run() function of AccelStepper, which you should call as often as you can and it will move the stepper one step toward the target position, if it's not yet at the target position.

The following rewrite is not tested, but the general structure should show you how you can change the way the program is written. You should think of the loop() function running and completing a million times a second, and each time through, you check the state (that is, on what step is the system currently on) and act accordingly. Change to a new state when appropriate.

In the example, there are 5 states, corresponding to all possible combinations of what your program could be doing:

  • STOPPED: the initial state, the stepper has no target
  • RUNTO1: the stepper is moving toward POS1, but hasn't reached it yet
  • WAIT1: the stepper has reached POS1 and is now waiting for a specified delay before moving to the next state. This state uses the process shown in the BlinkWithoutDelay standard Arduino example sketch for delaying without blocking.
  • RUNTO2: the stepper is moving toward POS2, but hasn't reached it yet
  • WAIT2: the stepper has reached POS2 and is now waiting for the next state

You can also see the situations where a state is changed:

  • When START is pressed, set a new target position and enter the RUNTO1 state

  • When CROSS is pressed, stop all stepper action and enter the STOPPED state

  • When in RUNTO1 or RUNTO2, and the distanceToGo() is zero, enter to the corresponding WAITn state

  • When in the WAITn state and the specified time has elapsed, enter the corresponding next RUNTOn state.

Example:

#define POS1 80
#define POS2 0

const uint16_t DELAY = 200;
enum stepperstates {STOPPED, RUNTO1, WAIT1, RUNTO2, WAIT2};

[...]

void loop() {
  static uint16_t last_millis;
  static stepperstates xStepState;
  if (PS3.getButtonClick(START)){
    xStepState = RUNTO1;
    StepperControl.moveTo(POS1); // go to POS1
    Serial.print("\n Starting Movement between POS1 and POS2");
  }
  if (PS3.getButtonClick(CROSS)) {
    xStepState = STOPPED;
  }

  StepperControl.run();

  switch (xStepState) {
    case STOPPED:
      StepperControl.stop();
      break;
    case RUNTO1:
      if (StepperControl.distanceToGo()==0) {
        xStepState = WAIT1;
        last_millis = millis();
      }
      break;
    case WAIT1:
      if (millis() - last_millis >= DELAY) {
        xStepState = RUNTO2;
        StepperControl.moveTo(POS2);
      }
      break;
    case RUNTO2:
      if (StepperControl.distanceToGo()==0) {
        xStepState = WAIT2;
        last_millis = millis();
      }
      break;
    case WAIT2:
      if (millis() - last_millis >= DELAY) {
        xStepState = RUNTO1;
        StepperControl.moveTo(POS1);
      }
      break;
  }
}
1
  • Ahh. That makes so much more sense. So basically with my original code - the user got almost no chance to even press X because delay() was blocking and so was the moveToPosition(). And now with this code - run() is not blocking and each time run() happens, we check if X was pressed and if so enter the STOP state. Commented Sep 8, 2018 at 15:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.