Running steppers multiple times

45 views
Skip to first unread message

Adrian Brown

unread,
Aug 25, 2025, 1:57:59 PM (13 days ago) Aug 25
to accelstepper
Greetings all!

New to all of this, so please bear with.

I am attempting to run three different nema17 steppers with accelStepper. My intention is to have the motors run to a specific distance (or perhaps for a specific duration?) when triggered.

But I want to be able to trigger them several times.

Currently, I am setting a default distance, and calling stepperA.run(); with each loop.

My questions is: How do I make the stepper run a second time? Do I need to call stepperA.setCurrentPosition(0); first? I suppose I could call stepperA.currentPosition() to determine when the stepper is finished, and then set that position to 0.

But I have also written an ISR to stop the motors in respone to a kill switch. How would I set that position to '0'? Should I just check until the speed is '0' and then make that position '0'?

I'm sure I sound crzy, but I am wondering if there is some well understood method for doing what I'm asking that I am unaware of.

Thanks in advance!

Adrian Brown

unread,
Aug 25, 2025, 2:14:00 PM (13 days ago) Aug 25
to accelstepper
Okay, I just read that using the stepper run as a condition will return true until the stepper has reached it's destination. In that case, would the following function run my steppers to their destination and then set that destination as zero for them to be triggered again?

void runMotors(){
  if(stepperAGo){
    stepperA.run();
    if (!stepperA.run()){
      stepperA.setCurrentPosition(0);
    }
  }
  if(stepperBGo){
    stepperB.run();
    if (!stepperB.run()){
      stepperB.setCurrentPosition(0);
    }
  }
  if(stepperCGo){
    stepperC.run();
    if (!stepperC.run()){
      stepperC.setCurrentPosition(0);
    }
  }

}

I suppose my next question would be: when using the killswitch, how do I make that not use blocking because I want to affect up to three motors at once.

Jim Larson

unread,
Aug 25, 2025, 5:50:16 PM (13 days ago) Aug 25
to accelstepper
Hi Adrian !
Welcome to the forum.

Could you please post the rest of your code so we can see how you are configuring your motors? I think your problem is interesting and that the solution is fairly straight-forward. I'll work on it when I can see your code.

         -jim

Adrian Brown

unread,
Aug 25, 2025, 8:04:35 PM (13 days ago) Aug 25
to accels...@googlegroups.com
Very, VERY much appreciated. I am fumbling through a poorly lit cave here, so I really can't thank you enough for your help.

I am not currently at work, but first thin in the morning I will post the code.

It is...lengthy, lol. Essentially I am building a case I can take on jobs that runs three solenoid valves and three steppers. I need both to be triggerable by buttons and an incoming signal from a robot (camera robot).

The next step will be to introduce rotary encoders and LED screens to make parameter changes to the steppers on the fly. That comes later, but wanted to explain why there will be commented out variables for the rotaries in the code.

--
You received this message because you are subscribed to a topic in the Google Groups "accelstepper" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/accelstepper/77mjQVpJ77Q/unsubscribe.
To unsubscribe from this group and all its topics, send an email to accelstepper...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/accelstepper/af44a89e-7674-4a18-a020-d50f27f6de79n%40googlegroups.com.
Message has been deleted
Message has been deleted

Adrian Brown

unread,
Aug 26, 2025, 9:42:02 AM (12 days ago) Aug 26
to accels...@googlegroups.com
I attempted to post my code, but it appears the message was deleted both times. Am I doing something incorrectly?

Jim Larson

unread,
Aug 26, 2025, 1:53:32 PM (12 days ago) Aug 26
to accelstepper
Try adding it as an attachment. Maybe it's too big?

             -jim
Message has been deleted
Message has been deleted

Adrian Brown

unread,
Aug 26, 2025, 4:33:44 PM (12 days ago) Aug 26
to accelstepper
I am confident you are correct. 
With comments, it is already 693 lines long. With more to  come. I am attaching it as a PDF.
Stepper_Solenoid Code.pdf

Adrian Brown

unread,
Aug 26, 2025, 4:33:44 PM (12 days ago) Aug 26
to accelstepper
#include <AccelStepper.h>
#include <Bounce2.h>


