Arduino + Two Momentary Switch, Set RPM Stepper Platform

Hi All,

First post and first time delving into Arduino & code with a little project I have embarked on. Has been a reasonable learning curve so far. But enjoying the challenge :slight_smile:

1x Uno scrapped already. :unamused:, and I think a bad stepper driver. In any case.

I look forward to any help users can provide to this noob.

I have used the search function and looked over other posts with similar titles/ objectives but go about it differently, so thought I should make my own project post.

Project: Using a Arduino Uno + easy driver (V4.4 A3967 set at 1/8 step) + 2x momentary switches. Move a Nema 11 stepper motor linear rail when Up or Down button is pressed at a set RPM only when and for as long as either button is pressed in the desired direction.

Issue/ Troubleshooting: I have simulated this device using wokwi and run the code. Once either button is pressed the wokwi simulation continues to run the motor in the direction without stopping once the button is released. It does notice both Up / Down directions.

This is also the case on the physical project. Once the Up or down button is pressed, the Motor will continue turning until the Uno board is reset (Don't want linear rail to hit stops)

Question: Do I simply have some code in the wrong order, or missing a If statement?
What is the best way to have the code continuously check for the release of the Up or down button?
Viewing the original code, the If currentTime - Last StepTime is possibly where I am getting issues?

Visual examples help me a great deal as I can interpret how it works in the code (if possible).

Troubleshooting: I have tried the single button press code (Original) and altered it but have not had any success. Along with different changes to the Up/Down code.
My most recent try had the motor start as soon as power was applied. Feel I am going backwards.

Thanks for any help you can provide with the project or pointing me in the direction to alter what is required.

Code example from:
https://www.schmalzhaus.com/EasyDriver/Examples/EasyDriverExamples

Code (Motor continues in direction of button push Up/Down:


 
#define RPMS                104.0
#define STEP_PIN                9     // Tells motor to move
#define DIRECTION_PIN           8     // Tells Board/ Motor Direction using HiGH (clockwise)/LOW (Anticlockwise)
#define UP_PIN                  3     // Up Pin = Up Momentary Switch
#define Down_PIN                2     // Down Pin = Down Momentary Switch

#define STEPS_PER_REV         200
#define MICROSTEPS_PER_STEP     8
#define MICROSECONDS_PER_MICROSTEP   (1000000/(STEPS_PER_REV * MICROSTEPS_PER_STEP)/(RPMS / 60))

int Stepping = false;
uint32_t LastStepTime = 0;
uint32_t CurrentTime = 0;

void setup() {               
  pinMode(STEP_PIN, OUTPUT);    
  pinMode(DIRECTION_PIN, OUTPUT);
  digitalWrite(STEP_PIN, LOW);
  digitalWrite(DIRECTION_PIN, LOW);
  pinMode(UP_PIN,INPUT);
  pinMode(Down_PIN,INPUT);
}

void loop() {
  if (digitalRead(UP_PIN) == LOW && Stepping == false)
  {
    digitalWrite(DIRECTION_PIN, LOW);             // Tells board Up on direction Pin using LOW
    Stepping = true;                              // Applies above only if true
  }
  if (digitalRead(Down_PIN) == LOW && Stepping == false)
  {
    digitalWrite(DIRECTION_PIN, HIGH);            // Tells board Down on direction Pin using HIGH
    Stepping = true;                              // Applies above only if true
  }
  if (Stepping == true)
    {
      CurrentTime = micros();
    if ((CurrentTime - LastStepTime) > MICROSECONDS_PER_MICROSTEP)
    {
      LastStepTime = CurrentTime;
      digitalWrite(STEP_PIN, HIGH);                             // When stepping True - Step Pin set HIGH Motor moves at Set Speed in Down Direction
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9)/2);
      digitalWrite(STEP_PIN, LOW);                              // When stepping True - Step Pin set LOW Motor moves at Set Speed in Down Direction
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9)/2);
    }
  }
}

Original Code: (Only set for One button)

