AccelStepper setSpeed inconsistancy?

I use the following functions to initialize (set Home and Limit Position) my stepper on a linear axis with 2 end switches:

void initStepper() {
  lcd.setCursor (0, 1); lcd.print(F("*** Initializing ***"));
  stepper.enableOutputs();
  stepper.setSpeed(INIT_SPEED);     // a positive speed here works for homing and limiting
  homeStepper();
  delay(500);
  limitStepper();
  delay(500);
  stepper.setSpeed(-INIT_SPEED);   // but without this, it crashes for targets 0 or -1 instead of going back
  stepper.moveTo(0);
  while (stepper.currentPosition() != 0) {
    stepper.run();
  }
}

void homeStepper() {
  // Always home the stepper to the motor side
  int32_t initial_homing = -1;

  lcd.setCursor (0, 2); lcd.print(F("      Homing        "));

  while (!digitalRead(LIMIT_M)) {
    stepper.moveTo(initial_homing);
    stepper.run();
    initial_homing--;
  }
  // home position reached, add a little offset to unload the switch
  stepper.setCurrentPosition(-LIMIT_OFFSET);
}

void limitStepper() {
  // Always limit the stepper to the off-motor side
  int32_t initial_homing = 1;

  lcd.setCursor (0, 2); lcd.print(F("     Limiting       "));

  while (!digitalRead(LIMIT_B)) {
    stepper.moveTo(initial_homing);
    stepper.run();
    initial_homing++;
  }
  limitBPos = stepper.currentPosition() - LIMIT_OFFSET;
}

It does, what I expect. For testing I added a move back to the home position (0) at the end of 'initStepper'. At this moment the stepper is at position 80000. Without adding a negative speed here the carriage is banging against the LIMIT_B end switch, instead of moving back. So I've tried it with a run to a negative position (-1). Same problem.

Notice that I've set a positive speed in initStepper(), which is used for homing as well as limiting. Why do I need a negative speed to go from a very high position to '0' or '-1' ? So sometimes, a negative target works correctly with a positive speed, and sometimes not.

Please post the complete program.

...R

Hi Robin,

thanks for answering. The full code fills an UNO and expands over 6 tabs. I made a shorter test version, which is fully functional. I want to home and limit the stepper in Setup(), while a goHome() function later would reside in Loop().

#include <TaskMacro.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <LCDMenuLib2.h>
#include <AccelStepper.h>


// Task Macros from: https://forum.arduino.cc/index.php?topic=415229.msg2859554

#define STEPS_cfg      4

#if(STEPS_cfg == 1)
// settings for single-step mode
# define MAXSPEED_           1000.0
# define ACCELERATION_       500.0
# define INIT_SPEED_         500.0
# define LIMIT_OFFSET_       60
#elif(STEPS_cfg == 4)
// settings for 4 microsteps mode
# define MAXSPEED_           4000.0
# define ACCELERATION_       2000.0
# define INIT_SPEED_         2000.0
# define LIMIT_OFFSET_       240
#elif(STEPS_cfg == 8)
// settings for 8 microsteps mode   // too fast, don't use
# define MAXSPEED_           8000.0
# define ACCELERATION_       4000.0
# define INIT_SPEED_         4000.0
# define LIMIT_OFFSET_       480
#elif(STEPS_cfg == 16)              // too fast, don't use
# define MAXSPEED_          16000.0
# define ACCELERATION_       8000.0
# define INIT_SPEED_         8000.0
# define LIMIT_OFFSET_       960
#else
#error STEPS_cfg is not defined or not in range
#endif

#define CAM_cfg      0    // Panasonic Lumix FZ2000/FZ2500

#if(CAM_cfg == 0)
# define TRIGGER_OPEN_     1000UL    // trigger needs 2 ms in MF mode, 500 ms in AF
# define CAM_LATENCY_       500UL    // time needed after triggering a cam
#elif(CAM_cfg == 1)                  // Dummy Cam 2
# define TRIGGER_OPEN_       20UL    // trigger needs 20 ms in MF mode, 500 ms in AF (DUMMY)
# define CAM_LATENCY_       500UL    // time between 2 shots (DUMMY)
#else
#error CAM_cfg is not defined or not in range
#endif

# define CARRIAGE_SETTLE_   500UL     // Time the slider carriage needs to rest before triggering cam

// Pins used
const uint8_t STEPPER_DIR_PIN = 6;
const uint8_t STEPPER_STEP_PIN = 5;
const uint8_t STEPPER_ENABLE_PIN = 7;
const uint8_t TRIGGER_PIN = 10;     // Camera trigger
const uint8_t LIMIT_M = 14;         // refers to A0
const uint8_t LIMIT_B = 15;         // refers to A1