//INPUT to read Flair signal on DMX
const byte dmxPinAll = X;
const byte dmxPin00 = X;
const byte dmxPin01 = X;
const byte dmxPin02 = X;
const byte dmxPin03 = X;
const byte dmxPin04 = X;
const byte dmxPin05 = X;

//OUTPUT to MOSFETS to trigger solenoids
//const byte solenoidAPin = X;
//const byte solenoidBPin = X;
//const byte solenoidCPin = X;
//const byte solenoidDPin = X;
//const byte solenoidEPin = X;
//const byte solenoidFPin = X;

//INPUT to read button input to trigger Solenoids
const byte solenoidAButton = X;
const byte solenoidBButton = X;
const byte solenoidCButton = X;
//const byte solenoidDButton = 10;
//const byte solenoidEButton = 11;
//const byte solenoidFButton = 12;

//Stepper controls (A) with a switch variable
//for potential future on/off switch
const byte stepperAStepPin = x;
const byte stepperADirPin = x;
const byte stepperASwitchPin = x;

//Stepper controls (B) with a switch variable
//for potential future on/off switch
const byte stepperBStepPin = x;
const byte stepperBDirPin = x;
const byte stepperBSwitchPin = x;

//Stepper controls (C) with a switch variable
//for potential future on/off switch
const byte stepperCStepPin = x;
const byte stepperCDirPin = x;
const byte stepperCSwitchPin = x;

//Variable to hold the current state of the
//Flair signal. Default to 2 to prevent accidental
//behavior at startup because 0 is 'ON'
const byte dmxAllState = 2;
const byte dmx00State = 2;
const byte dmx01State = 2;
const byte dmx02State = 2;
const byte dmx03State = 2;
const byte dmx04State = 2;
const byte dmx05State = 2;

//Variable to determine if stepper motor should be active
const bool stepperAGo = FALSE;
const bool stepperBGo = FALSE;
const bool stepperCGo = FALSE;

//variable to determine if stepper motor received
//killswitch command
volatile bool KILLSwitchPressed = FALSE;
volatile bool stepperAStop = FALSE;
volatile bool stepperBStop = FALSE;
volatile bool stepperCStop = FALSE;

//Variable to hold the millis() at time of
//a solenoid button being pressed
uint32_t solenoidATimer = 0;
uint32_t solenoidBTimer = 0;
uint32_t solenoidCTimer = 0;
//uint32_t solenoidDTimer = 0;
//uint32_t solenoidETimer = 0;
//uint32_t solenoidFTimer = 0;

//variable to hold the designated time in milliseconds
//that the solenoids should be active
const int solenoidDuration = 500;

//variable to hold the designated time in milliseconds
//before any solenoid can be triggered consecutively
const int solenoidCoolOff = 2000;

//Current time global variable, called at beginning of each loop
//to be available to any function
int32_t currentTime = 0;

//Instantiate bounce class objects for each button
//that will require bouncing
Bounce2::Button bounceSolenoidA = Bounce2::Button();
Bounce2::Button bounceSolenoidB = Bounce2::Button();
Bounce2::Button bounceSolenoidC = Bounce2::Button();
//Bounce2::Button bounceSolenoidD = Bounce2::Button();
//Bounce2::Button bounceSolenoidE = Bounce2::Button();
//Bounce2::Button bounceSolenoidF = Bounce2::Button();

//Instantiate bounce class objects for each rotary buttone
//that will require bouncing
Bounce2::Button bounceRotSwitchA = Bounce2::Button();
Bounce2::Button bounceRotSwitchB = Bounce2::Button();
Bounce2::Button bounceRotSwitchC = Bounce2::Button();

//Instantiations of AccelStepper function
//one for each motor
AccelStepper stepperA(AccelStepper::DRIVER , stepperAStepPin, stepperADirPin);
AccelStepper stepperB(AccelStepper::DRIVER , stepperBStepPin, stepperBDirPin);
AccelStepper stepperC(AccelStepper::DRIVER , stepperCStepPin, stepperCDirPin);


