[SOLVED] Changing stepmotor step resolution with button

Hi guys, im new here and with arduino stuff i need some help.
I've been into a project making a camera slider

its running great with the joystick but i want to change the motor step resolution with a simple button with 3 cases like 1-full step 2- half step 3-quarter step.

im using arduino uno with the drv8825

my code is a bit mess and i have no clue what im doing

This slider runs from side to side with a slide pot controlling it.
It goes faster the further i push the slider pot.
There are limit switches on both ends to prevent further traveling of the sliding plate.
I think its not possible to have delay on loop because of convertAnalogToSpeed part, please correct me.

#include "stepLib.h";
#include "smoothJoystick.h";

//not in use #define  LEFT_PIN  4
//not in use #define  STOP_PIN  5
//not in use #define  RIGHT_PIN 6
#define Limit01 2
#define Limit02 3
#define joystickPin A1 // analog pin A1 (27) 
int msstep = 7;     // Pin 7 pushbutton
int MS0 = 10;      // Pin 10 connected to MS0 pin
int MS1 = 11;      // Pin 11 connected to MS1 pin 
int MS2 = 12;      // pin 12 conected to MS2 pin
int val;
int buttonState =0;
int stepmode = 0;

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

#define maxSpeed 4000 // speed measured in Hz
#define minSpeed 1 // speed measured in Hz

#define deadband 50 // deadband for consideration of 0 speed

// define our step pins
# define sliderStep 9 // 9

// define our direction pins
# define sliderDir 8 // 8

// instantiate out objects
stepMotor slider(sliderStep, sliderDir); // stepper motor
joystick joy(joystickPin, 512); // declare a new instance of a joystick on our joystickPin, let's initialize our buffer to center joystick value: 512




void setup() {
  Serial.begin(115200);
//not in use   pinMode(LEFT_PIN, INPUT_PULLUP);
//not in use   pinMode(STOP_PIN, INPUT_PULLUP);
//not in use   pinMode(RIGHT_PIN, INPUT_PULLUP);
  pinMode(msstep, INPUT_PULLUP);
  pinMode(Limit01, INPUT);
  pinMode(Limit02, INPUT);
  pinMode(sliderDir, OUTPUT);
  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
  
}

void loop() {
  int buttonState = digitalRead(msstep);
  val = digitalRead(msstep);
  if (val == HIGH) {
    if (val != buttonState) {          // the button state has changed!
      if (val == HIGH) {                // check if the button is pressed
        if (stepmode == 0) {          // if its off
          stepmode = 1;               // microstep full step!
        } else {
          if (stepmode == 1) {        // if its all-on
            stepmode = 2;             // microstep 1/8 step!
          } else {
            if (stepmode == 2) {      // if its blinking
              stepmode = 0;           // microstep 1/16!
            }
          }
        }
      }
    }
  buttonState = val;

      }
     
      if (stepmode == 0) {
        digitalWrite (MS0, LOW);
        digitalWrite (MS1, LOW);
        digitalWrite (MS2, LOW);
      } 
      if (stepmode == 1) {
        digitalWrite (MS0, HIGH);
        digitalWrite (MS1, HIGH);
        digitalWrite (MS2, LOW);
      }
      if (stepmode == 2) {
        digitalWrite (MS0, LOW);
        digitalWrite (MS1, LOW);
        digitalWrite (MS2, HIGH);
    }

  
  unsigned int smoothAnalogVal = joy.smoothRead(); // get our smoothed analog value
  signed int stepperSpeed = convertAnalogToSpeed(smoothAnalogVal); // convert our analog value to a stepper speed with direction
  Serial.println(stepperSpeed);
  slider.step(stepperSpeed); // request a step with our joystick driven speed

}

// this function takes as an input, an unsigned analog value and will convert it into a signed value which represents stepper speed
// the output is bounded by a min and max stepper speed
signed int convertAnalogToSpeed(unsigned int analogVal) {

  if (analogVal < 512 + deadband && analogVal > 512 - deadband) { // we are within deadband, return a 0 speed
    return 0;
  }

    if (analogVal >= 512 + deadband) { // move in forward direction - return a positive speed. joyLeft
      if (!digitalRead(Limit01)) {
    return 0; // check if limit switch is activated
  }
  else {
      return map(analogVal, 512 + deadband, 1024, minSpeed, maxSpeed); // interpolate our joystick value to the proper speed
    }
  }
    if (analogVal <= 512 + deadband) { // move in reverse direction - return a negative speed. joyRight
      if (!digitalRead(Limit02)) {
    return 0; // check if limit switch is activated
  }
  else { 
      return -map(analogVal, 512 - deadband, 0, minSpeed, maxSpeed); // interpolate our joystick value to the proper speed
    }
  }
}