//Interval/delay when/where this Timer expires
const uint32_t dummyStartInterval = 1000UL;    // 1000ms to simulate other code
const uint32_t CARRIAGE_SETTLE     = CARRIAGE_SETTLE_;
const uint32_t TRIGGER_OPEN        = TRIGGER_OPEN_;
const uint32_t CAM_LATENCY         =  CAM_LATENCY_;
const uint32_t dummyEndInterval   = 1000UL;    // 1000ms to simulate other code

bool fotoMarker = true; // Task transfer marker, starts trigger task
bool goHome = false;
bool goneHome = false;

uint8_t lang = 0;           // 0 = EN 1, 1 = DE, ...
uint8_t noOfShots = 2;
uint32_t limitBPos;         // Position just off upper limit switch
uint32_t stepWidth;
uint32_t noOfSteps = 10;
uint8_t offsetBase = 0;     // 0..100 (percent): to start from an offset instead of base
uint8_t offsetLimit = 0;    // 0..100 (percent): to reduce the upper limit
uint8_t opMode = 0;         // 0: step, 1: slide, 2: macro, 3: dollyzoom
bool homePos = 0;           // 0: Motor side, 1: Other side
bool inUse = 0;             // 0: No active process, 1: process running
bool useTrigger = 0;        // 0: No, cam's trigger used, 1: yes

// Objects
AccelStepper stepper(1, STEPPER_STEP_PIN, STEPPER_DIR_PIN); // 1 = Driver
LiquidCrystal_I2C lcd(0x27, 20, 4);

void initStepper() {
  lcd.setCursor (0, 1); lcd.print(F("*** Initializing ***"));
  stepper.enableOutputs();
  stepper.setSpeed(INIT_SPEED_);
  homeStepper();
  delay(500);
  limitStepper();
  delay(500);
  
  // Following is only for testing new limits
  // Will be moved to Loop() later
  stepper.setSpeed(-INIT_SPEED_);
  stepper.moveTo(0);
  while (stepper.currentPosition() != 0) {
    stepper.run();
  }
  delay(1000);
  stepper.moveTo(limitBPos);
  while (stepper.currentPosition() != limitBPos) {
    stepper.run();
  }  
}

void homeStepper() {
  // Always home the stepper to the motor side
  int32_t initial_homing = -1;

  lcd.setCursor (0, 2); lcd.print(F("      Homing        "));

  while (!digitalRead(LIMIT_M)) {
    stepper.moveTo(initial_homing);
    stepper.run();
    initial_homing--;
  }
  // home position reached, add a little offset to unload the switch
  stepper.setCurrentPosition(-LIMIT_OFFSET_);
}

void limitStepper() {
  // Always limit the stepper to the off-motor side
  int32_t initial_homing = 1;

  lcd.setCursor (0, 2); lcd.print(F("     Limiting       "));

  while (!digitalRead(LIMIT_B)) {
    stepper.moveTo(initial_homing);
    stepper.run();
    initial_homing++;
  }
  limitBPos = stepper.currentPosition() - LIMIT_OFFSET_;
}

void setup()
{
  Serial.begin(9600);
  Serial.println("Start");
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(TRIGGER_PIN, OUTPUT);
  pinMode(LIMIT_M, INPUT_PULLUP);
  pinMode(LIMIT_B, INPUT_PULLUP);
  stepper.setEnablePin(STEPPER_ENABLE_PIN);
  stepper.setPinsInverted(false, false, true); // invert enable pin
  stepper.setMaxSpeed(MAXSPEED_);
  stepper.setAcceleration(ACCELERATION_);

  // LCD Begin
  lcd.init();
  lcd.backlight();
  lcd.setCursor (0, 1);
  lcd.print(F("   *** Welcome ***  "));
  delay(1000);
  lcd.clear();

  initStepper();
}

void loop()
{

}

I added another test by changing the limiting function:

void limitStepper() {
 // Always limit the stepper to the off-motor side
 int32_t initial_homing = 1;

 lcd.setCursor (0, 2); lcd.print(F("     Limiting       "));

 while (!digitalRead(LIMIT_B)) {
   stepper.moveTo(initial_homing);
   stepper.run();
   initial_homing++;
 }
 
 limitBPos = stepper.currentPosition() - LIMIT_OFFSET;
 Serial.print("LimitB: ");Serial.println(limitBPos);
 stepper.moveTo(limitBPos);
 while (stepper.currentPosition() != limitBPos){
   stepper.run();
 }

 Serial.print("FinalPos: ");Serial.println(stepper.currentPosition());  
}

