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));
}
}
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.
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.
// 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));
}
}
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