Convert code from Stepper.h to AccelStepper.h

Good Day
A bit newish to coding. Topic might be a little vague but what I would like to know or get help with is being able to take a code that utilizes the regular Stepper library and replace it with the AccelStepper library. I have a code that allows for the opening and closing of a DIY flat panel for my telescope. The code works fine, I just wanted to be able to set the home position of the panel when it is initially powered up or reset. That part works great. The part I am having trouble with is in the loop. When I press the "Open" button I would like the panel to accelerate to its position and vise versa. Right now, I am kinda of using both libraries but I would to use AccelStepper excusively. I would have attached the .ino but being that I am a new user here, I cannot so I just copied and pasted. Apologies.

#include <Stepper.h>
#include "AccelStepper.h" 

#define STEPS 20000 // steps per revolution for the motor
#define RPMS 150 // desired motor speed

#define home_switch 9

Stepper stepper(STEPS, 5, 3); // Blue wire (Arduino D5) goes to the TB6600 stepper driver DIR- pin. RED wire (Arduino D3) goes to PUL- of TB6600 stepper driver. Black wire loops from DIR+ to PUL+ and goes to the 5V of Arduino

AccelStepper stepperX(1, 5, 3);
long initial_homing=-1;

volatile int ledPin = 6;      // the pin that the LED is attached to, needs to be a PWM pin.
int brightness = 0;

enum devices {
  FLAT_MAN_L = 10,
  FLAT_MAN_XL = 15,
  FLAT_MAN = 19,
  FLIP_FLAT = 99
};

enum motorStatuses {
  STOPPED = 0,
  RUNNING
};

enum lightStatuses {
  OFF = 0,
  ON
};

enum shutterStatuses {
  NEITHER_OPEN_NOR_CLOSED = 0, // ie not open or closed...could be moving
  CLOSED,
  OPEN,
  TIMED_OUT
};

enum motorDirection {
  OPENING = 0,
  CLOSING,
  NONE
};


int deviceId = FLIP_FLAT; //set this to FLAT_MAN if you want to remove or not use the motor handling
int motorStatus = STOPPED;
int lightStatus = OFF;
int coverStatus = CLOSED;
float targetAngle = 0.0;
float currentAngle = 0.0;
int motorDirection = NONE;

void setup() {
  // initialize the serial communication:
  Serial.begin(9600);
  // initialize the ledPin as an output:
  pinMode(ledPin, OUTPUT);
  
  pinMode(home_switch, INPUT_PULLUP);
  
  analogWrite(ledPin, 0);
  //stepper.setSpeed(RPMS);

  stepperX.setMaxSpeed(6000.0);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepperX.setAcceleration(5000.0);  // Set Acceleration of Stepper

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

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

  while (!digitalRead(home_switch)) { // Make the Stepper move CW until the switch is deactivated
    stepperX.moveTo(initial_homing);  
    stepperX.run();
    initial_homing++;
   // delay(1);
  }
  
  stepperX.setCurrentPosition(0);
  //Serial.println("Homing Completed");
  //Serial.println("");
  stepperX.setMaxSpeed(5000.0);      // Set Max Speed of Stepper (Faster for regular movements)
  stepperX.setAcceleration(3000.0);  // Set Acceleration of Stepper
  
  //stepper.setSpeed(RPMS);
}


void loop() {
  handleSerial();
  handleMotor();
}