/* This example assumes a step/direction driver with Step on pin 9, Direction on pin 8
 * And an input switch on pin 3. The switch is a switch to ground, with pin 3 pulled
 * high with a pullup resistor. When the switch is turned on (closed, i.e. goes low)
 * the the sepper motor steps at the rate specified (104 RPM in this code, with
 * 1/8th microstepping of a 200 steps/rev motor)
 */
 
#define RPMS                104.0
#define STEP_PIN                9
#define DIRECTION_PIN           8
#define GO_PIN                  3

#define STEPS_PER_REV         200
#define MICROSTEPS_PER_STEP     8
#define MICROSECONDS_PER_MICROSTEP   (1000000/(STEPS_PER_REV * MICROSTEPS_PER_STEP)/(RPMS / 60))

uint32_t LastStepTime = 0;
uint32_t CurrentTime = 0;

void setup() {               
  pinMode(STEP_PIN, OUTPUT);    
  pinMode(DIRECTION_PIN, OUTPUT);
  digitalWrite(STEP_PIN, LOW);
  digitalWrite(DIRECTION_PIN, LOW);
  pinMode(GO_PIN,INPUT);
}

void loop() {
  if (digitalRead(GO_PIN) == LOW)
  {
    CurrentTime = micros();
    if ((CurrentTime - LastStepTime) > MICROSECONDS_PER_MICROSTEP)
    {
      LastStepTime = CurrentTime;
      digitalWrite(STEP_PIN, HIGH);
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9)/2);
      digitalWrite(STEP_PIN, LOW);
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9)/2);
    }
  }
}

Cheers,
Gav

You keep on moving the time with this

Evevry time loop() goes through this, the CurrentTime get updated. If you move CurrentTime = micros(); to the earlier ifs where you test the button, it might work.

So when you detect the button press and the code is not stepping, set the current time. Look at the original code how it's done there.

1 Like

Thanks for the quick reply and assistance Sterretje.

I had a try at moving the CurrentTime and depending on where I positioned it, best result I could get was one step from the motor using wokwi as a simulation when a button was pressed.

After more searching the net for solutions, I came across code very close to what I am working with and picked out what looked to be missing on myside. (Many thanks to the random person who created their code)

Below is the code I have ended up with that allows the code / device to function as desired for anyone interested.

/* This example assumes a step/direction driver with Step on pin 9, Direction on pin 8
 * And an input switch on pin 3. The switch is a switch to ground, with pin 3 pulled
 * high with a pullup resistor. When the switch is turned on (closed, i.e. goes low)
 * the the stepper motor steps at the rate specified (50 RPM in this code, with
 * 1/8th microstepping of a 200 steps/rev motor)
 * each 'step' from the code's perspective is 1/8th of the motor's full step, 
 * because we are assuming the use of the EasyDriver in 1/8th microstep mode. 
 * If you use the Big Easy Driver, it's default is 1/16 microstep, so adjust your expectations for motor motion accordingly
 * https://www.schmalzhaus.com/EasyDriver/Examples/EasyDriverExamples.html - example 1.7
 * 2= DOWN, 3= UP, 9= STEP, 8=DIR
 */ 


 
#define DISTANCE            25
#define RPMS                50.0
#define STEP_PIN                9     // Tells motor to move
#define DIRECTION_PIN           8     // Tells Board/ Motor Direction using HiGH (clockwise)/LOW (Anticlockwise)
#define UP_PIN                  3     // Up Pin = Up Momentary Switch
#define Down_PIN                2     // Down Pin = Down Momentary Switch

#define STEPS_PER_REV         200
#define MICROSTEPS_PER_STEP     8
#define MICROSECONDS_PER_MICROSTEP   (1000000/(STEPS_PER_REV * MICROSTEPS_PER_STEP)/(RPMS / 60))

int StepCounter = 0;
int Stepping = false;
uint32_t LastStepTime = 0;
uint32_t CurrentTime = 0;

void setup() {               
  pinMode(STEP_PIN, OUTPUT);    
  pinMode(DIRECTION_PIN, OUTPUT);
  digitalWrite(STEP_PIN, LOW);
  digitalWrite(DIRECTION_PIN, LOW);
  pinMode(UP_PIN,INPUT);
  pinMode(Down_PIN,INPUT);
}

