Need help getting stepper motor to loop through spin/pause cycle

the design has changed. i thought he had intended the motor to run for spintime and then pause for step delay.

so now it looks like it will run run one step every stepdelay usec

are these needed?

millisecond not µs

You have got yourself into a very typical beginning programmer bind: You have a lot of code that doesn't work so it's hard to debug. What you need is a little bit of code that doesn't work so you can figure out what's wrong and fix it. Then add back the rest of your stuff a bit at a time, testing as you go.

I suggest that you write a new minimalist program that just turns the stepper at a constant speed and vary the speed by changing the stepdelay and recompiling.

I am surprised that only positions 7 and 8 work. It looks like it's the delayMicroseconds that do it, but I really don't understand why stepdelay has to be zero.

You have an odd combo of use of millis and delayMicroseconds. I'd get rid of the delays and every time millis tells you it's time to act, flip the step pin to whatever state it isn't currently in.

here is something to test (typed here so untested)

#include <ezButton.h>
// CONSTANTS
const byte stepPin = 3;
const byte dirPin = 4;
const byte goButtonPin = 5;
const byte enablePin = 6;
const unsigned long stepValidationTime_us = 100;

// VARIABLES
unsigned long stepdelay; //Motor off time
byte spindirection; // Motor spin direction
unsigned long lastStepTime;
bool motorIsRunning = false;
ezButton goButton(goButtonPin);

// CODE
void setup() {
  Serial.begin(115200);

  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  goButton.setDebounceTime(50);
  digitalWrite(enablePin, HIGH); //Sets initial condition of the stepper motor driver board outputs to "off"
  lastStepTime = millis(); //Initial spin time value

  // for the time being let's use fixed values
  stepdelay = 450;
  spindirection = HIGH;
}

void loop() {

  goButton.loop();

  if (button.isPressed()) {
    Serial.println("The button is pressed");
    digitalWrite(dirPin, spindirection); //Set motor spin direction based on rotary switch position
    digitalWrite(enablePin, LOW); //Turn on motor driver board outputs
    motorIsRunning = true;
  }

  if (button.isReleased()) {
    Serial.println("The button is released");
    digitalWrite(enablePin, HIGH); //Turn off motor driver board outputs
    motorIsRunning = false;
  }

  if (motorIsRunning) {
    unsigned long currentMillis = millis(); 
    if (currentMillis - lastStepTime >= stepdelay) {  
      digitalWrite(stepPin, HIGH);
      delayMicroseconds(stepValidationTime_us);
      digitalWrite(stepPin, LOW);
      lastStepTime = currentMillis;
    }
  }
}

(EDITED TO REMOVE THE STRAY ';')

Removed stray semicolon. Woulda been a compiler error, so at least that line would get some attention.

a7

oops - those damn copy/paste! thanks - corrected in the code

consider

const int stepPin   = 3;
const int dirPin    = 4;
const int enable    = 6;
const int go_button = 5;

void loop ()
{
    if (LOW == digitalRead (go_button))  {
        digitalWrite      (stepPin, HIGH);
        delayMicroseconds (100);
        digitalWrite      (stepPin, LOW);
        delayMicroseconds (100);

        delay (10);
    }
}

void setup()
{
    pinMode (go_button, INPUT_PULLUP);
    pinMode (stepPin, OUTPUT);
    pinMode (dirPin,  OUTPUT);
    pinMode (enable,  OUTPUT);

    digitalWrite (enable,HIGH);
}

Thank you all for taking the time to respond. I do appreciate that. I'll go through a (hopefully brief) history of the project so that you have a better understanding of where I've come from. I left all that off in the beginning in order to create a smaller post, but understand the value it can bring. So now a little history for context.

This program exists to drive a small hand-held syringe pump. The stepper drives a pushblock along linear rails to depress the plunger of a syringe at varying speeds. The challenge is that the fluid in the syringe is very viscous (roughly the same as castor oil), but it's being pushed through a 21 gauge needle. In addition, the device must be relatively small and light.

Due to the small/light requirements, I could not use a stepper larger than Nema 17 (and honestly wish I could use an 11). This also meant that a reduction gearbox was also out. Even the smallest planetary reduction boxes were too heavy and bulky for the application.

Originally I designed the code so that the stepper was being driven in a constant manner, and I would simply vary the delayMicroseconds for the HIGH and LOW motor pulses in order to change the speed. This actually worked very well in terms of driving the motor exactly as fast (or slow) as I needed. The catch? Even a NEMA 17 did not create enough torque to drive that fluid through that small of an aperture. It would spin and then stall, spin and then stall. It was this behavior that got me to try pulsing the button (and thus the motor) instead of trying to hold it at a constant speed.

Success! (in a way) When cycling the button, the motor is strong enough to deliver the fluid in the time required. Turn motor on briefly, hydraulic pressure builds up, stop the motor, and the pressure reduces over a short time as the droplet exits the needle. Repeat that process until the syringe is empty. (sure sounds like an if loop to me! :slightly_smiling_face:)

But doing this manually by squeezing and releasing the trigger button was not precise and prone to user error. Thus, I wanted a way to do it via code. The user should only have to select the required delivery time (via the rotary switch), then press the button and the motor will "pulse" at the required speed to drive the syringe plunger at the correct rate.

All of this is to say that this iteration of the code isn't the first go-round. I've been steadily building it up over time. I can successfully drive the motor at different speeds in a steady-state manner using delays from 75 µs to 900 µs. It's the on/off pulsing that is giving me trouble.