I've been over this for days, im preety sure its very simple but i just cant get it.
Im like ZERO experience with coding arduino.
If someone could help i would apreciate. Thanks

Hi

In the setup section of your code, you have not enabled MS0

Your code :

void setup() {
  Serial.begin(115200);
//not in use   pinMode(LEFT_PIN, INPUT_PULLUP);
//not in use   pinMode(STOP_PIN, INPUT_PULLUP);
//not in use   pinMode(RIGHT_PIN, INPUT_PULLUP);
  pinMode(msstep, INPUT_PULLUP);
  pinMode(Limit01, INPUT);
  pinMode(Limit02, INPUT);
  pinMode(sliderDir, OUTPUT);
  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
 
}

try adding pinMode(MS0, OUTPUT);

May I ask why you would want to change micro-stepping? Generally micro-stepping is used at slower speeds to smooth out the transition detente-detente. With a camera slider I would think that you're never going to be operating very fast and would therefore use the highest micro-step available for the smoothest ride. I vary the micro-step in my CNC machines as the speed increases, but this reduces to full step at high speed in order to maximize the pulse width. Please explain.

If I may put my own two cents worth...

It may be the case that changing micro-stepping mode adds additional accuracy and/or smoothness, based on the use at the time.

At lower stepping modes (ie full/half step), low speed movements may become quite jerky. If capturing is happening while moving the camera, this would come through in the footage/capture.

Also, higher resolution would assist in position accuracy. If taking still shots (especially in macro photography) a fraction of a mm becomes quite important to frame/focus a subject.

Being able to switch between stepping modes adds a lot of versatility. Lower stepping modes to get into approx position quickly, higher stepping modes for accuracy/smoothness of motion.

Just my take :slight_smile:

darrob:
If I may put my own two cents worth...

It may be the case that changing micro-stepping mode adds additional accuracy and/or smoothness, based on the use at the time.

At lower stepping modes (ie full/half step), low speed movements may become quite jerky. If capturing is happening while moving the camera, this would come through in the footage/capture.

Also, higher resolution would assist in position accuracy. If taking still shots (especially in macro photography) a fraction of a mm becomes quite important to frame/focus a subject.

Being able to switch between stepping modes adds a lot of versatility. Lower stepping modes to get into approx position quickly, higher stepping modes for accuracy/smoothness of motion.

Just my take :slight_smile:

I'll give you 3 out of 4 on that. A common mis-perception vis-a-vis micro-stepping in with positional accuracy. While it is very true that smoothness increases, the fractional currents uses in micro stepping, other than 2x (0.707, 0.707) are so one-sided that holding torque all but disappears at higher rates. If there is any load on the motor, especially the smaller frames, it is almost impossible to hold any intermediate position. My experience is limited to CNC machines and you simply cannot plan on an end-stop position to fall outside the native resolution of the motor (except for the 2x ustep). It may be that with a camera pan there is very little load and you might get away with it, but if that's what's important, you'd be far better off with a gearbox and full stepping, especially if speed in not an issue.

DKWatson:
I'll give you 3 out of 4 on that. A common mis-perception vis-a-vis micro-stepping in with positional accuracy. While it is very true that smoothness increases, the fractional currents uses in micro stepping, other than 2x (0.707, 0.707) are so one-sided that holding torque all but disappears at higher rates. If there is any load on the motor, especially the smaller frames, it is almost impossible to hold any intermediate position.

I cannot claim any real experience with this except with the project I'm working on at the moment. A macro focusing rail. However I have noticed that if you're able to bring the camera to rest where you want, it won't move by itself. This is because there is no external force (other than gravity at extreme positional angles) acting on the camera. Unlike CNC where there may be machinery pushing on the dolly while doing cutting etc.

DKWatson:
My experience is limited to CNC machines and you simply cannot plan on an end-stop position to fall outside the native resolution of the motor (except for the 2x ustep). It may be that with a camera pan there is very little load and you might get away with it, but if that's what's important, you'd be far better off with a gearbox and full stepping, especially if speed in not an issue.