void setup() {
  // put your setup code here, to run once:

  //set killswitch pin to HIGH
  pinmode(KILLSwitch, INPUT_PULLUP);

  //set killswitch pin as an interrupt
  //that triggers on low and calls the ISR
  attachInterrupt(digitalPinToInterrupt(KILLSwitch),KILLSwitchISR, LOW);
 
  //set stepper default parameters
  stepperA.setMaxSpeed(200.0);
  stepperA.setAcceleration(100.0);
  stepperA.moveTo(1000);
   
  stepperB.setMaxSpeed(200.0);
  stepperB.setAcceleration(100.0);
  stepperB.moveTo(1000);
   
  stepperC.setMaxSpeed(200.0);
  stepperC.setAcceleration(100.0);
  stepperC.moveTo(1000);

//Attach bounce instance to the solenoid INPUT pin
//decide if button will go to ground (INPUT_PULLUP) or not (INPUT)
  bounceSolenoidA.attach(solenoidAButton, INPUT_PULLUP);
  bounceSolenoidB.attach(solenoidBButton, INPUT_PULLUP);
  bounceSolenoidC.attach(solenoidCButton, INPUT_PULLUP);
  bounceSolenoidA.attach(solenoidDButton, INPUT_PULLUP);
  bounceSolenoidB.attach(solenoidEButton, INPUT_PULLUP);
  bounceSolenoidC.attach(solenoidFButton, INPUT_PULLUP);

//Attach bounce instance to the rotary encoder button INPUT pin
  bounceRotSwitchA.attach(stepperASwitchPin, INPUT_PULLUP);
  bounceRotSwitchB.attach(stepperBSwitchPin, INPUT_PULLUP);
  bounceRotSwitchC.attach(stepperCSwitchPin, INPUT_PULLUP);

//Set debounce interval per pin
  //solenoid buttons
  bounceSolenoidA.interval(25);
  bounceSolenoidB.interval(25);
  bounceSolenoidC.interval(25);
  //stepper buttons
  bounceStepperA.interval(25);
  bounceStepperB.interval(25);
  bounceStepperC.interval(25);
  //rotaryencoder buttons
  bounceRotSwitchA.interval(25);
  bounceRotSwitchB.interval(25);
  bounceRotSwitchC.interval(25);

//Set the pressed state for each button pin
//LOW for 'INPUT_PULLUP' or HIGH for 'INPUT'
  //solenoid buttons
  bounceSolenoidA.setPressedState(LOW);
  bounceSolenoidB.setPressedState(LOW);
  bounceSolenoidC.setPressedState(LOW);
  //stepper buttons
  bounceStepperA.setPressedState(LOW);
  bounceStepperB.setPressedState(LOW);
  bounceStepperC.setPressedState(LOW);
  //rotary encoder button pin
  bounceRotSwitchA.setPressedState(LOW);
  bounceRotSwitchB.setPressedState(LOW);
  bounceRotSwitchC.setPressedState(LOW);

//The DMX signal will connect the
//DMX pin 1 to pin 2, which is ground
//Therefore, to read a signal, it has to dump to '0'
//Designate solenoid dmx input pins
  pinMode(dmxPinAll, INPUT_PULLUP);
  pinMode(dmxPin00, INPUT_PULLUP);
  pinMode(dmxPin01, INPUT_PULLUP);
  pinMode(dmxPin02, INPUT_PULLUP);
  pinMode(dmxPin03, INPUT_PULLUP);
  pinMode(dmxPin04, INPUT_PULLUP);
  pinMode(dmxPin05, INPUT_PULLUP);

//Designating solenoid trigger pins
  pinMode(solenoidAPin, OUTPUT);
  pinMode(solenoidBPin, OUTPUT);
  pinMode(solenoidCPin, OUTPUT);
//pinMode(solenoidDPin, OUTPUT);
//pinMode(solenoidEPin, OUTPUT);
//pinMode(solenoidFPin, OUTPUT);

//Designating solenoid button input pins
  pinMode(solenoidAButton, INPUT_PULLUP);
  pinMode(solenoidBButton, INPUT_PULLUP);
  pinMode(solenoidCButton, INPUT_PULLUP);
//pinMode(solenoidDButton, INPUT_PULLUP);
//pinMode(solenoidEButton, INPUT_PULLUP);
//pinMode(solenoidFButton, INPUT_PULLUP);

//Designating pins per stepper
  pinMode(stepperADirPin, OUTPUT);
  pinMode(stepperAStepPin, OUTPUT);
  pinMode(stepperASwitchPin , INPUT);

  pinMode(stepperBDirPin , OUTPUT);
  pinMode(stepperBStepPin , OUTPUT);
  pinMode(stepperBSwitchPin, INPUT);

  pinMode(stepperCDirPin , OUTPUT);
  pinMode(stepperCStepPin , OUTPUT);
  pinMode(stepperCSwitchPin, INPUT);

}

