Home position resets when opening/closing serial monitor

266 views
Skip to first unread message

Flynn Walker

unread,
Jan 5, 2022, 6:21:40 PM1/5/22
to accelstepper
Hello,

I'm working on a project that controls 2 steppers in parallel (same exact movement for each), that will be controlled using serial commands from Python via a Python GUI to control a linear stage. It is mostly working very well - however, I am running into 2 issues:

1 (most important): The Home position for the steppers resets when I close/open a serial connection via either Arduino IDE or using python serial module. That is to say, every time I open a serial connection, it says that the current location is the home position. I then effectively lose the positional encoding of my linear stages. If this home position is being stored by the accelstepper library, why would simply opening/closing serial connection change that? 

2 (less important): I initially setup the code for 1 stepper, and then duplicated the commands to deal with the 2nd stepper as well. However, with both steppers running, they run significantly more slowly than when I just run 1 stepper. I've ensured that it is not a power supply issue, so I'm not really sure why this is happening. 

Below is my code:

//Transforming the motor's rotary motion into linear motion by using a threaded rod:
//Threaded rod's pitch = 2 mm. This means that one revolution will move the nut 2 mm.
//Default stepping = 200 step/revolution.
// 20000 step = 1 revolution = 8 mm linear motion. (4 start 2 mm pitch screw, with 100:1 gearbox)
// 1 cm = 10 mm =>> 10/8 * 20000 = 25000 steps are needed to move the nut by 1 cm.
 
 
//character for commands
/*
     'C' : Prints all the commands and their functions.
     'P' : Rotates the motor in positive (CW) direction, relative.
     'N' : Rotates the motor in negative (CCW) direction, relative.
     'R' : Rotates the motor to an absolute positive position (+).
     'r' : Rotates the motor to an absolute negative position (-).
     'S' : Stops the motor immediately.
     'A' : Sets an acceleration value.
     'L' : Prints the current position/location of the motor.
     'H' : Goes back to 0 position from the current position (homing).
     'U' : Updates the position current position and makes it as the new 0 position.
     'T' : Checks to see if the motor is running (to know if new commands can be sent
 */
 
#include <AccelStepper.h>
#include <MultiStepper.h>
 
//User-defined values
long receivedSteps = 0; //Number of steps
long receivedSpeed = 0; //Steps / second
long receivedAcceleration = 0; //Steps / second^2
char receivedCommand;
bool isrunning1 = false;
bool isrunning2 = false;
bool bool_out = false;
//-------------------------------------------------------------------------------
int directionMultiplier = 1; // = 1: positive direction, = -1: negative direction
bool newData, runallowed = true; // booleans for new data from serial, and runallowed flag
AccelStepper stepper1(1, 9, 8);//
AccelStepper stepper2(1, 6, 7);//

MultiStepper steppers;
 
void setup()
{
    Serial.begin(9600); //define baud rate
    //Serial.println("Demonstration of AccelStepper Library"); //print a messages
    //Serial.println("Send 'C' for printing the commands.");
 
    //setting up some default values for maximum speed and maximum acceleration
    //Serial.println("Default speed: 400 steps/s, default acceleration: 800 steps/s^2.");
    stepper1.setMaxSpeed(10000); //SPEED = Steps / second
    stepper1.setAcceleration(10000); //ACCELERATION = Steps /(second)^2
    stepper2.setMaxSpeed(10000); //SPEED = Steps / second
    stepper2.setAcceleration(10000); //ACCELERATION = Steps /(second)^2
   
}
 