void handleSerial() {
  if ( Serial.available() >= 6 ) { // all incoming communications are fixed length at 6 bytes including the \n
    char* cmd;
    char* data;
    char temp[10];

    int len = 0;

    char str[20];
    memset(str, 0, 20);
    Serial.readBytesUntil('\r', str, 20);

    cmd = str + 1;
    data = str + 2;

    // useful for debugging to make sure your commands came through and are parsed correctly.
    if ( false ) {
      sprintf( temp, "cmd = >%c%s\n", cmd, data);
      Serial.write(temp);
    }

    switch ( *cmd )
    {
      /*
        Ping device
        Request: >POOO\r
        Return : *PiiOOO\n
        id = deviceId
      */
      case 'P':
        sprintf(temp, "*P%dOOO\n", deviceId);
        Serial.write(temp);
        break;

      /*
        Open shutter
        Request: >OOOO\r
        Return : *OiiOOO\n
        id = deviceId
        This command is only supported on the Flip-Flat, set the deviceId accordingly
      */
      case 'O':
        sprintf(temp, "*O%dOOO\n", deviceId);
        setShutter(OPEN);
        Serial.write(temp);
        break;


      /*
        Close shutter
        Request: >COOO\r
        Return : *CiiOOO\n
        id = deviceId
        This command is only supported on the Flip-Flat, set the deviceId accordingly
      */
      case 'C':
        sprintf(temp, "*C%dOOO\n", deviceId);
        setShutter(CLOSED);
        Serial.write(temp);
        break;

      /*
        Turn light on
        Request: >LOOO\r
        Return : *LiiOOO\n
        id = deviceId
      */
      case 'L':
        sprintf(temp, "*L%dOOO\n", deviceId);
        Serial.write(temp);
        lightStatus = ON;
        analogWrite(ledPin, brightness);
        break;

      /*
        Turn light off
        Request: >DOOO\r
        Return : *DiiOOO\n
        id = deviceId
      */
      case 'D':
        sprintf(temp, "*D%dOOO\n", deviceId);
        Serial.write(temp);
        lightStatus = OFF;
        analogWrite(ledPin, 0);
        break;

      /*
        Set brightness
        Request: >Bxxx\r
         xxx = brightness value from 000-255
        Return : *Biiyyy\n
        id = deviceId
         yyy = value that brightness was set from 000-255
      */
      case 'B':
        brightness = atoi(data);
        if ( lightStatus == ON )
          analogWrite(ledPin, brightness);
        sprintf( temp, "*B%d%03d\n", deviceId, brightness );
        Serial.write(temp);
        break;

      /*
        Get brightness
        Request: >JOOO\r
        Return : *Jiiyyy\n
        id = deviceId
         yyy = current brightness value from 000-255
      */
      case 'J':
        sprintf( temp, "*J%d%03d\n", deviceId, brightness);
        Serial.write(temp);
        break;

      /*
        Get device status:
        Request: >SOOO\r
        Return : *SidMLC\n
        id = deviceId
         M  = motor status( 0 stopped, 1 running)
         L  = light status( 0 off, 1 on)
         C  = Cover Status( 0 moving, 1 closed, 2 open, 3 timed out)
      */
      case 'S':
        sprintf( temp, "*S%d%d%d%d\n", deviceId, motorStatus, lightStatus, coverStatus);
        Serial.write(temp);
        break;

      /*
        Get firmware version
        Request: >VOOO\r
        Return : *Vii001\n
        id = deviceId
      */
      case 'V': // get firmware version
        sprintf(temp, "*V%d003\n", deviceId);
        Serial.write(temp);
        break;
    }

    while ( Serial.available() > 0 ) {
      Serial.read();
    }
  }
}

void setShutter(int val)
{
  if ( val == OPEN && coverStatus != OPEN )
  {
    motorDirection = OPENING;
    targetAngle = 4500.0;
  }
  else if ( val == CLOSED && coverStatus != CLOSED )
  {
    motorDirection = CLOSING;
    targetAngle = 0.0;
  }
}

void handleMotor()
{
  if (currentAngle < targetAngle && motorDirection == OPENING) {
    motorStatus = RUNNING;
    coverStatus = NEITHER_OPEN_NOR_CLOSED;
    stepper.step(1);
    currentAngle = currentAngle + 360.0 / STEPS;
    if (currentAngle >= targetAngle) {
      motorStatus = STOPPED;
      motorDirection = NONE;
      coverStatus = OPEN;
    }
  } else if (currentAngle > targetAngle && motorDirection == CLOSING) {
    motorStatus = RUNNING;
    coverStatus = NEITHER_OPEN_NOR_CLOSED;
    stepper.step(-1);
    currentAngle = currentAngle - 360.0 / STEPS;
    if (currentAngle <= targetAngle) {
      motorStatus = STOPPED;
      motorDirection = NONE;
      coverStatus = CLOSED;
    }
  }
}

The motor being used is a https://www.amazon.com/gp/product/B00QEWAL98/ref=ppx_yo_dt_b_asin_title_o09_s00?ie=UTF8&psc=1
I am using a TB6600 stepper driver because happened to have one and I suspect I don't need to #define STEPS. Not quite certain.
Like stated above, what can I do in the loop to get it to use AccelStepper instead of the regular stepper library?

Thanks for taking the time, good, bad or indeiffernt.

Welcome to the forum.

You should have shown your code earlier :grimacing:

The AccelStepper library is an improvement, but it is not the perfect stepper library. It uses the 'stepper.run()' function to do the steps. As you can see in the bounce example.
That means that the 'stepper.run()' must be executed as often as possible to avoid waiting too long to make a step, so the loop() should have no delay() and should run as often as possible.
It helps if you can imagine the loop() making a buzzing sound because it executes so often. Bzzzzzz. The better the buzzing sound, the less hiccups for the stepper motor.

Reading the Serial input is less important.
In the loop() you do a 'Serial.readBytesUntil()'. That function waits until the '\r' character is found or until the timeout of 1 second has finished. We prefer to write our own code to read the serial input.

Would you be willing to remove all the sprintf() functions ?
Your code:

sprintf( temp, "*S%d%d%d%d\n", deviceId, motorStatus, lightStatus, coverStatus);
Serial.write(temp);

Code that can hardly go wrong:

Serial.print("*S");
Serial.print(deviceId);
Serial.print(motorStatus);
Serial.print(lightStatus);
Serial.print(coverStatus);
Serial.println();

The problem with sprintf() is the size of the destination buffer and pointers and '%c'.
Go in the Arduino IDE to the preferences, and set compiler warnings to "All". Then you see that it complains about the "%c".

Suppose you have this:

sprintf(buffer, "%d:%d:%d", hours, minutes, seconds);

Someone might think that the largest output is "24:59:59" plus zero-terminator makes 9 bytes.
In real life, a integer can take 7 character when it is "-30000". So the largest output is "-30000:-30000:-30000" plus zero-terminator makes 22 bytes. But it is only 22 bytes if I counted them correctly, so the real number is somewhere between 20 and 30.
With an extra safety marging, that makes 40 bytes.

char buffer[40];
sprintf(buffer, "%d:%d:%d", hours, minutes, seconds);

Using either one step forward or backward in the loop() is okay for the accelstepper.

Can you make a few test-sketches, try a few examples of the AccelStepper library to learn how to use it.

Hi.
Thanks for replying. The void handleserial() works as it should, just need to change the "O's" into zeros in order for it to work properly under RPI, which it does. Where my problem lies is within the void setShutter and void handleMotor. The code works as it is but i wanted to use AccelStepper within the void handleMotor portion so I can get that acceleration going. I'm having a hard time translating from one to the other. When the motor has set it's initial home postion, I would like this to be called by the handleMotor and go to a predefined position when the OPEN button is pressed and vise versa when pushing CLOSED. I know, " why fix it if it ain't broke"? It's been cloudy, nothing better to do but try to imporove on something plus, I like the sound the motor makes when opening and closing via AccelerStepper. If it's not possible then I guess I'm stuck with plain ol' motor open and close. :crazy_face:

At this point it does not matter what you want the sketch to do.
If you overwrite other variables, or mess up the stack, or have a bug that can cause a lot of trouble, then you have to fix that.

My goal is that you have a working project.

Introducing bugs, you are right on that. In the void setup I added a bit of code in there for homing the motor at start up. I was excited to get that working because I often have to change orientation of my scope when I add or remove hardware thus changing balance. I would have to manually re-align the dust cover and that was a pain. I thought, "why not have the loop work the same in terms of speed and acceleration. I like the thought of slowing down the motor so it can gently come to rest plus I also like the idea of reducing the time it takes to open and close. But I guess it won't work so I'll just leave it as is for now. Right now, I have a bench test going on a non-geared NEMA 23 with a DM 556 driver and having some issues with that and an unmodified version of the same code. By the way the bit of code I inserted into the void setup came from this:

/*  Motor Homing code using AccelStepper and the Serial Monitor
 
Created by Yvan / https://Brainy-Bits.com
This code is in the public domain...
You can: copy it, use it, modify it, share it or just plain ignore it!
Thx!

*/

#include "AccelStepper.h" 
// Library created by Mike McCauley at http://www.airspayce.com/mikem/arduino/AccelStepper/

// AccelStepper Setup
AccelStepper stepperX(1, 2, 3);   // 1 = Easy Driver interface
                                  // NANO Pin 2 connected to STEP pin of Easy Driver
                                  // NANO Pin 3 connected to DIR pin of Easy Driver

// Define the Pins used
#define home_switch 9 // Pin 9 connected to Home Switch (MicroSwitch)

// Stepper Travel Variables
long TravelX;  // Used to store the X value entered in the Serial Monitor
int move_finished=1;  // Used to check if move is completed
long initial_homing=-1;  // Used to Home Stepper at startup