I am not in the office to test your other suggestions right now, but I wanted to give a little background and let you know that I will absolutely be looking into the other things you guys have offered as help and will get back to you after that.

Thank you. Truly, I appreciate your help.

just to get the language right:

if introduces a Selection statement, not a loop ( Iteration statement)

  • A selection statement chooses between one of several control flows
  • An iteration statement repeatedly executes some code

Statements - cppreference.com

Hi Jackson, thank you for taking the time to put some code together to help.
I like the idea of separating the "button pressed" if statement into its own condition. To your point, it gets rid of the need to know the state of the button.

By the way, the background on that is that I originally used the same statement you're suggesting (if(button.isPressed()) , but the difference is that my program would run the stepPin code when that statement evaluted to true. As I'm sure you can imagine, it simply ran through once, even when the button was held down. Ergo my use of go_ButtonState. But your method is much cleaner, and accounts for whether the button is pressed once or held down.

Alas, changing my program to the code you recommend in post #24 above results in the same behavior as "Position 1" in my code from post #17. That is to say that the motor doesn't turn, it simply whines in an on/off cycle that repeats roughly 2.3 times per second. (I did an approximate check using a stopwatch and counting the whine on/off cycles for 10 seconds.)
Changing the value of stepValidationTime_us didn't make any appreciable difference (I tested both 75 and 500 as possible values)

When I change the value of stepdelay to 0 in the example code you posted, the motor turns the same as my code in switch Position 7. The only difference is that the motor makes a bit more noise since it doesn't have the 100 µs stepPin delay that exists in my code. But it does turn at what I judge to be the same speed.

I was very hopeful that separating the if statements into discrete parts was going to do the trick, but unfortunately it does not. That being said, it absolutely cleans up the code and allows us to focus strictly on what is going on within the motorIsRunning timing loop. I consider that progress. Thank you again for your help there.

Any other thoughts?

How is your system powered ?
Can you post the circuit and details about the parts?

You should even further simplify the code and get the motor to work without the buttons. Using a library could be a good idea to see (with one of the built in example) that the circuit you have does work.

I believe the fact that it works in positions 7 and 8 prove that the hardware is working as intended. I also had it working at all switch positions when the design was constant rotation instead of the pulsing that I'm trying now (see post #28). None of the components or layout have changed since then. Only the code.

But in the interest of completeness, I'll put together a circuit diagram and post it when I can later today.

For reference, this is the "constant rotation" code I was referring to that worked perfectly:

#include <ezButton.h>

const int stepPin = 3; 
const int dirPin = 4;
ezButton go_button = 5; 
const int enable = 6;
const int pos_1 = 8;
const int pos_2 = 9;
const int pos_3 = 10;
const int pos_4 = 16;
const int pos_5 = 14;
const int pos_6 = 15;
const int pos_7 = 18;
const int pos_8 = 7;
int stepperdelay;
bool spindirection;

void setup() {
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
  pinMode(enable,OUTPUT);
  pinMode(pos_1,INPUT_PULLUP);
  pinMode(pos_2,INPUT_PULLUP);
  pinMode(pos_3,INPUT_PULLUP);
  pinMode(pos_4,INPUT_PULLUP);
  pinMode(pos_5,INPUT_PULLUP);
  pinMode(pos_6,INPUT_PULLUP);
  pinMode(pos_7,INPUT_PULLUP);
  pinMode(pos_8,INPUT_PULLUP);
  go_button.setDebounceTime(50);
  digitalWrite(enable,HIGH); //Sets the initial condition of the stepper motor driver board's outputs to "off"
}

void loop() {
  int pos_1_State = digitalRead(pos_1);
  int pos_2_State = digitalRead(pos_2);
  int pos_3_State = digitalRead(pos_3);
  int pos_4_State = digitalRead(pos_4);
  int pos_5_State = digitalRead(pos_5);
  int pos_6_State = digitalRead(pos_6);
  int pos_7_State = digitalRead(pos_7);
  int pos_8_State = digitalRead(pos_8);
  
  if (pos_1_State == LOW) {
    stepperdelay = 450;
    spindirection = HIGH;
  } else if (pos_2_State == LOW) {
    stepperdelay = 550;
    spindirection = HIGH;
  } else if (pos_3_State == LOW) {
    stepperdelay = 650;
    spindirection = HIGH;
  } else if (pos_4_State == LOW) {
    stepperdelay = 750;
    spindirection = HIGH;
  } else if (pos_5_State == LOW) {
    stepperdelay = 850;
    spindirection = HIGH;
  } else if (pos_6_State == LOW) {
    stepperdelay = 950;
    spindirection = HIGH;
  } else if (pos_7_State == LOW) { //Highest speed for testing purposes
    stepperdelay = 100;
    spindirection = HIGH;
  } else if (pos_8_State == LOW) { //Reverse position
    stepperdelay = 75;
    spindirection = LOW;
  } else { //Accounting for switch in "empty" positions
    stepperdelay = 32000;
    spindirection = LOW;
  }
    
  go_button.loop();
  int go_ButtonState = go_button.getState();
  
  if(go_ButtonState == 0) {
    digitalWrite(enable,LOW); //Turn on the motor driver board's outputs
    digitalWrite(dirPin,spindirection);
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(stepperdelay); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(stepperdelay);
    go_button.getState();
  }
  
  if(go_button.isReleased()){
    digitalWrite(enable,HIGH); //Turn off the motor driver board's outputs
  }

}

Thanks!

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