Homing 2 steppermotors with different steps

Hi there,

I'm new to Arduino. This is the first time I'm working with it. I've learned a lot of this forum but now I'm stuck in my code :frowning:

I've got 2 NEMA17 steppermotors with 2 drivers and 2 switches on a Arduino Mega. First thing they have to do is "homing" apart of each other. This works fine.

Then they need to move at the same time to 2 different steps. One at 415 and the other one at 830. When the first one has moved to 415 steps, he will wait until the other one is at step 830. This is what is want and they do this correctly.

After that they have to move at the same time to the "homingswitches". They do this, but both the motors will keep on moving until both switches are activated. This is not what i want. I want them to move at the same time to the switches and if one motor activates a switch, I want that motor to wait until the other motor activates the other switch. After that they can move at the same time again.

I have used this code so far:

#include "AccelStepper.h" 

// AccelStepper Setup

      // MOTOR A
AccelStepper stepperA(1, 2, 3);         // 1 = Type Driver interface
                                        // Pin 2 connected to STEP pin
                                        // Pin 3 connected to DIR pin

      // MOTOR B
AccelStepper stepperB(1, 12, 13);         // 1 = Type Driver interface
                                        // Pin 12 connected to STEP pin
                                        // Pin 13 connected to DIR pin


// Define the Pins used
#define home_switchA 30 // Pin 30 connected to Home Switch (MicroSwitch)
#define home_switchB 35 // Pin 35 connected to Home Switch (MicroSwitch)


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


// --------------------------------------------------------------------------------------------------------

