Stepper motor control through serial window

Hi!

I’ve been working on this piece of code for a few days now, and I can’t seem to get it to work.

I’m using the Big Easy Driver to driver a NEMA14 stepper motor. My goal is to make a Qt interface that allows me to control the motor. However, to facilitate debugging, my starting point is to be able to control how many steps the motor takes through the arduino IDE’s serial window.

Here’s my problem: The code works fine whenever I use the part of the code that allows me to choose how many steps to take. However, the part where it only moves plus or minus one step doesn’t work like I want it to.

I want it to move one step forward whenever I enter ‘P’, or one step backward whenever I enter ‘M’. As long as I keep feeding it 'P’s or 'M’s, it should keep moving one step at a time.

However, the behavior I see is this: I can move one step further or one step backwards from the starting position, but no more than that. I can’t move two steps in a direction.

The code is a bit messy right now due to me trying a whole bunch of different methods. I really don’t get where the problem is from. Any help would be much appreciated!

Thanks!

stepperTest.ino (1.25 KB)

I think you are using stepper.run() incorrectly. Try .runToPosition()

Stepper.run() is intended for non-blocking moves. It it is normally called as the last thing in loop() and loop() should be designed so that it repeats very quickly - much quicker than the step speed. When AccelStpper receives a call to .run() it checks if it is time for a move and if it is not, it does nothing. As you are only calling it once I hope you can see why it probably does not work.

Of course it might be better to reorganize your code so that is uses stepper.run().

...R

Yes, just move the .run() call to top-level inside the loop() function so it is called very frequently -
the AccelStepper library is not interrupt driven, you have to allow it to sample the time often enough
to sequence its output steps.

Get rid of the call to delay(), this is bad. For your 'B' command you'll need to add some state:

boolean in_B_command = false ;

void loop ()
{
  if (Serial.available () > 0)
  {
    char ch = Serial.read () ;
    if (in_B_command)    // state from last read
    {
      dist = Serial.read () - '0' ;
      nbreSteps += round(dist*convDist);
      stepper.moveTo (nbreSteps);    // only use moveTo() and run() - much the easiest way to use library
      in_B_command = false ;   // change state back
    }
    else if (ch == 'B')
    {
      in_B_command = true ;  // change state
    }
    else if (ch == 'P')
    {
      nbreSteps ++ ;
      rstepper.moveTo (nbreSteps) ;
    }
    else if (ch == 'M')
    {
      nbreSteps -- ;
      stepper.moveTo (nbreSteps) ;
    }
  }
  stepper.run () ;   // must be called often
}

Actually a quick addendum - its often much clearer to split each action out of loop() as a
separate function so everything is independent and uncluttered:

void loop ()
{
  if (Serial.available () > 0)   // deal with any incoming serial
    handle_serial (Serial.read ()) ;
  ....
  ....
  stepper.run () ;    // keep stepper happy
}


// All input parsing happens here with no calls to delay():
void handle_serial (char ch)
{
  static boolean in_B_command = false ;   // nicer to use a locally scoped static for the state like this
  switch (ch)   // switch statements always good for this
  {
  case 'B':
     ....
     break ;
  case 'P':
     ....
    break ;
  }
}

Thanks a lot for the replies guys, I'll try that!

However, I thought run() only move one step per call - What would my code look like if I wanted it to be blocking one move the required number of steps before moving to the next action?

Sorry if this is a bit basic, I'm still quite new to microcontrollers :slight_smile:

After trying your piece of code, I had the same problem as before : I could only move 1 step around my starting position.

I've found a workaround though: I input an extra byte after the 'M' or the 'P' that's equal to 1, read it and then add it to my number of steps (Like I do for custom distances, but without the distance conversion). For some reason this works while the other method doesn't.

Deathlymonkey:
For some reason this works while the other method doesn't.

You need to post your latest code.

However, I thought run() only move one step per call

It may move one step - if it is time for a step based on the selected speed. Obviously it won't move 2 steps, but it might move 0 steps.

...R

Deathlymonkey:
Thanks a lot for the replies guys, I’ll try that!

However, I thought run() only move one step per call - What would my code look like if I wanted it to be blocking one move the required number of steps before moving to the next action?

Sorry if this is a bit basic, I’m still quite new to microcontrollers :slight_smile:

You must call run() as often as possible for good pulse timing. Yes it will only ever
generate one pulse, but normally generates no pulses. Its the timing of the pulses
that matters, so you have to call it very frequently - hence putting it in loop()