Implementing a "halt" function with AccelStepper library

Hi, I am using the Accel Stepper motor library to build a linear drive, with a matrix keypad to give input. I am trying to implement a stop function, where i press 'A' on the keypad, and the motor stops immediately, and displays where it stopped. But when i press it, it gives a pause, shows me the motor position at that point, and continues to spin for a while before it stops. It usually stops after a 1000 steps. Below is the code:

#include <AccelStepperWithDistance.h>

#include <Keypad.h>
//#include <AccelStepper.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display
AccelStepper stepperX(1, 12, 11);   // 1 = Easy Driver interface

const byte home_switch =  10;

// Stepper Travel Variables
long TravelX;  // Used to store the X value preovided by the user
bool moveComplete = true;

//keypad setup
const int ROW_NUM = 4; //four rows
const int COLUMN_NUM = 4; //four columns

char keys[ROW_NUM][COLUMN_NUM] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte pin_rows[ROW_NUM]      = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte pin_column[COLUMN_NUM] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), pin_rows, pin_column, ROW_NUM, COLUMN_NUM );

String keypadInput;

void lcdEnterValue() {
  lcd.clear();
  lcd.print(F("set value:"));
  lcd.setCursor(0, 1);
  lcd.print("now at ");
  lcd.print(stepperX.currentPosition());
   lcd.setCursor(10, 0);
  Serial.println(F("Enter Travel distance (Positive for CW / Negative for CCW and Zero for back to Home): "));
}


bool readKeypad(long& newDestination) {
  long userInput = 0;
  char key = keypad.getKey();
  if (key) {
    if (key >= '0' && key <= '9') {     // only act on numeric keys ==> HOW DO YOU ENTER NEGATIVE VALUES?
      keypadInput += key;               // append new character to input string
      lcd.print(key);

    } else if (key == '#') {
      if (keypadInput.length() > 0) {
        userInput = keypadInput.toInt();
        keypadInput = "";               // clear input
        }
    

        if (userInput < 0 || userInput > 69500) {  // Make sure the position entered is not beyond the HOME or MAX position
          Serial.println(F("\nPlease enter a value greater than zero and smaller or equal to 69500.....\n"));
          lcd.clear();
          lcd.print(F("invalid value."));
          delay(2000);
          lcdEnterValue();
          return false;
        } else {
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print(F("Moving to:"));
          lcd.print(userInput);
          Serial.print(F("Moving stepper into position: "));
          Serial.println(userInput);
          delay(1000);  // Wait 1 seconds before moving the Stepper
          newDestination = userInput;
          return true;
        }
    }
  else if (key == '*') {
      keypadInput = "";                 // clear input
      lcdEnterValue();
        }
  else if (key == 'A' && keypadInput=="") {
    stepperX.stop();
    lcd.clear();
    lcd.print("HALTED at ");
    lcd.print(stepperX.currentPosition());
    delay(1500);
    

      }
      
    
  }
  return false;
}

void homing() {
  //  Set Max Speed and Acceleration of each Steppers at startup for homing
  stepperX.setMaxSpeed(800.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperX.setAcceleration(800.0);  // Set Acceleration of Stepper

  // Start Homing procedure of Stepper Motor at startup
  lcd.setCursor(0, 0);
  lcd.print(F("HOMING"));
  Serial.print("Stepper is Homing");

  long initial_homing = -1; // Used to Home Stepper at startup

  while (digitalRead(home_switch) == HIGH) {
    stepperX.moveTo(initial_homing);  // Set the position to move to
    initial_homing--;  // Decrease by 1 for next move if needed
    stepperX.run();  // Start moving the stepper
    delay(1);
  }

  stepperX.setCurrentPosition(0);   // Set the current position as zero for now
  stepperX.setMaxSpeed(100.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperX.setAcceleration(100.0);  // Set Acceleration of Stepper
  initial_homing = 1;
  delay(15);                        // anti-bounce

  while (digitalRead(home_switch) == LOW) {
    stepperX.moveTo(initial_homing);
    initial_homing++;
    stepperX.run();
    delay(1);
  }

  stepperX.setCurrentPosition(0);

  lcd.clear();
  lcd.print(F("HOMING DONE!"));
  delay(2500);
  Serial.println(F("Homing OK\n"));

}

void setup() {
  pinMode(home_switch, INPUT_PULLUP);
  Serial.begin(115200);     // don't go slow.

  keypadInput.reserve(10);  // avoid dynamic allocation
  keypadInput == "";        // empty the String

  lcd.init();               // initialize the lcd
  lcd.clear();
  lcd.backlight();

  homing();

  stepperX.setMaxSpeed(1000.0);      // Set Max Speed of Stepper (Faster for regular movements)
  stepperX.setAcceleration(500.0);  // Set Acceleration of Stepper

  // Print out Instructions on the Serial Monitor at Start
  lcdEnterValue();
}

void loop() {

  if (readKeypad(TravelX)) {
    stepperX.moveTo(TravelX);  // Set new moveto position of Stepper
    moveComplete = false;
  }

  // Check if the Stepper has reached desired position
  if ((stepperX.distanceToGo() != 0)) {
    stepperX.runSpeed();  // Move Stepper if needed
    stepperX.setSpeed(1000);
  } else {
    if (!moveComplete) {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("COMPLETED!");
      delay(2500);
      Serial.println("COMPLETED!");
      lcdEnterValue();
      moveComplete = true;
    }
  }
}

I know that it stops immediately till the delay i added for the display lasts, then rotates for a while before it stops. I want it to stop exactly where it does the first time.

Implement the delay using millis() instead of delay().

I'm not an expert on the stepper library but I'll try...
Everyware a command is used to move the stepper the emergency switch must be checked. I can't tell how to abort an ongoing stepperX.moveTo.
One way is to have a mains breaker....

I think there is a stop() method in that library... but the run() method is abused in the code above. It's supposed to be called every time through loop(), no matter what. There, it's nested in a while loop. While that is going on, the keypad is ignored.

So then, possibly two problems.

  1. the run() method is not always called in loop
  2. keypad polling is done in loop() so if any delay() calls are in loop(), the keypad will go dead for periods of time.

That's not the best way to use AccelStepper. You need to call stepperX.run() very frequently all the time - no calls to delay(). When you call stop() it decelerates the motor to a stop at the standard ramp-up/ramp-down rate (so long as run() is being called frequently), so that there are no miss-steps. If you tune the parameter to setAcceleration() properly this should be fine.

Basically AccelStepper should be fire and forget - calls like moveTo, stop just schedule the motor trajectory, and the run() method does the work of outputing steps as and when required.