void setup() {

// Start Homing procedure of Stepper Motor "A" at startup

   pinMode(home_switchA, INPUT_PULLUP);
   



   //  Set Max Speed and Acceleration of each Steppers at startup for homing
  stepperA.setMaxSpeed(160.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperA.setAcceleration(1000.0);  // Set Acceleration of Stepper


  while (digitalRead(home_switchA)) {  // Make the Stepper move CCW until the switch is activated   
    stepperA.moveTo(initial_homing);  // Set the position to move to
    stepperA.moveTo(initial_homing++);  // Decrease by 1 for next move if needed
    stepperA.run();  // Start moving the stepper
}

  stepperA.setCurrentPosition(0);  // Set the current position as zero for now
  stepperA.setMaxSpeed(160.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperA.setAcceleration(1000.0);  // Set Acceleration of Stepper
  stepperA.moveTo(initial_homing=1);



//--------------------------------------------------------------------------------------------------------

// Start Homing procedure of Stepper Motor "B" at startup

   pinMode(home_switchB, INPUT_PULLUP);
   




   //  Set Max Speed and Acceleration of each Steppers at startup for homing
  stepperB.setMaxSpeed(160.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperB.setAcceleration(1000.0);  // Set Acceleration of Stepper


  while (digitalRead(home_switchB)) {  // Make the Stepper move CCW until the switch is activated   
    stepperB.moveTo(initial_homing);  // Set the position to move to
    stepperB.moveTo(initial_homing++);  // Decrease by 1 for next move if needed
    stepperB.run();  // Start moving the stepper
}

  stepperB.setCurrentPosition(0);  // Set the current position as zero for now
  stepperB.setMaxSpeed(160.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperB.setAcceleration(1000.0);  // Set Acceleration of Stepper
  stepperB.moveTo(initial_homing=1);



}


//--------------------------------------------------------------------------------------------------------

void loop() {

                                            

// (1)

    stepperA.moveTo(-830); 
    stepperB.moveTo(-415);
  while 
    (
    stepperA.currentPosition() != -830 ||
    stepperB.currentPosition() != -415
    ) 
    {
    stepperA.run();
    stepperB.run();
    }


// (2) (HOMING)

  while 
    ( 
    digitalRead(home_switchA) ||
    digitalRead(home_switchB)
    ) 
    {
    stepperA.moveTo(initial_homing);
    stepperA.moveTo(initial_homing++);
    stepperA.run(); 
    stepperB.moveTo(initial_homing);
    stepperB.moveTo(initial_homing++);
    stepperB.run();
    }

    stepperA.setCurrentPosition(0);
    stepperA.moveTo(initial_homing=1);
    stepperB.setCurrentPosition(0);
    stepperB.moveTo(initial_homing=1);
}

I hope you'll understand what I'm meaning and I hope if you can help me.

I saw another topic on this forum that looks to have the same issue as mine: https://forum.arduino.cc/t/homing-two-stepper-motors-simultaneously-with-accelstepper/588107

There they assumed to change the code like this:

bool homingComplete = false;
bool xHome = false;
bool yHome = falsel
while(homingComplete == false) {
   if (digitalRead(home_switch_stepperX) {
       stepperX.moveTo(initial_homing_stepperX);  // Set the position to move to
       initial_homing_stepperX--;
       stepperX.run(); 
   }
   else {
       xHome = true;
   }
   // likewise for stepper Y
   
   if (xHome == true and yHome == true) {
      homingComplete = true;
   }
}

I tried this, but I think I'm doing something wrong because it didn't work..

Your code is awkward with the while() loops, which block it from being able to do different things at the same time, such as:

Look into these:

shouldn't the motor be moved one step at a time until the homing switch becomes active and when it does, that's where it's initial position should be set: setCurrentPosition (0)

don't understand why you are incrementing initial_homing, calling moveto() twice and using that value for both motors

1 Like

Doesn't it try with the stepperX.run()s inside the while()s?

Accelstepper has a built-in-example where you can abort a move early with controlled deceleration:

If you were going slow enough, you could stop at a predictable distance from where you triggered the switch. 3d printers sometimes have a multi-part process where they approach at higher speed, then back off and step slowly onto the switch.

This coding doesn't even really make sense ( and the comments also )
The first moveTo does not have any effect, because it is directly overwritten by the next one. And be aware, that your while loop is executed at full processor speed. stepperA.run(); will not wait until a step is due. It returns immediately - wether a step has been executed or not. So your intial_homing is incremented as fast as the processor loops - many times between two steps. Calling moveTo with a high value ( enough to reach the limit switch ) before the while would have the same effect.

That's how it is programmed. The same problem with the variable initial_homing increasing very rapidly, and run() is called for both steppers antil both limit switches are activated. So both steppers will run, until both are activated.
Position the moveTo() ( or move() ) in front of the while loop(), and make the run() call dependent wether the according limit switch is activated.

On the other hand - why not simply moving both steppers to position 0 after the homing has been done in setup() ?

1 Like

What do you trying to achieve this?

There are a couple Mobatools MobaStepper examples that move steppers to homing switches. This one might have some of the infrastructure you need:

You'd need a richer configuration of its state machine logic to get the sequencing you want.

If you don't want to change the library, you may try this (untested):

#include "AccelStepper.h"

// AccelStepper Setup

// MOTOR A
AccelStepper stepperA(1, 2, 3);         // 1 = Type Driver interface
// Pin 2 connected to STEP pin
// Pin 3 connected to DIR pin

// MOTOR B
AccelStepper stepperB(1, 12, 13);         // 1 = Type Driver interface
// Pin 12 connected to STEP pin
// Pin 13 connected to DIR pin


// Define the Pins used
#define home_switchA 30 // Pin 30 connected to Home Switch (MicroSwitch)
#define home_switchB 35 // Pin 35 connected to Home Switch (MicroSwitch)


// Stepper Travel Variables
long initial_homing = 4000; // Number of steps to reach limit switch in any case


// --------------------------------------------------------------------------------------------------------

void setup() {

  // Start Homing procedure of Stepper Motor "A" at startup

  pinMode(home_switchA, INPUT_PULLUP);
  //  Set Max Speed and Acceleration of each Steppers at startup for homing
  stepperA.setMaxSpeed(160.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperA.setAcceleration(1000.0);  // Set Acceleration of Stepper


  stepperA.move(initial_homing);        // Move towards the limit switch
  while (digitalRead(home_switchA)) {   // Make the Stepper move CCW until the switch is activated
    stepperA.run();                       // step until limit switch is reached

  }

  stepperA.setCurrentPosition(0);  // Set the current position as zero for now

  // You don't need the following if speed and Acceleration doesn'T change from settings above
  stepperA.setMaxSpeed(160.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperA.setAcceleration(1000.0);  // Set Acceleration of Stepper




  //--------------------------------------------------------------------------------------------------------

  // Start Homing procedure of Stepper Motor "B" at startup


  pinMode(home_switchB, INPUT_PULLUP);
  //  Set Max Speed and Acceleration of each Steppers at startup for homing
  stepperB.setMaxSpeed(160.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperB.setAcceleration(1000.0);  // Set Acceleration of Stepper


  stepperB.moveTo(initial_homing);      // Move towards the limit switch
  while (digitalRead(home_switchB)) {   // Make the Stepper move CCW until the switch is activated
    stepperB.run();                     // step until limit switch is reached
  }

  stepperB.setCurrentPosition(0);  // Set the current position as zero for now
  stepperB.setMaxSpeed(160.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperB.setAcceleration(1000.0);  // Set Acceleration of Stepper

}


//--------------------------------------------------------------------------------------------------------

void loop() {

  // (1)

  stepperA.moveTo(-830);
  stepperB.moveTo(-415);
  while
  (
    stepperA.currentPosition() != -830 ||
    stepperB.currentPosition() != -415
  )
  {
    stepperA.run();
    //delay(5);             // no need for a delay - it may slow down the movement
    stepperB.run();
    //delay(5);
  }


  // (2) (HOMING) - Drawback of this approach is that there is no deceleration. It stops immediately if the homeswitch is reached
  stepperA.moveTo(initial_homing);
  stepperB.moveTo(initial_homing);
  {
    while
    (
      digitalRead(home_switchA) ||
      digitalRead(home_switchB)
    )
    {
      if ( digitalRead(home_switchA) )stepperA.run(); // Step only if home switch A is not activated
      //delay(5);
      if ( digitalRead(home_switchB) )stepperB.run(); // Step only if home switch B is not activated
      //delay(5);
    }

    stepperA.setCurrentPosition(0);
    stepperB.setCurrentPosition(0);
  }

  /* alternative approch for (2):
      stepperA.runToNewPosition(0);
      stepperB.runToNewPosition(0);
  */

}

But with MobaTools it's much more easy to get a nonblocking solution.

I do not understand the need the multiple homing cycles. If the machine isn't underpowered and loses steps, the one homing cycle in setup() should be enough, and then future motions should all be referenced to that.

Do you move the "homing" switches between cycles? If not, then this whole requirement:

...could be solved with:

void moveBackHome(){
  stepperA.moveTo(0);
  stepperB.moveTo(0);
  while(stepperA.distanceToGo() > 0 
        || stepperB.distanceToGo() > 0 ) 
  {
    stepperA.run();
    stepperB.run();
  }
}

Hi all,

Thanks for the replies and to think along with me in this project.

The reason I want to reset the motors to "home" in the loop, is that after some time I saw the zero-postion was moved a few steps. That's why I thought to recalibrate every motor seperatly to its zero-postion in the loop, every time they hit their own switch.

The loop will run for about a few hours. After that the power will go off untill the next day.

As mentioned earlier, I'm really new to code-writing and Arduino. The code I had was mostly copied and paste from online sources. I know it was not good but it was everything I had. I hope to learn a lot from you and make some awesome projects with it. So again, thanks for every help I can get from all of you!

I will test your advises in the code tonight (I'm from the Netherlands) and I will keep you posted!

This indicates another error. Your stepper is obviously losing steps. You should search for the reason. A well designed stepper system should not lose steps.

1 Like

while i generally agree, i though many systems will re-sync (?) whenever they happen to travel past some sync point, just in case.

could the motor be being stepped too quickly when started? what about adding another switch somewhere near the middle of travel that can be used to verify it's position?

i read that some aircraft instruments use encoders to verify the position of their stepper motor driven gauges. of course, the instruments could be under high-g forces


would you ming explaining the differences between the libray move, run and step commands? do move cmds actually cause the motor to turn?

Yes, there are use cases in which this can be useful or even necessary.

Are you talking about Accelstepper? The step command is usually not called by an application when using accelstepper. The move commands only set a target value ( in steps, counted from the last set reference point. move sets this target relative to the actual position, moveTo sets it relative to the reference point ( an 'absolute' position, which is not dependent on the actual position). These commands don't move the stepper. Only run() and runSpeed() create steps, but at max 1 step per call. They check if a step is due. If it is, they create the step and return, if not they return immediately. So you have to call them frequently ( more often than the actual steprate needs ). This can be challenging, if there are also other ( long running ) tasks to do.
There are also blocking commands, that only return if the target is reached.
Here you can find a documentation of all AccelStepper methods.

The MoToStepper class of my MobaTools creates the steps in the background ( via timer interrupts ). So you don't have to cope with step generation in your sketch. With MoToStepper the move command(s) really start the stepper.
Drawback is, that it's hardware dependent ( because of the timer configuration and ISR ) and does not support all processors/boards.

This program could lose steps with hard stops that occur when it activates a limit switch.

If it is a concern, I'd set the limit/homing/reference switches such that you could go past them a little bit, and make the return target at -5 or -10 or so, so you could decelerate smoothly to a stop at a place that engagnes the limit switch, and also get a measure of system health & reliability each cycle.

The AccelStepper examples cover a few quick stops that have controlled deceleration:

but wouldn't the "homing" case be a good example of where it should be used? or should it?

when homing, don't you want to increase the relative position by one and only increase it again after a step has been completed? (how can you do that with AccelStepper)?

wouldn't repeatedly calling move() and run() without any delay potential be faster than the motor is actually turning, allowing the position set by move() to accumulate?

shouldn't you be going relatively slow when homing to avoid any momentum issues.

presumably you should never come close to the limit switch during normal operation

No, step is internally used by e.g. the run() command. It creates a step immediately without any step timing. So let run() do the job.

Usually you should know the max count of steps to reach the limit switch. So set the target that amount away from the actual position in the direction of the limit switch. If the limit switch is reached you cancel this target. There is no need to set a new target again and again. And it is always a good idea to go a little bit beyond the limit switch and then go back slowly as @DaveX suggested.

If really depends how you conceptualize the switches. If you want accurate positioning, you slowly approach a good quality repeatable switch from a single direction and note when you activate it, and then expect your robust stepping system doesn't lose steps as you use it. If it is for safety, you put an extra set of switches to cut power to the system and stop the motors. If the switches are part of the process, you put them in the range of motion and decelerate after you hit them, AccelStepper::stop(), for example.

If you try to use the same switch for a blurry mix of all three, you can get bad behavior-- "home" moves when you hit the switch at different speeds, You skip steps if you stop stepping cold-turkey when you hit the switch. If you put the edge of your process at the limit switch, you don't get repeatable deceleration.

why would it lose steps if done properly?

how does AccelStepper know a complete step has been performed?

Barring a wrench in the works, it wouldn't, and you wouldn't need to look at the homing switch again. If you expect it to lose steps, you shouldn't run at speed into hard-stop when it hits the limit, you should decelerate before you get to where you expect it to be, and step slowly towards past where you think it should be while watching to see how many steps it has lost, either + or -. If the switch is broken or the driver is broken you can detect an error.