void setup() {
   Serial.begin(9600);  // Start the Serial monitor with speed of 9600 Bauds
   
   pinMode(home_switch, INPUT_PULLUP);
   
   delay(5);  // Wait for EasyDriver wake up

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

// Start Homing procedure of Stepper Motor at startup

  Serial.print("Stepper is Homing . . . . . . . . . . . ");

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

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

  while (!digitalRead(home_switch)) { // Make the Stepper move CW until the switch is deactivated
    stepperX.moveTo(initial_homing);  
    stepperX.run();
    initial_homing++;
    delay(5);
  }
  
  stepperX.setCurrentPosition(0);
  Serial.println("Homing Completed");
  Serial.println("");
  stepperX.setMaxSpeed(1000.0);      // Set Max Speed of Stepper (Faster for regular movements)
  stepperX.setAcceleration(1000.0);  // Set Acceleration of Stepper

// Print out Instructions on the Serial Monitor at Start
  Serial.println("Enter Travel distance (Positive for CW / Negative for CCW and Zero for back to Home): ");
}

void loop() {

 while (Serial.available()>0)  { // Check if values are available in the Serial Buffer

  move_finished=0;  // Set variable for checking move of the Stepper
  
  TravelX= Serial.parseInt();  // Put numeric value from buffer in TravelX variable
  if (TravelX < 0 || TravelX > 1350) {  // Make sure the position entered is not beyond the HOME or MAX position
    Serial.println("");
    Serial.println("Please enter a value greater than zero and smaller or equal to 1350.....");
    Serial.println("");
  } else {
    Serial.print("Moving stepper into position: ");
    Serial.println(TravelX);
  
  stepperX.moveTo(TravelX);  // Set new moveto position of Stepper
  
  delay(1000);  // Wait 1 seconds before moving the Stepper
  }
  }

  if (TravelX >= 0 && TravelX <= 1350) {

// Check if the Stepper has reached desired position
  if ((stepperX.distanceToGo() != 0)) {
    
    stepperX.run();  // Move Stepper into position
    
  }

// If move is completed display message on Serial Monitor
  if ((move_finished == 0) && (stepperX.distanceToGo() == 0)) {
    Serial.println("COMPLETED!");
    Serial.println("");
     Serial.println("Enter Travel distance (Positive for CW / Negative for CCW and Zero for back to Home): ");
    move_finished=1;  // Reset move variable
  }
  }
}

The above code allows you to input a value into the serial monitor. I was just trying to see if there were a way to have a pre-difined positions that will execute when the OPEN button is pushed and likewise when CLOSED is pushed. Just want to make it possible to use acceleration and what not like in the void setup.

here is how you can deal with setShutter() ann handleMotor() with the AccelStepper library. @Koepel has already commented on the serial input routine...

Basically, start moving when you get a shutter command and then check if you are done every time through handleMotor()

void setShutter(int val)
{
  if ( val == OPEN )
  {
    motorDirection = OPENING;
    coverStatus = NEITHER_OPEN_NOR_CLOSED;
    stepperX.moveTo(openPosition);
  }
  else if ( val == CLOSED )
  {
    motorDirection = CLOSING;
    coverStatus = NEITHER_OPEN_NOR_CLOSED;
    stepperX.moveTo(closedPosition);
  }
}

void handleMotor()
{
  if (stepperX.distanceToGo() ) {
    // still moving
    motorStatus = MOVING;
    stepperX.run();
  }
  else {
    // done moving, figure out where we are
    motorStatus = STOPPED;
    if ( motorDirection == OPENING ) {
      coverStatus = OPEN;
      motorDirection = NONE;
    }
    else if ( motorDirection == CLOSING ) {
      coverStatus = CLOSED;
      motorDirection = NONE;
    }
  }
}

And, rather than doing all that floating point math for angles, just use steps...

const long openPosition = 4500.0 * STEPS / 360.0;   // figure out step count, not angles
const long closedPosition = 0;  // by design, closed == HOME

The stepper.run(); can be called in the loop() all the time. It is more a "update" function. The AccelStepper library decides what to do.

Other libraries that are built around millis() use such a function. It is mostly called "update".

Thanks
I'll give that a whirl later after I feed my grand child

It is working the way I would like it to. Thank you very much for offering guidence. Now it's time to take it off bench test mode and put it into working mode. Again, thanks.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.