Controlling stepper with buttons. buttons only working at top of loop

Hi I’m new to arduino and trying to control a stepper motor with a4988 controller.

I have 2 buttons and 4 led. One button is the on/off. The other button I want to cycle thru 3 different speeds of the motor. One led is for on/off button. And the other 3 are to indicate which speed mode it is on. I want the motor do go a set amount in one direction and then the same amount back. But I have a problem with the code. The buttons are only working if I press them at the beginning of the loop. If I press any button in the middle nothing happens. Any help would be greatly appreciated.
Here is my code

// Define pins for stepper motor and driver
const byte DIR_PIN = 2;   // Direction pin
const byte STEP_PIN = 3;  // Step pin
const byte EN_PIN = 4;    // Enable pin

// Define pins for buttons and LEDs
const byte ON_OFF_PIN = 5;   // Button to turn on/off the motor
const byte SPEED_PIN = 6;    // Button to cycle through speeds
const byte LED_PINS[] = {7, 8, 9}; // Array of LED pins for speed indicators
const byte MOTOR_LED_PIN = 10; // LED to indicate motor state

// Define motor steps per revolution
const int SPR = 200;

// Define speeds in steps per second
const int SPEEDS[] = {50, 150, 200};

// Define variables to store current state and speed
boolean state = LOW;        // Motor state (LOW = off, HIGH = on)
byte speedIndex = 0;        // Index of current speed in SPEEDS array

void setup() {
  // Set pin modes
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(EN_PIN, OUTPUT);

  pinMode(ON_OFF_PIN, INPUT_PULLUP); // Enable internal pull-up resistor for button
  pinMode(SPEED_PIN, INPUT_PULLUP);

  for (byte i = 0; i < sizeof(LED_PINS); i++) {
    pinMode(LED_PINS[i], OUTPUT);
  }
  pinMode(MOTOR_LED_PIN, OUTPUT);

  setMotorState(false);   // Turn off motor initially
}

void loop() {
  if (digitalRead(ON_OFF_PIN) == LOW) { // If button is pressed
    state = !state; // Toggle motor state
    setMotorState(state);
    delay(50); // Debounce delay
  }

  if (state) { // If motor is on
    if (digitalRead(SPEED_PIN) == LOW) { // If button is pressed
      speedIndex = (speedIndex + 1) % (sizeof(SPEEDS)/sizeof(*SPEEDS)); // Cycle to next speed
      setSpeedIndicator(speedIndex);
      delay(50); // Debounce delay
    }

    // Rotate clockwise for one revolution at current speed
    rotateMotor(SPEEDS[speedIndex], true);
    
    // If motor is turned off during clockwise rotation, exit the loop
    if (!state) {
      return;
    }

    // Rotate counter-clockwise for one revolution at current speed
    rotateMotor(SPEEDS[speedIndex], false);

    // If motor is turned off during counter-clockwise rotation, exit the loop
    if (!state) {
      return;
    }
  }
}

void rotateMotor(int speed, boolean dir) {
  int interval = (100000000/speed)/SPR; // Calculate interval between steps in microseconds
  digitalWrite(DIR_PIN, dir);      // Set direction based on dir parameter

  for (int i = 0; i < SPR; i++) {
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(interval/2);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(interval/2);
  }
}

void setMotorState(boolean state) {
  digitalWrite(EN_PIN, !state);    // Enable/disable motor driver
  digitalWrite(MOTOR_LED_PIN, state); // Turn on/off LED to indicate motor state
  setSpeedIndicator(state ? speedIndex : 255); // Turn off all speed indicators when motor is off
}

void setSpeedIndicator(byte speedIndex) {
  for (byte i = 0; i < sizeof(LED_PINS); i++) {
    digitalWrite(LED_PINS[i], (i == speedIndex));
  }
}

Try:

  1. Press-and-hold
  2. SpeedIndex depends on a division so if the division is zero, the modulous is zero (sizeof(SPEEDS)/sizeof(*SPEEDS)) Serial.print speedIndex and compare with what you expect.

Hi, @steinn39
Welcome to the forum.

Thanks for using code tags. :+1:

Can we please have a circuit diagram?
An image of a hand drawn schematic will be fine, include ALL power supplies, component names and pin labels.

Did you write you code in stages?
Have you got code that JUST controls the stepper, to prove your hardware and software control?

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