I think it also depends on how you're moving the platform around. On the project I'm working with, I'm using a t8 screw to move the dolly backwards and forwards. The thread pitch it very low (2mm) so I can attach the camera to the dolly and hold the rig with the camera pointed to the ground, and the camera will not pull the dolly down unless I start turning the screw.

Getting the camera moving up at that angle, however, is another matter :slight_smile:

Using a system which utilises a pulley system might be more susceptible, but I would think that in most circumstances they would be horizontal (ie parallel to the ground) where gravity is not a factor.

It's getting the platform moving in the first place that current becomes an issue.

darrob:
It's getting the platform moving in the first place that current becomes an issue.

This is where an acceleration profile plays a critical role. These are closely guarded industry secrets (making one better than the other??) if positional accuracy and multi-axis sync are important. They generally involve integral and/or log functions and Taylor Series approximations. It gets ugly. If not so important, there are several available for public consumption. There's a library out there (somewhere) called Smoothstep that does a reasonable job. The algorithm is quite slow so it's not particularly useful at higher speeds, but it can get you moving. I don't have a link, but I did my own implementation of it and tested. (See attached). Once you get up to the bottom end of the pull-in torque curve, you can move away from it and implement whatever other profile you choose.

Smoothstep Interpolation Example.pdf (57.1 KB)

I just did a quick Google and there's actually a good Wikipedia article here.

DKWatson:
This is where an acceleration profile plays a critical role. These are closely guarded industry secrets (making one better than the other??) if positional accuracy and multi-axis sync are important. They generally involve integral and/or log functions and Taylor Series approximations. It gets ugly. If not so important, there are several available for public consumption. There's a library out there (somewhere) called Smoothstep that does a reasonable job. The algorithm is quite slow so it's not particularly useful at higher speeds, but it can get you moving. I don't have a link, but I did my own implementation of it and tested. (See attached). Once you get up to the bottom end of the pull-in torque curve, you can move away from it and implement whatever other profile you choose.

Thanks for the PDF.

The whole mess of acceleration is what started my thread on integer division speed. I've got a stepper driver which is quite nice (TMC260), but the library provided offers no acceleration. just "move the motor x steps at this speed"
I then started to use AccelStepper library, but found that there are some problems.

I do like the last line in the PDF ... "The trick now is to find a way to mathematically optimize it"

Thanks again :slight_smile:

So I needed to develop my own acceleration ramp profile algorithm, which will work with the stepper driver I have.

What fun :slight_smile:

coupled with that... I am a bear with very little brain. Some of the explanations of acceleration curve profiles very rapidly develop into a polyglot of maths-speak that I have difficulty in understanding. When Maths starts using curly symbols or greek letters, I run screaming....

darrob:
I run screaming....

In Greek I hope.

Socras:
my code is a bit mess and i have no clue what im doing

I have tidied up the code in the first part of loop() as below. There seemed to be a lot of unnecessary stuff. And I think the line to retain the previous value of the button was in the wrong place.

void loop() {
    buttonState = val;  // preserve the previous value
    val = digitalRead(msstep);
    if (val == HIGH) {
        if (val != buttonState) {          // the button state has changed!
            if (stepmode == 0) {          // if its off
                stepmode = 1;               // microstep full step!
            } 
            else if (stepmode == 1) {        // if its all-on
                    stepmode = 2;             // microstep 1/8 step!
            } 
            else {     // if its blinking
                stepmode = 0;           // microstep 1/16!
            }
        }
    }

         
    if (stepmode == 0) {
        digitalWrite (MS0, LOW);
        digitalWrite (MS1, LOW);
        digitalWrite (MS2, LOW);
    } 
    if (stepmode == 1) {
        digitalWrite (MS0, HIGH);
        digitalWrite (MS1, HIGH);
        digitalWrite (MS2, LOW);
    }
    if (stepmode == 2) {
        digitalWrite (MS0, LOW);
        digitalWrite (MS1, LOW);
        digitalWrite (MS2, HIGH);
    }

    
    unsigned int smoothAnalogVal = joy.smoothRead(); // get our smoothed analog value
    signed int stepperSpeed = convertAnalogToSpeed(smoothAnalogVal); // convert our analog value to a stepper speed with direction
    Serial.println(stepperSpeed);
    slider.step(stepperSpeed); // request a step with our joystick driven speed

}

