Help coding a push button to reverse direction with a potentiometer

Hello,

First, thanks to everyone here who has posted helpful information. This forum is amazing. I have gone from knowing nothing about microcontrollers and coding to know just a little. So thank you for teaching a layman like myself.

I am using an Arduino Uno, a4988 driver, and a potentiometer to control a Nema 17 stepper motor. This part is working wonderfully thanks to this forum. But now I am trying to add a push button to allow me to change the direction of the motor. Currently, if the button is unpressed, the motor spins counterclockwise. When pressed it spins clockwise.

My 3 problems:

  1. the speed of the rotation isn't the same when the button is pressed (it's slightly slower)
  2. how can I modify the code so that when I do not have to continually press the button?
  3. It only functions if powered from the computer plug. When using a 9v battery it doesn't function.

Here is the code:

const int stepPin = 3;
const int dirPin = 4; 
int customDelay,customDelayMapped; // Defines variables
const int ms1 = 8;
const int ms2 = 9;
const int ms3 = 10;
const int buttonPin = 2;
int buttonState = 0;

void setup() {
  // Sets the two pins as Outputs
 
 pinMode(buttonPin, INPUT);
  pinMode(stepPin,OUTPUT);
  pinMode(dirPin,OUTPUT);
  pinMode(ms1,OUTPUT);
  pinMode(ms2,OUTPUT);
  pinMode(ms3,OUTPUT);
 
  
}
void loop() {
 
 customDelayMapped = speedUp(); // Gets custom delay values from the custom speedUp function
  // Makes pules with custom delay, depending on the Potentiometer, from which the speed of the motor depends

  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH)  {
    digitalWrite(dirPin, HIGH);
  } else {
    digitalWrite(dirPin, LOW);
  }
   digitalWrite(stepPin, LOW);
  delayMicroseconds(customDelayMapped);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(customDelayMapped);
  digitalWrite(ms1, HIGH);
  digitalWrite(ms2, HIGH);
  digitalWrite(ms3, HIGH);
}
  
  
 


// Function for reading the Potentiometer
int speedUp() {
  int customDelay = analogRead(A0); // Reads the potentiometer
  int newCustom = map(customDelay, 0, 1023, 300,4000); // Convrests the read values of the potentiometer from 0 to 1023 into desireded delay values (300 to 4000)
  return newCustom;  
}

Any help would be greatly appreciated. Also this is my first post so if anything is posted incorrectly please forgive me and if additional information is needed to help please let me know. Thank you

Instead of

  buttonState = digitalRead(buttonPin);

Something like:

static int lastbuttonpin;
if (buttonpin != lastbuttonpin) {
   lastbuttonpin = buttonpin;
   if (buttonpin) {
     buttonstate = !buttonstate;
   }
}

My own preference is to always use unsigned variables if a variable cannot ever be negative, so unsigned int instead of int, and to use the smallest variable that will do the job so unsigned char instead of int for the variables you have. I don't know if this is just my own preference or if it is something better programmers than me would adopt too.

  1. It only functions if powered from the computer plug. When using a 9v battery it doesn’t function.

That would be the expected and normal behavior. A 9v block battery can barely provide enough current to run the Arduino for a short time, the current demands of a stepper motor far exceed the capability of the battery.

Motors in general (servos, steppers, etc) should always be powered from a separate power supply, using the 5v supply from the Arduino will be problematic and must be avoided. The Arduino voltage output is there for sensors and other logic, not motors, which draw high current and will induce noise back into their power supply.

Thanks Perry i'll give this a shot.

WattsThat, the stepper is being powered from a 12v battery pack. sorry I forgot to mention that. So when I plug in the 9v battery to run the Arduino, it spins one direction and when I press the button it shuts off the Arduino. But, when plugged into the computer, when I press the button it reverses direction just at a slightly slower motor speed. It's probably related to bad wiring on my part. I am working on creating a circuit diagram so maybe someone can tell what I'm doing wrong.

These links may be of interest

Stepper Motor Basics
Simple Stepper Code

...R

Thanks Robin. I’ll read through those again. below is the diagram of the setup with the potentiometer that works. How can I add a button to this?

Image from Reply #5 so we don't have to download it. See this Simple Image Guide

...R

Fritzing diagrams are very easy to misunderstand. A photo of a simple pencil drawing with clear labels will be much better.