void loop()
{
    //Constantly looping through these 2 functions.
    //We only use non-blocking commands, so something else (should also be non-blocking) can be done during the movement of the motor
 
    checkSerial(); //check serial port for new commands
    stepper1.run();  
    stepper2.run();
}
 
 
void checkSerial() //function for receiving the commands
{  
    if (Serial.available() > 0) //if something comes from the computer
    {
        receivedCommand = Serial.read(); // pass the value to the receivedCommad variable
        newData = true; //indicate that there is a new data by setting this bool to true
 
        if (newData == true) //we only enter this long switch-case statement if there is a new command from the computer
        {
            switch (receivedCommand) //we check what is the command
            {
 
            case 'P': //P uses the move() function of the AccelStepper library, which means that it moves relatively to the current position.              
               
                receivedSteps = Serial.parseFloat(); //value for the steps
                directionMultiplier = 1; //We define the direction
                RotateRelative(); //Run the function
 
                //example: P2000 400 - 2000 steps (5 revolution with 400 step/rev microstepping) and 400 steps/s speed
                //In theory, this movement should take 5 seconds
                break;        
 
            case 'N': //N uses the move() function of the AccelStepper library, which means that it moves relatively to the current position.      
               
                receivedSteps = Serial.parseFloat(); //value for the steps
                directionMultiplier = -1; //We define the direction
                RotateRelative(); //Run the function
 
                //example: N2000 400 - 2000 steps (5 revolution with 400 step/rev microstepping) and 500 steps/s speed; will rotate in the other direction
                //In theory, this movement should take 5 seconds
                break;
 
            case 'R': //R uses the moveTo() function of the AccelStepper library, which means that it moves absolutely to the current position.            
 
                receivedSteps = Serial.parseFloat(); //value for the steps    
                directionMultiplier = 1; //We define the direction
                RotateAbsolute(); //Run the function
 
                //example: R800 400 - It moves to the position which is located at +800 steps away from 0.
                break;
 
            case 'r': //r uses the moveTo() function of the AccelStepper library, which means that it moves absolutely to the current position.            
 
                receivedSteps = Serial.parseFloat(); //value for the steps
                directionMultiplier = -1; //We define the direction
                RotateAbsolute(); //Run the function
 
                //example: r800 400 - It moves to the position which is located at -800 steps away from 0.
                break;
 
            case 'S': // Stops the motor
               
                stepper1.stop(); //stop motor
                stepper2.stop(); //stop motor
               
                Serial.println("Stopped."); //print action
                break;
 
            case 'A': // Updates acceleration
                receivedAcceleration = Serial.parseFloat(); //receive the acceleration from serial
                stepper1.setAcceleration(receivedAcceleration); //update the value of the variable
                stepper2.setAcceleration(receivedAcceleration); //update the value of the variable
                break;
 
            case 'L': //L: Location

                Serial.println(stepper1.currentPosition()); //Printing the current position in steps.
                break;
               
            case 'H': //H: Homing
 
                GoHome();// Run the function
                break;
 
            case 'U':
 
                stepper1.setCurrentPosition(0); //Reset current position. "new home"
                stepper2.setCurrentPosition(0); //Reset current position. "new home"      
                Serial.println("New Home Position Set");    
                break;

            case 'T':
                isrunning1 = stepper1.isRunning();
                isrunning2 = stepper2.isRunning();
                bool_out = (isrunning1 || isrunning2);
                Serial.println(bool_out);
                break;
 
            case 'C':

                PrintCommands(); //Print the commands for controlling the motor
                break;
 
            default:  

                break;
            }
        }
        //after we went through the above tasks, newData is set to false again, so we are ready to receive new commands again.
        newData = false;      
    }
}
 
 
void GoHome()
{  
    if (stepper1.currentPosition() == 0)
    {
        Serial.println("We are at the home position.");
    }
    else
    {
        stepper1.moveTo(0);
        stepper2.moveTo(0);
       
    }
}
 
void RotateRelative()
{
    //We move X steps from the current position of the stepper motor in a given direction.
    //The direction is determined by the multiplier (+1 or -1)
    stepper1.move(directionMultiplier * receivedSteps); //set relative distance and direction
    stepper2.move(directionMultiplier * receivedSteps); //set relative distance and direction
}
 
 
 
void RotateAbsolute()
{
    //We move to an absolute position.
    //The AccelStepper library keeps track of the position.
    //The direction is determined by the multiplier (+1 or -1)
    //Why do we need negative numbers? - If you drive a threaded rod and the zero position is in the middle of the rod...
 
    stepper1.moveTo(directionMultiplier * receivedSteps);
    stepper2.moveTo(directionMultiplier * receivedSteps);
}
 