...R

@darrob
As an aside, my stepper driver uses 1 of 4 profiles (user selectable);
Sigmoid function which produces the classic S-curve,
Harmonic function which produces the top half of the S-curve, useful for fast starts,
Linear approximation which is a Taylor Series expansion of an integral (check Atmel application note AVR446),
and a Sinusoidal function which is useful for moving through curves and circles.
To get these all working in less than 50us took about two years, a bit of cheating and a bunch of assembly. Not something I want to do again.

DKWatson:
May I ask why you would want to change micro-stepping? Generally micro-stepping is used at slower speeds to smooth out the transition detente-detente. With a camera slider I would think that you're never going to be operating very fast and would therefore use the highest micro-step available for the smoothest ride. I vary the micro-step in my CNC machines as the speed increases, but this reduces to full step at high speed in order to maximize the pulse width. Please explain.

Thank you, so the slider is about 1 meter long, at full speed takes about 4 sec from end to end. The thing is that at slow speeds , it does a big noise and runs not so smoothly, lots of vibration.
I want to make ,fullstep, 1/8 step, and 1/32 step. Im thinking about super slow for some nice timelapses.

Robin2:
I have tidied up the code in the first part of loop() as below. There seemed to be a lot of unnecessary stuff. And I think the line to retain the previous value of the button was in the wrong place.

void loop() {

buttonState = val;  // preserve the previous value
   val = digitalRead(msstep);
   if (val == HIGH) {
       if (val != buttonState) {          // the button state has changed!
           if (stepmode == 0) {          // if its off
               stepmode = 1;               // microstep full step!
           }
           else if (stepmode == 1) {        // if its all-on
                   stepmode = 2;             // microstep 1/8 step!
           }
           else {     // if its blinking
               stepmode = 0;           // microstep 1/16!
           }
       }
   }

if (stepmode == 0) {
       digitalWrite (MS0, LOW);
       digitalWrite (MS1, LOW);
       digitalWrite (MS2, LOW);
   }
   if (stepmode == 1) {
       digitalWrite (MS0, HIGH);
       digitalWrite (MS1, HIGH);
       digitalWrite (MS2, LOW);
   }
   if (stepmode == 2) {
       digitalWrite (MS0, LOW);
       digitalWrite (MS1, LOW);
       digitalWrite (MS2, HIGH);
   }

unsigned int smoothAnalogVal = joy.smoothRead(); // get our smoothed analog value
   signed int stepperSpeed = convertAnalogToSpeed(smoothAnalogVal); // convert our analog value to a stepper speed with direction
   Serial.println(stepperSpeed);
   slider.step(stepperSpeed); // request a step with our joystick driven speed

}




...R

Ok, thankyou. Ill ad the ms0 input, and try it like this at the end of day. Ill update if it works.

Pay attention to the first bit of your loop that Robin changed. In your original code you had

  int buttonState = digitalRead(msstep);
  val = digitalRead(msstep);

which in 999 times out of 1000 suggests that buttonState and val will have the same value.

DKWatson:
@darrob
As an aside, my stepper driver uses 1 of 4 profiles (user selectable);
Sigmoid function which produces the classic S-curve,
Harmonic function which produces the top half of the S-curve, useful for fast starts,
Linear approximation which is a Taylor Series expansion of an integral (check Atmel application note AVR446),
and a Sinusoidal function which is useful for moving through curves and circles.
To get these all working in less than 50us took about two years, a bit of cheating and a bunch of assembly. Not something I want to do again.

Now that fills me with confidence. Only two years huh :o .
I don't suppose you'd like to share ?? :wink:

See attached.

Atmel stepper code.pdf (153 KB)

Close Approximations of Sigmoid Functions.pdf (1.32 MB)

Harmonic series (mathematics) - Wikipedia.pdf (330 KB)

DKWatson:
Pay attention to the first bit of your loop that Robin changed. In your original code you had

  int buttonState = digitalRead(msstep);

val = digitalRead(msstep);



which in 999 times out of 1000 suggests that buttonState and val will have the same value.

Im a bit confused so if button state is button pressed or not pressed, the val will be also the same thing?
I want like cycling trough 3 diferent step resolutions not just 1 or 0.

Unless the state of the msstep pin changes in that quater microsecond (or so) between reads.

So guys, first of all thank you so much for your help!
Finaly i made the sugested modifications of the code and YES YES it worked as i intended!!