From you Original Post I have the impression that you already have a working button, albeit not working quite as you want it to.

I think you need a variable to keep track of whether you want the motor to run clockwise or counter-clockwise. The value in that variable should then change whenever your button changes from not-pressed to pressed. The code in Reply #1 illustrates the idea.

At the moment you have a variable that changes whenever the button changes - which is why you have to hold it down.

...R

Thanks again Robin2. Thanks for helping out with the picture. I appreciate all the information from everyone. Now it's time to get back to work and see if I can figure it all out! Cheers!

Ok almost everything is working now. When powered on the motor spins clockwise. When I press and hold the button the motor will spin counterclockwise with no loss of power. PerryBebbington, I tried to add your variable code but I could not get it to function. It actually made me think about another question though.

Can I make the button change direction and start/stop the motor? Perhaps 1 click to start, 2nd click to change direction and 3rd click to stop? Below is the code i currently have:

#define ACTIVATED LOW
const int stepPin = 3;
const int dirPin = 4;
int customDelay, customDelayMapped; // Defines variables
const int ms1 = 8;
const int ms2 = 9;
const int ms3 = 10;

int buttonPin = 2;
int buttonState = 0;
void setup() {
  // Sets the two pins as Outputs
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(ms1, OUTPUT);
  pinMode(ms2, OUTPUT);
  pinMode(ms3, OUTPUT);
   //Enables the motor to move in a particular direction
}
void loop() {

  customDelayMapped = speedUp(); // Gets custom delay values from the custom speedUp function
  // Makes pules with custom delay, depending on the Potentiometer, from which the speed of the motor depends
  digitalWrite(stepPin, LOW);
  delayMicroseconds(customDelayMapped);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(customDelayMapped);
  
  digitalWrite(ms1,HIGH);
  digitalWrite(ms2,HIGH);
  digitalWrite(ms3, HIGH);
 buttonState = digitalRead(buttonPin); 
if (buttonState == ACTIVATED) {
   digitalWrite(dirPin, HIGH); // spin counterclockwise
}
   else { 
    digitalWrite(dirPin, LOW); // spin clockwise button not pushed
   }
}

// Function for reading the Potentiometer
int speedUp() {
  int customDelay = analogRead(A0); // Reads the potentiometer
  int newCustom = map(customDelay, 0, 1023, 300, 4000); // Convrests the read values of the potentiometer from 0 to 1023 into desireded delay values (300 to 4000)
  return newCustom;
}

Hi,
If you are using one of these for 9V,

That will not have the current to properly supply the UNO, they are meant for Smoke Detectors.

Have you got the gnd of the stepper supply connected to the gnd of the UNO?

Thanks.. Tom... :slight_smile:

You are still responding directly to every change of the button state

buttonState = digitalRead(buttonPin); 
if (buttonState == ACTIVATED) {
   digitalWrite(dirPin, HIGH); // spin counterclockwise
}
   else { 
    digitalWrite(dirPin, LOW); // spin clockwise button not pushed
   }
}

What you need to do is something like this pseudo code

if (buttonState == LOW and previousState == HIGH) {
   change motorState
}
if (motorState = XXX) {
   // go forward, or backwards or stop as appropriate
}

Note how the motorState variable only changes when you press the button and not when you release it.

Note also how the behaviour of the button and the motor are quite separate with the only common thing being the variable motorState. The motor does not need to know how that variable acquired its value. And the button does not need to know what its action will be used for.

Have a look at Planning and Implementing a Program

...R

Ok, I have gone through everyone's suggestions and modified the code and everything is working correctly. The motor runs at the speed according to the potentiometer and each button click changes the direction. I found and modified some code to set the pushbutton which helped me solve that problem (honestly I don't fully understand it, but I managed to get it to work). The code sets up two events. 1 for a short click, which i am using to change direction and 2 for a long click which I am trying to get to start and stop the motor. (Technically I could just use the switch on the power supply i guess) below is the code that sets up the 2 events and reads the button:

void buttonset() {
    // Read the state of the button
buttonVal = digitalRead(buttonPin);

// Test for button pressed and store the down time 
if (buttonVal == LOW && buttonLast == HIGH && (millis() - btnUpTime) > long(debounce))
{
btnDnTime = millis();
}

// Test for button release and store the up time
if (buttonVal == HIGH && buttonLast == LOW && (millis() - btnDnTime) > long(debounce))
{
if (ignoreUp == false) event1();
else ignoreUp = false;
btnUpTime = millis();
}
// Test for button held down for longer than the hold time
if (buttonVal == LOW && (millis() - btnDnTime) > long(holdTime))
{
event2();
ignoreUp = true;
btnDnTime = millis();
}
  }
  
//=================================================
// Events to trigger by click and press+hold

void event1() // changes the rotation direction with a click
{
dirVal = !dirVal;
digitalWrite(dirPin, dirVal);
}

void event2() // starts and stops the motor with 2 second press of button
{
stepVal = !stepVal;
digitalWrite(stepPin, stepVal);
}

event1() is working to change the direction. How can I modify event2() to start and stop the motor?
Do I need to break the loop? Modify the motorrun() code? (This is a bit above my understanding I think)

The full code is posted below:

#define buttonPin 2 // analog input pin to use as a digital input
#define stepPin 3 // digital output pin for LED 1 indicator
#define dirPin 4 // digital output pin for LED 2 indicator

#define debounce 20 // ms debounce period to prevent flickering when pressing or releasing the button
#define holdTime 2000 // ms hold period: how long to wait for press+hold event

// Button variables
int buttonVal = 0; // value read from button
int buttonLast = 0; // buffered value of the button's previous state
long btnDnTime; // time the button was pressed down
long btnUpTime; // time the button was released
boolean ignoreUp = false; // whether to ignore the button release because the click+hold was triggered

// motor variables
boolean stepVal = false; // state of motor power
boolean dirVal = false; // state of direction
//16th stepping
const int ms1 = 8;
const int ms2 = 9;
const int ms3 = 10;
int customDelay, customDelayMapped;

void setup() 
{

// Set button input pin
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH );

// Set output pins
pinMode(stepPin, OUTPUT);
digitalWrite(stepPin, stepVal);
pinMode(dirPin, OUTPUT);
digitalWrite(dirPin, dirVal);
pinMode(ms1, OUTPUT);
pinMode(ms2, OUTPUT);
pinMode(ms3, OUTPUT);

}

void loop()
{
  
  motorrun(); // starts the stepper motor moving clockwise at a speed dependant on the potentiometer
  buttonset(); // sets short and long click function of the button (short click = direction change, 2 second click stops motor)
 buttonLast = buttonVal;

}



//====================================================
int potentiometer() {
  int customDelay = analogRead(A0); // Reads the potentiometer
  int newCustom = map(customDelay, 0, 1023, 300, 4000); // Convrests the read values of the potentiometer from 0 to 1023 into desireded delay values (300 to 4000)
  return newCustom;
}
//====================================================
void motorrun() { // make motor move in a given direction
  customDelayMapped = potentiometer();
  digitalWrite(stepPin, LOW);
  delayMicroseconds(customDelayMapped);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(customDelayMapped);
  digitalWrite(ms1,HIGH);
  digitalWrite(ms2,HIGH);
  digitalWrite(ms3, HIGH);
  }

//======================================================
  
  void buttonset() {
    // Read the state of the button
buttonVal = digitalRead(buttonPin);

// Test for button pressed and store the down time 
if (buttonVal == LOW && buttonLast == HIGH && (millis() - btnUpTime) > long(debounce))
{
btnDnTime = millis();
}

// Test for button release and store the up time
if (buttonVal == HIGH && buttonLast == LOW && (millis() - btnDnTime) > long(debounce))
{
if (ignoreUp == false) event1();
else ignoreUp = false;
btnUpTime = millis();
}
// Test for button held down for longer than the hold time
if (buttonVal == LOW && (millis() - btnDnTime) > long(holdTime))
{
event2();
ignoreUp = true;
btnDnTime = millis();
}
  }
  
//=================================================
// Events to trigger by click and press+hold

void event1() // changes the rotation direction with a click
{
dirVal = !dirVal;
digitalWrite(dirPin, dirVal);
}

void event2() // starts and stops the motor with 2 second press of button
{
stepVal = !stepVal;
digitalWrite(stepPin, stepVal);
}

Thank for all the help!

Hi,
Why don't you use a second button to switch the stepper ON and OFF?