void loop() {
  // put your main code here, to run repeatedly:
  currentTime = millis();

  //always check if killswitch was activated
  //if it was, run the function to stop the steppers
  if (KILLSwitchPressed){
    //reset the killswitch
    KILLSwitchPressed = FALSE;
    fullStop();
  }
  readDMX ();
  //readRotary (); //this function will be fleshed out later
  readButtons ();
  actOnButtons ();
  runmotors();
  checkTimer ();
 
 

}

//ISR that sets the killswitch to activate
//loop constantly checks if 'Pressed' is TRUE
void KILLSwitchISR(){
  KILLSwitchPressed = TRUE;
  stepperAGo = FALSE;
  stepperBGo = FALSE;
  stepperCGo = FALSE;
  stepperA.stop();
  stepperB.stop();
  stepperC.stop();

}

//This will just be polling for a high signal coming in from a single
//lead from the robot. It will probably only ever call the
//solenoid trigger functions, though it may be futureproofing
//to set it up to also call the steppers. That would require there
//being default parameters within the stepper functions or, perhaps
//more simply, having functions that set the paramters separately,
//then wait for the stepper to be called. More research needed.
void readDMX () {
  dmxAllState = digitalRead(dmxPinAll);
  if (robotTriggerState == 1){} //while pin reads HIGH, nothing is happening, so do nothing...for now
  if (robotTriggerState == 0){
    //write code to trigger all solenoids
    solenoidAGo();
    solenoidBGo();
    solenoidCGo();
    stepperAGo = TRUE;
    stepperBGo = TRUE;
    stepperCGo = TRUE;
  }
  dmx00State = digitalRead(dmxPin00);
  if (robotTriggerState == 1){} //while pin reads HIGH, nothing is happening, so do nothing...for now
  if (robotTriggerState == 0){
    solenoidAGo();
  }
  dmx01State = digitalRead(dmxPin01);
  if (robotTriggerState == 1){} //while pin reads HIGH, nothing is happening, so do nothing...for now
  if (robotTriggerState == 0){
    solenoidBGo();
  }
  dmx02State = digitalRead(dmxPin02);
  if (robotTriggerState == 1){} //while pin reads HIGH, nothing is happening, so do nothing...for now
  if (robotTriggerState == 0){
    solenoidCGo();
  }
  dmx03State = digitalRead(dmxPin03);
  if (robotTriggerState == 1){} //while pin reads HIGH, nothing is happening, so do nothing...for now
  if (robotTriggerState == 0){
    stepperAGo = TRUE;
  }
  dmx04State = digitalRead(dmxPin04);
  if (robotTriggerState == 1){} //while pin reads HIGH, nothing is happening, so do nothing...for now
  if (robotTriggerState == 0){
    stepperBGo = TRUE;
  }
  dmx05State = digitalRead(dmxPin05);
  if (robotTriggerState == 1){} //while pin reads HIGH, nothing is happening, so do nothing...for now
  if (robotTriggerState == 0){
    stepperCGo = TRUE;
  }
}

//This will have to read the encoder, but put the data into different
//variables based on a selection. The button on the encoder can likely be used
//to switch between parameters -thus calling separate functions?
//void readRotary (){}