void PrintCommands()
{  
    //Printing the commands
    Serial.println(" 'C' : Prints all the commands and their functions.");
    Serial.println(" 'P' : Rotates the motor in positive (CW) direction, relative.");
    Serial.println(" 'N' : Rotates the motor in negative (CCW) direction, relative.");
    Serial.println(" 'R' : Rotates the motor to an absolute positive position (+).");
    Serial.println(" 'r' : Rotates the motor to an absolute negative position (-).");
    Serial.println(" 'S' : Stops the motor immediately.");
    Serial.println(" 'A' : Sets an acceleration value.");
    Serial.println(" 'L' : Prints the current position/location of the motor.");
    Serial.println(" 'H' : Goes back to 0 position from the current position (homing).");
    Serial.println(" 'U' : Updates the position current position and makes it as the new 0 position. ");

I am using the following stepper driver (x2):

The following stepper motor (x2):

With a 48V, 20A power supply.

Thank you for your help!

Jim Larson

unread,
Jan 5, 2022, 9:02:10 PM1/5/22
to accelstepper
Regarding the Home Position reset: Do you see the New Home Position Set message? That could mean you're getting unexpected characters when the python script starts serial communication. In any case, I would start by having the serial link echo whatever it receives and have each command print a message. That's independent of AccelStepper - make sure it works as expected first.
Unless your Arduino is being reset when serial starts, there is no obvious reason why the Home position should be reset.

Regarding the speed question: I'm assuming you are running an Arduino Uno or a clone. If so, based on my experiments, using run() as you're doing it will step at most 4500 steps per second without error. Faster than that will result in missed steps and so adding a second motor will cause even ore steps to be missed. AccelStepper won't know this, it just does the best it can. I suggest you slow both motors to 1000 steps per second and see if you get the correct behavior.

I've recently posted this https://hackaday.io/project/183279-accelstepper-the-missing-manual which you may find useful in using the AccelStepper library. Any comment you have are most appreciated.
    -jim

Flynn Walker

unread,
Jan 5, 2022, 9:50:08 PM1/5/22
to accelstepper
Thank you for the response, Jim.

Re: testing to make sure all serial commands are working properly -- they are, I tested that extensively with having each command print a message like you suggested, before removing them once I knew it was all working properly. There don't seem to be any unexpected characters being transferred. My thoughts are exactly what you are saying :: unless Arduino is being reset when serial starts, it shouldn't reset the home position. And yet it does for some reason...

For the speed question, I've noticed the same behavior that it can't do more than 4500 steps per second. However, both of my steppers are closed loop with closed loop controllers/encoders, so this missing steps part shouldn't matter -- I've also tested this, and I get the same speed with speed set at 4500 as I do at 5000 as I do at 10000. 

I will definitely dive into that manual there  -- thanks for linking that! 
- Flynn

Jim Larson

unread,
Jan 6, 2022, 12:19:28 AM1/6/22
to accelstepper
Let me be sure I understand correctly. First, regarding serial communication, I'm assuming that your Arduino is an Uno or similar. Further, you run the sketch, communicate from your PC using Python, close the port, or somehow stop communicating, then try to start again. That's when the reset of Home occurs. Is that correct? If so, exactly how do you end the communication? Have you tried using Tera Term or something like that?

Regarding the speed issue, if 4500 steps per second is the same limit you see, have you tried running the two motors at one half or less of that? Does it work? I'm not sure what having a closed loop controller has to do with it. Doesn't the closed loop just make sure that a commanded step is actually taken? So if the controller only sees (say) 2500 steps per second, won't that be how many steps the motor will take? Am i missing something here??

Finally, I mention the "Missing Manual" in case you're interested. Unfortunately, I don't think it will help with the problems you're describing.

                      -jim

Jim Larson

unread,
Jan 6, 2022, 12:55:38 AM1/6/22
to accelstepper
Looks like the serial comm reset is a known Arduino problem. Toe avoid it, you must avoid using the DTR signal from Python. This is a bit beyond me (I'm not a Python jock), but here's a link that might help.

HTH.

                 -jim

gjgsm...@gmail.com

unread,
Jan 6, 2022, 3:56:35 AM1/6/22
to accelstepper
Yes, Arduino does reset on opening the Arduino IDE serial monitor which then resets 'Current Position' to zero on boot. Accelstepper does not store the 'home' position, you can do that yourself to memory if you want. You could try using a terminal like CoolTerm to communicate with your MCU which, I think from memory does not reset the running MCU when connecting. I'd have to check that again to be sure...

Also, be aware that having any 'serial read/write' activity in the same loop as the 'stepper run' function will result in very slow (and possibly jerky) motion. What happens is that you will have serial activity in between each step of the steppers which is of course, a delay. It is best to 'run' the steppers to their targets in a seperate function where they can run uninterrupted, and then you can do any serial activity before calling the stepper run function again.

Geoff

Flynn Walker

unread,
Jan 6, 2022, 1:25:08 PM1/6/22
to accelstepper
Thanks Jim and Geoff -- very helpful insights.

In order to circumvent the problem with homing, I learned about Arduino EEPROM data this morning -- by storing the positional encoding there after completing a move of my stage, I'm able to maintain the position even when the Arduino code resets. So far it is working very well!

I'm still not sure what to do about the slow moving... Jim, yes, I have tried setting the speed slower and while that does cause it to run slower, it seems to run slower than it is set to at any given run speed as long as both motors are connected. With 1 motor connected, it runs at the speed that it is set to. 

Goeff, I figured that this Serial activity was causing slowing too, however even when I completely remove it and just define move locations in the setup loop so that ONLY the run commands are in the main loop, the steppers move at the same speed as they do with the Serial checking commands there. I still think it would be good practice to not cause the serial check as often (maybe using timer interrupt to do it at some standard frequency?),  but this doesn't seem to be the cause of the slowing.

gjgsm...@gmail.com

unread,
Jan 6, 2022, 5:02:02 PM1/6/22
to accelstepper
Flynn,

I use the same brand of drivers but not that particular model, it's possible I suppose that there is a driver setting that is contributing to the issue. I assume you are not using the 'encoder' for positioning which would mean that the driver would have to be used in the 'open loop' mode. Also, does the motor run hot and is it noisy (like a grinding noise)? It should run almost silently. What is the Control Voltage setting etc...? If you would like a second opinion, can you post pics showing all the switch settings.

Have you run the Accelstepper 'Bounce' example? Bearing in mind that the stepper motor has a 100:1 gearbox you will have play around with the variables to get a motion that is visibly ok but, it will verify if your setup and driver are working ok.

Geoff

Flynn Walker

unread,
Jan 6, 2022, 5:55:04 PM1/6/22
to accelstepper
Goeff,

I've attached pictures of the switch settings (same for both drivers). I certainly would not describe the operation as "almost silent" -- they are both quite loud. I attributed this to the gearbox, but if this is a symptom of non-optimal operation, I would completely believe it. I have run the bounce example and it does work, but one of the reasons that I have the acceleration set so high in my code (I actually have it running at 100,000 steps/s/s) is that the stepper shakes/grinds madly when it moves below 1000sh steps/second, so I blast past that part of the acceleration curve quickly to compensate. This does seem like it should be happening. 

Please let me know if there is any other info I can provide!

Flynn

20220106_164640.jpg
Message has been deleted
Message has been deleted

Flynn Walker

unread,
Jan 6, 2022, 5:58:34 PM1/6/22
to accelstepper
I seem to be having issues posting the other image with switch settings. I have the switches set to ON-ON-ON-ON-OFF-OFF-OFF-ON, with 5V signal input set. 

Jim Larson

unread,
Jan 6, 2022, 6:05:06 PM1/6/22
to accelstepper
First, calling Serial.available() should cause a minimal slowing. Slowing would only happen if you do Serial.read(). So your code should not be impacted by your serial input routine.

Regarding slow down of motors: Although I don't have your hardware, I can still run code very similar to yours and see the response of the library. I moved the computNewSpeed function from the protected are to the public area in AccelStepper.h so I could call it from my Arduino code. Then I created my own run() function by copying the library code but adding counters that counted each pass through loop() as causing an actual step or not. I printed results every second for 20 seconds. What I found was that at 2000 steps per second (for each motor), the two motors ran almost identically, diverging by 5 steps after 20 seconds, and about 180,000 passes. The actual speed averaged about 1950 steps/second after the initial acceleration; about 2.5% error.

Trying 2500 steps/sec, the two motors recorded exactly the same numbers. The average speed was 2213 steps per second and there were no extra cycles available. The error is 11.5%, so the speed is no longer reliable, but it is the same for both motors.

I'm not sure what's causing the performance you observe, but I don't think it's a problem in AccelStepper. At least I haven't been able to locate it.

Flynn Walker

unread,
Jan 6, 2022, 6:22:09 PM1/6/22
to accelstepper
Thank for testing that, Jim.

Your results seem to indicate that the maximum speed that the library can output TOTAL is 4500 steps/second across all motors -- hence the avg speed of 2213 per motor (roughly 2250, aka 4500 total) when you tried 2500 steps/sec per motor (5000 steps/sec total). So this is likely why I'm experiencing a slow down with multiple motors as I try to push each of them to the single motor max speed. I guess that is solved then, and I will need to use some other method to pulse the motors faster than Accelstepper can provide if that is important to me. 

Thank you again for your help, it is much appreciated.

gjgsm...@gmail.com

unread,
Jan 6, 2022, 7:12:49 PM1/6/22
to accelstepper
I agree that your problem is most likely due to the UNO being too slow (16MHz) for your speed and acceleration settings.
A cost effective option could be to use a faster MCU like an ESP32 (228MHz), roughly 14 times faster. A DUE at (80MHZ) works well too. I have a device running a DUE with 4 steppers running simultaneously at speeds and acceleration around the 3000 to 5000.

Getting the right combination of steppers, gearing, drivers, power requirements, controller etc... to achieve the desired torque, speed, etc... is a balancing act at times. Your stepper motor alone has a max torque of 1.2kg at 10cm, this is relatively high. Adding the gearbox increases the available torque theoretically by 100 times but this is limited to 40kg at 10cm by the gearbox max capacity. If you can use a lower gearing or no gearbox then using a lower step rate (max speed/accel) to run the steppers would be possible.

Unless you're needing very high torque I would try lowering your current setting to '3' or '2' to get a smoother response.

Geoff


Flynn Walker

unread,
Jan 7, 2022, 10:53:45 AM1/7/22
to accelstepper
Thank Geoff, I'll try 3 or 2 and see how they go.

On the topic of speed -- it unless I'm misunderstanding, then 4500 steps/second seems like an AccelStepper limitation, not an issue with the UNO clock. The driver that I'm using requires a 1 microsecond pulse for each on/off signal. As I understand it, it would then require 4 microseconds to pulse 2 motors a single time each -- on1, off1, on2 off2. At 4 microseconds per step, this would theoretically allow you to get 250,000 steps per second (1,000,000 total on/off signals) , which is still below the 16 MHz limitation of the UNO. Am I understanding the clock speed correctly here? Is it possible to change the pulse time of the AccelStepper library to do this?

Jim Larson

unread,
Jan 7, 2022, 2:57:28 PM1/7/22
to accelstepper
Here's some data from tests I did with a Rigol DS1074 Scope. With run() only called once per loop, it takes 14usec if no step is taken and ~225 used if a step is taken. The step pulse width will be 16uSec with the default (1usec) setting for pulse width. It is possible to make the pulse wider, but not narrower. So the maximum speed without a step is 71KHz and nearly 4500 Hz with a step. This agrees with the results I reported earlier.
Conclusion: you can't get more than 4500 steps per second with AccelStepper using run() on an Uno. From other experiments, if you use runStep() you can get up to 20,000 steps per second, but acceleration is not available.

gjgsm...@gmail.com

unread,
Jan 7, 2022, 9:43:36 PM1/7/22
to accelstepper
This has been a hot topic for many years. See this conversation which I remembered from 2016 - gregor - speed discussion 2016
... at the time this speed test code was endorsed by Mike.

Geoff

Reply all
Reply to author
Forward
0 new messages