void loop() {
  if (digitalRead(UP_PIN) == LOW && Stepping == false)
  {
    digitalWrite(DIRECTION_PIN, LOW);             // Tells board Up on direction Pin using LOW
    Stepping = true;                              // Applies above only if true
  }
  if (digitalRead(Down_PIN) == LOW && Stepping == false)
  {
    digitalWrite(DIRECTION_PIN, HIGH);            // Tells board Down on direction Pin using HIGH
    Stepping = true;                              // Applies above only if true
  }
  if (Stepping == true)
    {
      CurrentTime = micros();
    if ((CurrentTime - LastStepTime) > MICROSECONDS_PER_MICROSTEP)
    {
      LastStepTime = CurrentTime;
      digitalWrite(STEP_PIN, HIGH);                             // When stepping True - Step Pin set HIGH Motor moves at Set Speed in Down Direction
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9)/2);
      digitalWrite(STEP_PIN, LOW);                              // When stepping True - Step Pin set LOW Motor moves at Set Speed in Down Direction
      delayMicroseconds((MICROSECONDS_PER_MICROSTEP * 0.9)/2);
     StepCounter = StepCounter + 1;

    if (StepCounter == DISTANCE)
    {
      StepCounter = 0;
      Stepping = false;
    }
  }
}
}

Cheers
Gav

You can share a link to your wokwi simulation which would make it so easy for us to see your code and run it and suggest modifications and stuff.

Just sayin'.

a7

How do you have the buttons wired? It looks like they could be floating inputs when the microswitch is open.

If you don't have external pullup resistors, I'd consider using the internal pullups and having the other pin of the switches go to ground to make it not float:

pinMode(GO_PIN,INPUT_PULLUP);

Thanks @alto777,

I was hesitant to share as its not the same driver I am using. Even so I guess the concept is the same. This is the wokwi simulation for the project.

@DaveX, External Pullups. 3.3k Ohm Then to ground.

I will attach a rough drawn schematic of the setup below. Bit messy but hope it helps people.

Thanks for the replies and looking forward to any insight you are able to give.

Cheers,
Gav

OK IC THX.

The driver issue may or not matter. I would look to establish identical low level behaviour between the sim and real life implementations, then confidently build higher level stuff on top of that and expect it to work no matter the details at the driver/hardware side.

By turning the RPMS to 2.0 and the DISTANCE to 100, I see the red button make the motor go 100 steps in one direction, and the green button make the motor go 100 steps in the other direction.

If you keep your big fat thumb on either button, the motor spins continuously and will keep relaunching 100 step blocks of movement.

This appears, at a quick reading, to be the goal of the sketch.

I'll read the thread later to see if this is wrong, or what the problem actually is… :expressionless:

It may be… that you want only one distance per press of the button. If so, you need to implement "state change detection" for the buttons, so they cause action because they become pressed rather than because they are pressed.

There are examples in the IDE, see Examples/02.Digital/StateChangeDerection and google.

Pro tip: add an arrow to your motor in the wokwi. Edit the *.json file and add an attribute to the motor

    {"size":"17", "arrow":"orange"}

Fun!


Nice, thx. It's not usual to specific the wire color, losing those labels would de-clutter it a bit. Also modern schematics can have wires cross without the little croquet wicket jumper hops. If two crossing wires have a dot where they cross, they connect. Otherwise it is understood that they do not. This too can make a schematic a bit prettier. You don't need reminder labels on the wires at the corners. Schematics convey huge information and leaving off irrelevant stuff makes it easier to read.

But srsly, nice job on the rough schematic!

L8R

a7

Thanks A7.

I took your advice and added a pointer arrow. Makes a massive difference visually. Cheers

Appreciate the explanation, Ill try simplify it for next time. Although, all the colours are helping me currently track down what goes where in my project. It looks like a bit of a birds nest.

You are correct. The goal of this project has been reached and I can confirm the physical project works also. Winning! :slight_smile: :smiley:

Thanks for the report. You'd be surprised how many times we never hear if someone succeeded or not.

a7

At this point, I'm a sucker for punishment, have decided to make it slightly more complex..Just because I can.. I think it should be straight forward, but I may have under estimated how difficult this tweak is for me to achieve.