Here's your code on Wokwi (give it a try to see if it works OK) ...

1 Like

Your rotateMotor() function could take up to 4 seconds (if speed is 50 SPS) to execute and that is why you cannot get a button response while the motor is running.

If you want to be able to change the speed or turn off the motor while it is moving then you need to execute each motor step in loop() instead of doing it all in one for loop. Use a static or global variable to keep up with which step you are on.

Also I would recommend looking for a change in state of the button rather than the actual state of the button or it's going to be very hard to keep from toggling the motor state really quick or control the speed.

1 Like

See if this works for you:

// Define pins for stepper motor and driver
const byte DIR_PIN = 2;   // Direction pin
const byte STEP_PIN = 3;  // Step pin
const byte EN_PIN = 4;    // Enable pin

// Define pins for buttons and LEDs
const byte ON_OFF_PIN = 5;   // Button to turn on/off the motor
const byte SPEED_PIN = 6;    // Button to cycle through speeds
const byte LED_PINS[] = {7, 8, 9}; // Array of LED pins for speed indicators
const byte MOTOR_LED_PIN = 10; // LED to indicate motor state

// Define motor steps per revolution
const int SPR = 200;

// Define speeds in steps per second
const int SPEEDS[] = {50, 150, 200};

// Define variables to store current state and speed
boolean state = LOW;        // Motor state (LOW = off, HIGH = on)
byte speedIndex = 0;        // Index of current speed in SPEEDS array
byte stepIndex = 0;

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

  // Set pin modes
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(EN_PIN, OUTPUT);

  pinMode(ON_OFF_PIN, INPUT_PULLUP); // Enable internal pull-up resistor for button
  pinMode(SPEED_PIN, INPUT_PULLUP);

  for (byte i = 0; i < sizeof(LED_PINS); i++) {
    pinMode(LED_PINS[i], OUTPUT);
  }
  pinMode(MOTOR_LED_PIN, OUTPUT);
  digitalWrite(DIR_PIN, HIGH);

  setMotorState(false);   // Turn off motor initially
}

void loop() {
  static byte prevOnOffState = digitalRead(ON_OFF_PIN);
  byte onOffState = digitalRead(ON_OFF_PIN);
  static byte prevSpeedState = digitalRead(SPEED_PIN);
  byte speedState = digitalRead(SPEED_PIN);

  if (onOffState != prevOnOffState)  {
    delay(50); // Debounce delay
    prevOnOffState = onOffState;
    if (onOffState == LOW) { // If button is pressed
      state = !state; // Toggle motor state
      setMotorState(state);
    }
  }

  if (speedState != prevSpeedState)  {
    delay(50); // Debounce delay
    prevSpeedState = speedState;
    if (speedState == LOW && state) { // If button is pressed
      speedIndex = (speedIndex + 1) % (sizeof(SPEEDS)/sizeof(*SPEEDS)); // Cycle to next speed
      setSpeedIndicator(speedIndex);
    }
  }
 
  if (state) {
    int interval = (100000000/SPEEDS[speedIndex])/SPR; // Calculate interval between steps in microseconds
    if (stepIndex < SPR) {
      digitalWrite(STEP_PIN, HIGH);
      delayMicroseconds(interval/2);
      digitalWrite(STEP_PIN, LOW);
      delayMicroseconds(interval/2);
      stepIndex++;
    } else {
      digitalWrite(DIR_PIN, !digitalRead(DIR_PIN));
      stepIndex = 0;
    }
  }
}

void setMotorState(boolean state) {
  digitalWrite(EN_PIN, !state);    // Enable/disable motor driver
  digitalWrite(MOTOR_LED_PIN, state); // Turn on/off LED to indicate motor state
  setSpeedIndicator(state ? speedIndex : 255); // Turn off all speed indicators when motor is off
}

void setSpeedIndicator(byte speedIndex) {
  for (byte i = 0; i < sizeof(LED_PINS); i++) {
    digitalWrite(LED_PINS[i], (i == speedIndex));
  }
}
1 Like

Thank you all so much for your help. hope you all have a fantastic day.

if you want simple steppermotorcontrol combined with high responsiveness to button-presses use the mobaTools-library.
The mobatools create the step-pulses in the backround and you can execute a stop-function all the time and the code reacts instantly on the stop

best regards Stefan

1 Like

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