The final code i uploaded to arduino was this

#include "stepLib.h";
#include "smoothJoystick.h";

//not in use #define  LEFT_PIN  4
//not in use #define  STOP_PIN  5
//not in use #define  RIGHT_PIN 6
#define Limit01 2       //Limit switch
#define Limit02 3       //limit switch
#define joystickPin A1  // analog pin A1 (27) 

int msstep = 7;         // Pin 7 pushbutton to change step resulution
int MS0 = 10;           // Pin 10 connected to MS0 pin on driver
int MS1 = 11;           // Pin 11 connected to MS1 pin on driver
int MS2 = 12;           // pin 12 conected to MS2 pin on driver
int val;
int buttonState =0;
int stepmode = 0;

#define maxSpeed 5000   // speed measured in Hz
#define minSpeed 1      // speed measured in Hz

#define deadband 50     // deadband for consideration of 0 speed
# define sliderStep 9 // 9    // define our step pins
# define sliderDir 8 // 8     // define our direction pins

// instantiate out objects
stepMotor slider(sliderStep, sliderDir); // stepper motor
joystick joy(joystickPin, 512); // declare a new instance of a joystick on our joystickPin, let's initialize our buffer to center joystick value: 512

void setup() {
  Serial.begin(115200);
//not in use   pinMode(LEFT_PIN, INPUT_PULLUP);
//not in use   pinMode(STOP_PIN, INPUT_PULLUP);
//not in use   pinMode(RIGHT_PIN, INPUT_PULLUP);
  pinMode(msstep, INPUT_PULLUP);
  pinMode(Limit01, INPUT);
  pinMode(Limit02, INPUT);
  pinMode(sliderDir, OUTPUT);
  pinMode(MS0, OUTPUT);
  pinMode(MS1, OUTPUT);
  pinMode(MS2, OUTPUT);
  }

void loop() {
    buttonState = val;  // preserve the previous value
    val = digitalRead(msstep);
    if (val == HIGH) {
        if (val != buttonState) {        // the button state has changed!
            if (stepmode == 0) {         // if its 1/16 step resolution
                stepmode = 1;            // microstep full step!
            } 
            else if (stepmode == 1) {    // if its full step resolution
                    stepmode = 2;        // microstep 1/8 step!
            } 
            else {                       // if its 1/8 step resolution
                stepmode = 0;            // microstep 1/16!
            }
        }
    }
    if (stepmode == 0) {
        digitalWrite (MS0, LOW);
        digitalWrite (MS1, LOW);
        digitalWrite (MS2, LOW);
    } 
    if (stepmode == 1) {
        digitalWrite (MS0, HIGH);
        digitalWrite (MS1, HIGH);
        digitalWrite (MS2, LOW);
    }
    if (stepmode == 2) {
        digitalWrite (MS0, LOW);
        digitalWrite (MS1, LOW);
        digitalWrite (MS2, HIGH);
    }

    unsigned int smoothAnalogVal = joy.smoothRead(); // get our smoothed analog value
    signed int stepperSpeed = convertAnalogToSpeed(smoothAnalogVal); // convert our analog value to a stepper speed with direction
    Serial.println(stepperSpeed);
    slider.step(stepperSpeed); // request a step with our joystick driven speed
}

// this function takes as an input, an unsigned analog value and will convert it into a signed value which represents stepper speed
// the output is bounded by a min and max stepper speed
signed int convertAnalogToSpeed(unsigned int analogVal) {
    if (analogVal < 512 + deadband && analogVal > 512 - deadband) { // we are within deadband, return a 0 speed
      return 0;
    }
    if (analogVal >= 512 + deadband) { // move in forward direction - return a positive speed. joyLeft
      if (!digitalRead(Limit01)) {
        return 0; // check if limit switch is activated
    }
  else {
      return map(analogVal, 512 + deadband, 1024, minSpeed, maxSpeed); // interpolate our joystick value to the proper speed
    }
  }
    if (analogVal <= 512 + deadband) { // move in reverse direction - return a negative speed. joyRight
      if (!digitalRead(Limit02)) {
    return 0; // check if limit switch is activated
  }
  else { 
      return -map(analogVal, 512 - deadband, 0, minSpeed, maxSpeed); // interpolate our joystick value to the proper speed
    }
  }
}

Thank you so much

1 Like