Or use a toggle switch with center OFF position, LEFT is one direction and RIGHT is the other.
Then all you need to do is look at the switch position to tell if the stepper is OFF, or set for a particular direction when you have the stepper stationary.

Tom.... :slight_smile:

TomGeorge I don't want to use 2 switches because I want to keep the functionality as simple as possible and it already has two different control points, Pot and Switch. However, using a center off switch is a great idea. I've got some more homework to do now. Cheers!

Can anyone point me in the direction of some information on how to wire and code an on-off-on switch? I know the code will require and if or while statement which I think I can figure out, but what about the wiring? does it require the use of 2 pins? I wrote an example code below. Would it work to move a motor to the right, to the left, and turn off?

void switchTest() {
  stepVal = digitalRead(stepPin);
  dirVal1 = digitalRead(dirPin1);
  dirVal2 = digitalRead(dirPin2);

 if (stepVal == HIGH && dirVal1 == HIGH)
 {
  digitalWrite(motorRunClockwise());
 }
 if (stepVal == HIGH && dirVal2 == HIGH)
 {
  digitalWrite(motorRunAnticlockwise());
 }
 else
 {
  digitalWrite(motorOff());
 }
}

mamassingale:
Can anyone point me in the direction of some information on how to wire and code an on-off-on switch?

Wire one end of the switch to the GND pin and the other to your digitalInput pin. Then initialize the digitalInput pin with the internal pullup resistor. Then you can check the state of the switch by seeing if the pin is HIGH or LOW.

Power_Broker this would be the same as a normal 2 state (on/off). If it only reads HIGH and LOW how can it differentiate between the two different on positions present in a 3 state (on-off-on switch)?

For a centre off switch wire the centre pin to GND and the two outer pins to two separate I/O pins set as INPUT_PULLUP.

...R

Thanks Robin2. I have taken your advice and gone back through the planning resource you sent me to. I have rewritten the code to work with a switch instead of a button. This is the first code I have ever written on my on so I am not sure if it will work (fingers crossed). I'm off to the store to grab a switch. I have posted the code below if anyone has time to read it and give me some pointers. Thanks again for all the help!!

const int stepPin = 3;
const int dirPin = 4;
const int switchPinA = A1;
const int switchPinB = A2;
int customDelay, customDelayMapped; // Defines variables
const int ms1 = 8;
const int ms2 = 9;
const int ms3 = 10;
int switchPinAVal = 0;
int switchPinBVal = 0;


void setup() {
  
  pinMode(switchPinA, INPUT_PULLUP);
  pinMode(switchPinB, INPUT_PULLUP);
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(ms1, OUTPUT);
  pinMode(ms2, OUTPUT);
  pinMode(ms3, OUTPUT);
  switchPinAVal = digitalRead(A1);
  switchPinBVal = digitalRead(A2);
}
void loop() {

if (switchPinAVal == HIGH && switchPinBVal == LOW)
{
  motorRunClockwise();
}
if (switchPinAVal == LOW && switchPinBVal == HIGH)
{
  motorRunAntiClockwise();
}
 else
 {
  digitalWrite(stepPin, LOW); // stop the motor
 }
  

}

///////////////////////////////////// Function for reading the Potentiometer
int speedControl() {
  int customDelay = analogRead(A0); // Reads the potentiometer
  int newCustom = map(customDelay, 0, 1023, 300, 4000); // Convrests the read values of the potentiometer from 0 to 1023 into desireded delay values (300 to 4000)
  return newCustom;
}
////////////////////////////////////
void motorRunClockwise() {
  customDelayMapped = speedControl();
  digitalWrite(stepPin, LOW);
  delayMicroseconds(customDelayMapped);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(customDelayMapped);
  digitalWrite(dirPin,LOW);
  digitalWrite(ms1,HIGH);
  digitalWrite(ms2,HIGH);
  digitalWrite(ms3, HIGH);
}
////////////////////////////////////
void motorRunAntiClockwise() {
  customDelayMapped = speedControl();
  digitalWrite(stepPin, LOW);
  delayMicroseconds(customDelayMapped);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(customDelayMapped);
  digitalWrite(dirPin,HIGH);
  digitalWrite(ms1,HIGH);
  digitalWrite(ms2,HIGH);
  digitalWrite(ms3, HIGH);
}