//Need to poll all buttons with a debounce
void readButtons(){
  //bounce2 instances need to be called each loop
  //solenoid buttons
  bounceSolenoidA.update();
  bounceSolenoidB.update();
  bounceSolenoidC.update();
  //bounceSolenoidD.update();
  //bounceSolenoidE.update();
  //bounceSolenoidF.update();

  //stepper buttons
  bounceStepperA.update();
  bounceStepperB.update();
  bounceStepperC.update();
  }
}

//This function will take desired steps should any
//button have been pressed
void actOnButtons(){
//if the button update showed 'pressed' as TRUE
//the correlating pin will be written HIGH...so long as
//the preselected cool off period has expired
  if (bounceSolenoidA.pressed() && (currentTime - solenoidATimer) >= solenoidCoolOff){
    solenoidAGo();
    //get time when solenoid pin was triggered
    //to be used when timer function is called to set LOW
    solenoidATimer = millis();
  }
    if (bounceSolenoidB.pressed() && (currentTime - solenoidBTimer) >= solenoidCoolOff){
    solenoidBGo();
    solenoidBTimer = millis();
  }
    if (bounceSolenoidC.pressed() && (currentTime - solenoidCTimer) >= solenoidCoolOff){
    solenoidCGo();
    solenoidCTimer = millis();
  }
  //  if (bounceSolenoidD.pressed() && (currentTime - solenoidDTimer) >= solenoidCoolOff){
  //  solenoidDGo();
  //  solenoidDTimer = millis();
  //}
  //  if (bounceSolenoidE.pressed() && (currentTime - solenoidETimer) >= solenoidCoolOff){
  //  solenoidEGo();
  //  solenoidETimer = millis();
  //}
  //  if (bounceSolenoidF.pressed() && (currentTime - solenoidFTimer) >= solenoidCoolOff){
  //  solenoidFGo();
  //  solenoidF Timer = millis();
  //}
    //
    if (bounceStepperA.pressed()){
    stepperAGo = TRUE;
  }
    if (bounceStepperB.pressed()){
    stepperBGo = TRUE;
  }
    if (bounceStepperB.pressed()){
    stepperCGo = TRUE;
  }
}

//This is a function to process the input from the rotary encoders
void rotaryEncoderInput(){}

//This function checks to see if the solenoids have been 'on' long enough
//If they have been HIGH more than the designated # of millis()
//it will set the bool that actOnButtons checks to FALSE
void checkTimer(){

  if (currentTime - solenoidATimer >= solenoidDuration){
    digitalWrite(solenoidAPin, LOW);
  }
    if (currentTime - solenoidBTimer >= solenoidDuration){
    digitalWrite(solenoidBPin, LOW);
  }
    if (currentTime - solenoidCTimer >= solenoidDuration){
    digitalWrite(solenoidCPin, LOW);
  }
  //  if (currentTime - solenoidDTimer >= solenoidDuration){
  //  digitalWrite(solenoidDPin, LOW);
  //}
  //  if (currentTime - solenoidETimer >= solenoidDuration){
  //  digitalWrite(solenoidEPin, LOW);
  //}
  //  if (currentTime - solenoidFTimer >= solenoidDuration){
  //  digitalWrite(solenoidFPin, LOW);
  //}
}


//These functions trigger solenoids exclusively, allowing these functions to be called
//by any other function, rather than writing all the code each time.

void solenoidAGo(){
  digitalWrite(solenoidAPin, HIGH);
}

void solenoidBGo(){
  digitalWrite(solenoidBPin, HIGH);
}

void solenoidCGo(){
  digitalWrite(solenoidCPin, HIGH);
}

//void solenoidDGo(){
//  digitalWrite(solenoidDPin, HIGH);
//}

//void solenoidEGo(){
//  digitalWrite(solenoidEPin, HIGH);
//}

//void solenoidFGo(){
//  digitalWrite(solenoidFPin, HIGH);
//}

//This function will check if motors are supposed to be running
//and then run them until they reach the set position
void runMotors(){
  if(stepperAGo){
    stepperA.run();
    if (!stepperA.run()){
      stepperAGo = FALSE;
      stepperA.setCurrentPosition(0);
    }
  }
  if(stepperBGo){
    stepperB.run();
    if (!stepperB.run()){
      stepperBGo = FALSE;
      stepperB.setCurrentPosition(0);
    }
  }
  if(stepperCGo){
    stepperC.run();
    if (!stepperC.run()){
      stepperCGo = FALSE;
      stepperC.setCurrentPosition(0);
    }
  }

}