The result:

Start
LimitB: 8035
FinalPos: 8035

This shows, that I got LimitB correct - but during the run to the final position (stepper.moveTo(limitBPos):wink: the stepper first runs/crashes against the limit switch, and then retracts to the correct position. And I don't know why this happens.

Thank you very much for posting a short program - many don't think of doing that.

I'm finding it hard to visualize what is happening.

My guess is that you are using the AccelStepper in a strange way by updating the value of moveTo in each iteration. AFAIK it is more usual to set a destination once and then let the library make the necessary steps to get there.

If you want to move one step at a time towards an end stop then runSpeed() may be more suitable.

...R

The following code works now:

void initStepper() {
  lcd.setCursor (0, 1); lcd.print(F("*** Initializing ***"));
  stepper.enableOutputs();
  homeStepper();
  delay(1000);
  limitStepper();
  delay(1000);
  
  // Following is only for testing new limits
  // Will be moved to Loop() later
   
  stepper.moveTo(0);
  stepper.setSpeed(-INIT_SPEED_); 
  while (stepper.currentPosition() != 0) {
    stepper.runSpeed();
  }
  delay(1000);
  
  stepper.moveTo(limitBPos);
  stepper.setSpeed(INIT_SPEED_); 
  while (stepper.currentPosition() != limitBPos) {
    stepper.runSpeed();
  } 
}

void homeStepper() {
  // Always home the stepper to the motor side

  lcd.setCursor (0, 2); lcd.print(F("      Homing        "));
  stepper.moveTo(-1000000);
  stepper.setSpeed(INIT_SPEED_);
  while (!digitalRead(LIMIT_M)) {
    stepper.runSpeedToPosition();    
  }
  // home position reached, add a little offset to unload the switch
  stepper.setCurrentPosition(-LIMIT_OFFSET_);
}

void limitStepper() {
  // Always limit the stepper to the off-motor side

  lcd.setCursor (0, 2); lcd.print(F("     Limiting       "));
  stepper.moveTo(1000000);
  stepper.setSpeed(INIT_SPEED_);  

  while (!digitalRead(LIMIT_B)) {
    stepper.runSpeedToPosition();
  }
  limitBPos = stepper.currentPosition() - LIMIT_OFFSET_;
  Serial.print("LimitB Current Pos.: "); Serial.println(limitBPos);
}

Unfortunately now the negative speed setting is back (see part after the comment in initStepper(). Without the negative speed setting it goes to the wrong direction. It seems to me that any class with accelerations and decelerations has a side effect of leaving direction and speed information for the next turn, even if the final position has been reached. What I would need is a function to reset all 'outdated' stepper directives once a destination is reached.

As I don't have the hardware I can't try your code.

What is the purpose of the limitStepper() function?

...R

I'm building a camera slider. The linear axis has 2 limit switches. One on the motor side, one on the other. In setup of the machine there is an option to swap the position of the home point. Plus multiple options to add offsets to both sides of the axis. So I need both positions - and I must understand how AccelStepper works.

Blocking calls in setup are no problem at all, but I must/want avoid them in the Loop(), where my state machine is running. Homing and limiting are working perfectly now, the only problem left is going home (or elsewhere) without setting negative or positive speed.

You have loops that repeatedly call moveTo() in them.

Call moveTo once, then in the loop you just have to call run(). moveTo could be doing a lot of work.

You only need to use moveTo() and stop() to control the camera, setting the speed is then just
positive values and indicates the top speed during ramped motion. Ramping the motion is always
a good idea when you have lots of inertia in the system, so you don't jump steps or stall the stepper.

All your movements can be of the form:

set the max speed,
moveTo()
wait for completion while calling run() (or wait for limit switch, which means calling stop() on reaching it)

Pull that out of line into a function or functions so the main business logic is all high-level...

Hi Mark,

thanks for your help. The homing and limiting routines from post #5 are fine. I want this part in setup - and its working without any negative speed and without setting new positions in any kind of loop.

Now, after homing and limiting are done, I add a goHome() function as last directive in Setup(), and use the real Loop():

void goHomeTest() {

 uint32_t homePosition = limitBPos;  
 lcd.setCursor (0, 2); lcd.print(F("    Going home      "));
 stepper.enableOutputs();

 if (stepper.currentPosition() > homePosition) {
   // go back
   stepper.setSpeed(-MAXSPEED_);
 } 

 stepper.moveTo(homePosition);  
}

...
...

void loop()
{
   stepper.run();
}

Without the if-clause to determine the position (and setting negative speed), the stepper goes in the wrong direction (tripple checked).

After homing and limiting I have 2 limits, home (at 0) and limitB (at 86000). But going from 86000 to 0 does not work without a negative speed. Thats strange.

Oh what a day. After 2 days of struggle I found a small note about possible issues when using fixed speed functions (like my old homing and limiting routines in setup) in a row with acceleration and deceleration functions (like in my old Loop()).

Therefore I wrote a non-breaking task which puts everything into the Loop(), and only uses acceleration and deceleration code. No more need for negative speeds, and even positioning at the end runs like charme.

Another problem was 'stepper.stop'. It does not work as expected, and seems to stop with deceleration (crashing into the limits). After I used 'stepper.setSpeed(0.0)' instead, the carriage came to an instant stop.

I only post the relevant sections of my code. Maybe it helps someone.

#include <TaskMacro.h>    // This is required for my init task (just 10 lines of code)
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>

bool initMarker = true;     // Task transfer marker, starts init process
bool initComplete = false;  // Task transfer marker, starts processes after init

// Objects
AccelStepper stepper(1, STEPPER_STEP_PIN, STEPPER_DIR_PIN); // 1 = Driver
LiquidCrystal_I2C lcd(0x27, 20, 4);

Task initMachine()
{
  taskBegin();
  while (1)
  {
    // Wait for init request marker
    taskWaitFor(initMarker);
    Serial.println("Init process started");
    lcd.setCursor (0, 1); lcd.print(F("*** Initializing ***"));
    stepper.enableOutputs();
    stepper.setSpeed(INIT_SPEED_);
    lcd.setCursor (0, 2); lcd.print(F("      Homing        "));
    Serial.println("Homing process started");
    stepper.moveTo(-1000000);

    taskWaitFor(digitalRead(LIMIT_M));
    stepper.setSpeed(0.0);    // This seems to work, stepper.stop() doesn't
    stepper.setCurrentPosition(-LIMIT_OFFSET_);
    Serial.println("Homing complete");
    Serial.println("Limiting process started");
    lcd.setCursor (0, 2); lcd.print(F("      Limiting       "));
    stepper.moveTo(1000000);

    taskWaitFor(digitalRead(LIMIT_B));
    stepper.setSpeed(0.0);    // This seems to work, stepper.stop() doesn't
    limitBPos = stepper.currentPosition() - LIMIT_OFFSET_;
    // don't do init again    
    initMarker = false;
    Serial.print("Limiting complete");
    lcd.setCursor (0, 2); lcd.print(F("        Ready        "));

    // Now go home to finalize
    stepper.moveTo(0);
    taskWaitFor(stepper.currentPosition() == 0);
    lcd.setCursor (0, 2); lcd.print(F("        At Home      "));
    // set a marker so that other code can run
    initComplete = true;

    // Just for testing, move to the other end of axis
    taskPause(1000);
    stepper.moveTo(limitBPos);
    taskWaitFor(stepper.currentPosition() == limitBPos);
    lcd.setCursor (0, 2); lcd.print(F("       At LimitB    "));
  }
  taskEnd();
}

void setup()
{
  Serial.begin(9600);
  Serial.println("Start");
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(TRIGGER_PIN, OUTPUT);
  pinMode(LIMIT_M, INPUT_PULLUP);
  pinMode(LIMIT_B, INPUT_PULLUP);
  stepper.setEnablePin(STEPPER_ENABLE_PIN);
  stepper.setPinsInverted(false, false, true); // invert enable pin
  stepper.setMaxSpeed(MAXSPEED_);
  stepper.setAcceleration(ACCELERATION_);

  // LCD Begin
  lcd.init();
  lcd.backlight();
  lcd.setCursor (0, 1);
  lcd.print(F("   *** Welcome ***  "));
  delay(1000);
  lcd.clear();
}

void loop() {
  initMachine();  
  stepper.run();
}

Thanks again for your help!

Demokrit:
Another problem was 'stepper.stop'. It does not work as expected, and seems to stop with deceleration (crashing into the limits).

While the AccelStepper documentation is a great deal better than for many other Arduino libraries there is still a lot of room for improvement. A high-level overview would be very useful.

...R

The documentation is correct, but omits some (important) details.

stop(): Sets a new target position that causes the stepper to stop as quickly as possible, using the current speed and acceleration parameters.

What new target position will be set?
A better name for 'stop()' would be 'stopSoon' :slight_smile:

Now I'll have some analogue fun with a Glenfiddich and an open fire.

Demokrit:
Now I'll have some analogue fun with a Glenfiddich and an open fire.

Would flicking it into the fire to make small conflagrations both be wasteful and
technically digit-al?

I recommend a virtual tour of Islay using half a dozen malts and Google Earth!