My updated goal, is to maintain the current functionality, but depending on how the button is pressed. The motor speed increases to a second set RPM (Essentially trying to reduce travel time between points)

Two trains of thought on how this may work.

Option One - If pressing a button (Up or Down) and hold, code works as it currently does (constant rpm ~25).
Ideal Addition: If pressing Up or Down button Once quickly, off for ~0.5sec (timed window slot +/- value eg. 0.25sec) and then hold button RPM is set to a higher value (eg to 150RPM), at any time the button is released the motor stops.

Or

Option two: Press Up or Down button for defined amount of time (lets say 2secs) - motor spins at its set 25rpm from 0-2seconds, after 2secs RPM increases to 150RPM, at any time the button is released the motor stops.

I have looked into single, double, hold button options.

Can anyone provide insight into how best they would achieve this?
I like the idea of option one the most, although objectively, option 2 is more likely achievable for me in the short term.

Below is what I am attempting to work with (option 2) (from Jeff's Arduino Blog: Click for A, Press and Hold for B) and trying to insert into the working code earlier in the thread.

#define debounce               20     // ms debounce period to prevent flickering when pressing or releasing the button/switch
#define holdTime             20000     // ms hold period: how long to wait for press+hold event
// UP Button variables
int UPbuttonVal = 0;                  // value read from button
int UPbuttonLast = 0;                 // buffered value of the button's previous state
long UPbtnDnTime;                     // time the button was pressed down
long UPbtnUpTime;                     // time the button was released
boolean UPignoreUp = false;             // whether to ignore the button release because the click+hold was triggered

// Down Button variables
int DownbuttonVal = 0;                  // value read from button
int DownbuttonLast = 0;                 // buffered value of the button's previous state
long DownbtnDnTime;                     // time the button was pressed down
long DownbtnUpTime;                     // time the button was released
boolean DownignoreUp = false;               // whether to ignore the button release because the click+hold was triggered

// 2 Speed Motor variables
boolean Motspeed1Val1 = false;      // state of Motor Speed 1
boolean Motspeed2Val2 = false;      // state of Motor Speed 2 

// Read the state of the button
UPbuttonVal = digitalRead(UP_PIN);


// Test for button pressed and store the down time
if (UPbuttonVal == LOW && UPbuttonLast == HIGH && (millis() - UPbtnUpTime) > long(debounce))
{
UPbtnDnTime = millis();
}


// Test for button release and store the up time
if (UPbuttonVal == HIGH && UPbuttonLast == LOW && (millis() - UPbtnDnTime) > long(debounce))
{
if (UPignoreUp == false) event1();
else UPignoreUp = false;
UPbtnUpTime = millis();
}

// Test for button held down for longer than the hold time
if (UPbuttonVal == LOW && (millis() - UPbtnDnTime) > long(holdTime))
{
event2();
UPignoreUp = true;
UPbtnDnTime = millis();
}

UPbuttonLast = UPbuttonVal;

}

// Read the state of the button
DownbuttonVal = digitalRead(Down_PIN);


// Test for button pressed and store the down time
if (DownbuttonVal == LOW && DownbuttonLast == HIGH && (millis() - DownbtnUpTime) > long(debounce))
{
DownbtnDnTime = millis();
}


// Test for button release and store the up time
if (DownbuttonVal == HIGH && DownbuttonLast == LOW && (millis() - DownbtnDnTime) > long(debounce))
{
if (DownignoreUp == false) event1();
else DownignoreUp = false;
DownbtnUpTime = millis();
}

// Test for button held down for longer than the hold time
if (DownbuttonVal == LOW && (millis() - DownbtnDnTime) > long(holdTime))
{
event2();
DownignoreUp = true;
DownbtnDnTime = millis();
}

DownbuttonLast = DownbuttonVal;

}


// Events to trigger by click and press+hold

void event1()
{
Motspeed1Val1 = !Motspeed1Val1; 
digitalWrite(STEP_PIN, Motspeed1Val1);
}

void event2()
{
Motspeed2Val2 = !Motspeed2Val2;
digitalWrite(STEP_PIN2, Motspeed2Val2);
}

Thanks all for looking and any replies you are happy to share.

Cheers
Gav

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