void fullStop(){
  //run each stepper until its 'stop'
  //condition is true
  if(stepperAStop){
    stepperA.run();
    if (!stepperA.run()){
      stepperAStop = FALSE;
      stepperA.setCurrentPosition(0);
    }
  if(stepperBStop){
    stepperB.run();
    if (!stepperB.run()){
      stepperBStop = FALSE;
      stepperB.setCurrentPosition(0);
    }
  if(stepperCStop){
    stepperC.run();
    if (!stepperC.run()){
      stepperCStop = FALSE;
      stepperC.setCurrentPosition(0);
    }
  }

  //reset killswitch pressed condition once all motors have stopped
  if (!stepperA.run() && !stepperB.run() && !stepperC.run){
    KILLSwitchPressed = FALSE;
  }
}
//this function may be superfluous, but perhaps it would be helpful to have
//a function that only receives the input and stores it. Then the function
//returns what it is holding to the stepperfunction. Or maybe just assigning
//values to some variables which can then be used by whichever function needs them.
//void stepperParameters (){}


//These functions need to have parameters passed to them that define
//speed and direction. AccelStepper should be used for the speed ramping
//capabilities, since these need to be able to change the direction of the
//motor. Also need to either run the motor until stopped (button press)
//or run for a duration that can be altered with a rotary encoder.
//void moveStepper01(){}

//void moveStepper02(){}

//void moveStepper03(){}





Adrian Brown

unread,
Aug 26, 2025, 4:33:44 PM (12 days ago) Aug 26
to accelstepper
I tried adding it as a PDF, and that message disappeared as well. Is there a preferred attachment media? Can I add a google drive link? Am I being hazed as the new guy?

On Tuesday, August 26, 2025 at 1:53:32 PM UTC-4 jla...@pacifier.com wrote:

Adrian Brown

unread,
Aug 26, 2025, 4:33:45 PM (12 days ago) Aug 26
to accelstepper
void runMotors(){
  if(stepperAGo){
    stepperA.run();
    if (!stepperA.run()){
      stepperAGo = FALSE;
      stepperA.setCurrentPosition(0);
    }
  }
  if(stepperBGo){
    stepperB.run();
    if (!stepperB.run()){
      stepperBGo = FALSE;
      stepperB.setCurrentPosition(0);
    }
  }
  if(stepperCGo){
    stepperC.run();
    if (!stepperC.run()){

Mike McCauley

unread,
Aug 26, 2025, 4:34:53 PM (12 days ago) Aug 26
to accels...@googlegroups.com, Adrian Brown

Google held them because it thought they were spam.

They have now been approved.

> >> <https://groups.google.com/d/msgid/accelstepper/af44a89e-7674-4a18-a020-

> >> d50f27f6de79n%40googlegroups.com?utm_medium=email&utm_source=footer> .



--

Mike McCauley           VK4AMM                   mi...@airspayce.com

Airspayce Pty Ltd 9 Bulbul Place Currumbin Waters QLD 4223 Australia  

http://www.airspayce.com                                 5R3MRFM2+X6

Phone +61 7 5598-7474


Jim Larson

unread,
Aug 27, 2025, 2:35:44 PM (11 days ago) Aug 27
to accelstepper
Hi Adrian -
First, thanks for your persistence in getting your code posted. I do, however, really appreciate google's anti-spam efforts!
I have a few questions after looking over your code. Please note that my focus is on getting the steppers to run properly, the rest is up to you.
The dmx appears to be just another method of controlling your system, in addition to the buttons. Is that correct? (I'm guessing you are trying to control some piece of stage lighting or effects machine?)
What Arduino are you using or planning to use? That will limit how many interrupts you have to use for buttons.
If I understand correctly, your system will start with no motors running. When a button is pressed, a motor will begin running and will run for some distance. While the motor is running, another motor may be started. The motors will stop when they reach the desired distance, or if the KILL button is pressed. The KILL will cause all motors to stop.Another press of a motor button will start the motor running again for the same distance and same direction.
Questions: 
Are the motors reset to a starting position when they stop, or do they just run repetitively on each button press?
How quickly does the system have to respond? That is, if the buttons were only polled every 1/10th second, would that be fast enough or not?

OK. That should be enough to get me started.

          -jim

Adrian Brown

unread,
Aug 27, 2025, 3:34:56 PM (11 days ago) Aug 27
to accels...@googlegroups.com
I am not salty about the caution regarding people posting, particularly brandnoobs like me.

I am building a box to control some motors in front of a camera.

Yes, the DMX input will be used by a digital input to trigger things more precisely, but being able to activate them manually was necessary as well.

Originally I was using a Mega 2560 from Elegoo.

I was going to rely on polling for the manual triggers, since anything requiring more precision will be triggered through DMX.

The steppers do not currently need to return to a starting position, but I planned to introduce it in the future in the event that cables might get twisted. I am simultaneously working on code for a rotary encoder that will interact with menus on a small screen to allow for manipulating stepper parameters. a 'go home' function would be very useful.

Nothing will ever need to very for very long, probably not very quickly, and certainly not in rapid succession.

Jim Larson

unread,
Aug 30, 2025, 12:32:33 AM (9 days ago) Aug 30
to accelstepper
HI Adrian -

Following is a sketch that runs one motor repetitively on successive button presses. If the KillSwitch is pressed, the motor stops and can be restarted with another button press. Next I will add another motor with similar behavior, But I post this so you can see the development. If this behavior is not what you desire, please let me know so I can correct this.
You will have to change the pin numbers for your system. I am using an STM32G431 based Arduino, but your Mega should work fine.

                 -jim
-----------------------------------------------------------------------------------------------------------------------
/*
From the OP:
I am attempting to run three different nema17 steppers with accelStepper. My intention is to have the motors run to a specific distance (or perhaps for a specific duration?) when triggered.
But I want to be able to trigger them several times.
Currently, I am setting a default distance, and calling stepperA.run(); with each loop.
My questions is: How do I make the stepper run a second time? Do I need to call stepperA.setCurrentPosition(0); first? I suppose I could call stepperA.currentPosition() to determine when the stepper is finished, and then set that position to 0.
But I have also written an ISR to stop the motors in respone to a kill switch. How would I set that position to '0'? Should I just check until the speed is '0' and then make that position '0'?
*/
/*
Control Scheme:
1. System starts with stepperAGo set to false.
2. If button is pressed, move(1000) is called to start motor. stepperAGo is now true.
3. run() is continually called if stepperAGo is true until distanceToGo is zero, then stepperAGo is set to false. Return to 2.
4. If stepperAGo is true and button is pressed, notheing happens, the motor continues to run like 3.
5. If the KillSwitch is pressed, motorA.stop() will be called if stepperAGo is true. The motor continues to run as in 3.
6. If the KillSwitch has been pressed, make sure all motors have stopped before re-enabling the interrupt.
*/
#include <AccelStepper.h>
#include <Bounce2.h>
// Stepper controls (A) with a switch variable
// used to start the A motor
const byte stepperAStepPin = 14;
const byte stepperADirPin = 16;
const byte stepperASwitchPin = 3;
//Variable to determine if stepper motor should be active
//// These shouldn't be declared const!
bool stepperAGo = false;
//variable to determine if stepper motor received
//killswitch command
volatile bool KILLSwitchPressed = false;
//// The Kill pin
const byte KILLSwitch = 4;
//Current time global variable, called at beginning of each loop
//to be available to any function
////int32_t currentTime = 0;
// Bounce object for stepper A run button
Bounce2::Button bounceStepperA = Bounce2::Button();
//Instantiations of AccelStepper function
//one for each motor
AccelStepper stepperA(AccelStepper::DRIVER, stepperAStepPin, stepperADirPin);
//---------------------------------------------------------------------------
void setup() {
//configure killswitch pin
pinMode(KILLSwitch, INPUT_PULLUP);
//set killswitch pin as an interrupt
//that triggers on low and calls the ISR
//// Note that the G431 has up to 16 interrupts!
attachInterrupt(digitalPinToInterrupt(KILLSwitch), KILLSwitchISR, LOW); /// either way works
//attachInterrupt(KILLSwitch, KILLSwitchISR, LOW);
//set stepper default parameters
stepperA.setMaxSpeed(200.0);
stepperA.setAcceleration(100.0);
////stepperA.moveTo(1000); // Do this on button press
//Attach bounce instance to the stepper button INPUT pins
bounceStepperA.attach(stepperASwitchPin, INPUT_PULLUP);
//stepper buttons
bounceStepperA.interval(25);
//Set the pressed state for each button pin
//LOW for 'INPUT_PULLUP' or HIGH for 'INPUT'
//stepper buttons
bounceStepperA.setPressedState(LOW);
//Designating pins per stepper
//// Note accelStepper doesn't need pin initialization
//// Need to check, but bounce probably doesn't need initialization either (examples don't show any init)
}
//---------------------------------------------------------------------------------------------
void loop() {
//always check if killswitch was activated
//if it was, run the function to stop the steppers
if (KILLSwitchPressed == true) {
//reset the killswitch - no more interrupts can happen until this is done.
//// Actually, do this later after motors stop
////KILLSwitchPressed = false;
fullStop();
//// Is it necessary to lock out button presses until all stopped?
}
////readDMX ();
//readRotary (); //this function will be fleshed out later
readButtons();
actOnButtons();
runMotors();
////checkTimer (); ////??
}
//ISR that sets the killswitch to activate
//loop constantly checks if 'Pressed' is TRUE
///////////////////////
//// This should use the method of disabling the action until time has passed
//// so that multiple interrupts can't happen.
////////////////////////////////////////////////////////////
void KILLSwitchISR() {
if (KILLSwitchPressed == false)
KILLSwitchPressed = true;
//// The rest should be set in the loop or fullStop()
////stepperAGo = FALSE;
////stepperA.stop();
}
//Need to poll all buttons with a debounce
void readButtons() {
//bounce2 instances need to be called each loop
//stepper buttons
bounceStepperA.update();
}
//This function will take desired steps should any
//button have been pressed
// //This should be combined with readButtons() - see button2 examples
void actOnButtons() {
//if the button update showed 'pressed' as TRUE
//the correlating pin will be written HIGH.
//
////Need to check if motor is stopped. If not, ignore the press.
//// May need to lock out presses if KillSwitch has been pressed?????
if(stepperAGo == false) {
if (bounceStepperA.pressed()) {
stepperAGo = true;
stepperA.move(1000); // Do this on button press
}
}
}
//This function will check if motors are supposed to be running
//and then run them until they reach the set position
void runMotors() {
if (stepperAGo) {
stepperA.run();
if (stepperA.distanceToGo() == 0) { //// May need to be sure speed is zero also?
stepperAGo = false;
}
}
//reset killswitch pressed condition once all motors have stopped
if (stepperAGo == false) {
////if (!stepperA.run() && !stepperB.run() && !stepperC.run) {
KILLSwitchPressed = false;
}
}
//// All this needs to do is call stop() for each motor that has stepperXGo true. runMotors() will take care of actually stopping the motors.
void fullStop() {
if (stepperAGo) {
stepperA.stop();
}
}

Jim Larson

unread,
Sep 2, 2025, 10:49:23 PM (5 days ago) Sep 2
to accelstepper
A two motor version of the above code is now working. While I can post the code if anyone is interested, the two motor version is pretty much an obvious extension of the one motor version.. All the statements are simply copied with the motor or button changed to B instead of A. The only statement that has an interaction is the KillSwitchPressed reset  test to make sure the motors are stopped. That test becomes 
if ((stepperAGo == false) && (stepperBGo == false)){
{
Obviously, the extension to three motors follows the same procedure.

              -jim

Ralph Hulslander

unread,
Sep 4, 2025, 5:12:37 PM (3 days ago) Sep 4
to accels...@googlegroups.com
I would like to see the code! rhulslander at gmail

You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/accelstepper/05c49b89-84ab-430c-882b-56932c56b7f0n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages