Hi,
Frequent visitor, first time poster. Appreciate all the topics and help out there. I'm generally new to Arduino, though I've completed a few successful projects so far. This one is stumping me.
I'm making a motorized winder with a NEMA17 stepper and A4988 driver which defaults to 31 turns, but can be increased to 100. I have 3 buttons:
- run (executes the desired number of turns, a second press during the run cycle aborts the process and resets the count)
- advance (meant to turn the motor about 30rpm or 100 steps/sec while it's being pressed only)
- count (this increases the turn count before wrapping back to 1 after 100).
I also have a TFT showing basic turn cycle info: number of turns and "complete" or "abort" when motor stops.
I'm struggling with the advance button. I'll spare the long winded version and post what it's doing now - the closest I could get. In this version, two things are happening that should not happen:
- advance turns about 10rpm
- advance interrupts run if pressed during the cycle and stops/aborts.
Here's my setup (note: at this moment, screen switch is bypassed, buck is bypassed and board is powered direct from laptop)
Here is my code. I've done several tests so it may have gotten a little messy. Open to any input. Thank you!
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9163.h>
#include <AccelStepper.h>
#include <SPI.h>
#include <SD.h>
// Define stepper motor pins
#define STEP_PIN 2
#define DIR_PIN 3
#define ENABLE_PIN 4
// Define button pins
#define RUN_BUTTON_PIN 5
#define ADVANCE_BUTTON_PIN 6
#define COUNT_BUTTON_PIN 7#include <Adafruit_GFX.h>
#include <Adafruit_ILI9163.h>
#include <AccelStepper.h>
#include <SPI.h>
#include <SD.h>
// Define stepper motor pins
#define STEP_PIN 2
#define DIR_PIN 3
#define ENABLE_PIN 4
// Define button pins
#define RUN_BUTTON_PIN 5
#define ADVANCE_BUTTON_PIN 6
#define COUNT_BUTTON_PIN 7
// Initialize the TFT LCD display
#define TFT_CS 8
#define TFT_RST 9
#define TFT_DC 10
Adafruit_ILI9163 tft = Adafruit_ILI9163(TFT_CS, TFT_DC, TFT_RST);
// Create two AccelStepper objects: one for Run, one for Advance
AccelStepper stepperRun(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);
AccelStepper stepperAdvance(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);
// Define the number of steps per revolution
const int stepsPerRevolution = 200; // 1.8 degrees per step
// Button state variables
bool motorRunning = false;
bool lastRunButtonState = HIGH; // Initialize the last run button state
bool currentRunButtonState = HIGH; // Initialize the current run button state
unsigned long lastButtonPressTime = 0; // Time of last button press
const unsigned long buttonCooldown = 250; // 250ms cooldown period
int currentTurns = 31; // Default turns
const int maxTurns = 100; // Maximum turns before wrapping around
// Advance button state variables
bool advanceMode = false; // Flag to indicate if advance mode is active
bool lastAdvanceButtonState = HIGH; // Last state of advance button
bool currentAdvanceButtonState = HIGH; // Current state of advance button
// Count button debounce
bool lastCountButtonState = HIGH; // Initialize the last count button state
unsigned long lastCountButtonPressTime = 0;
const unsigned long debounceDelay = 100; // Debounce delay for count button
// Flag to disable inputs during "COMPLETE" or "ABORT" message
bool inputDisabled = false;
// Function prototypes
void handleRunButton();
void handleCountButton();
void handleAdvanceButton();
void updateTurnsDisplay();
void displayCompleteMessage(bool aborted);
void setup() {
// Set the motor control pins as outputs
pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
pinMode(ENABLE_PIN, OUTPUT);
// Set the button pins as input with pull-up resistor enabled
pinMode(RUN_BUTTON_PIN, INPUT_PULLUP);
pinMode(ADVANCE_BUTTON_PIN, INPUT_PULLUP);
pinMode(COUNT_BUTTON_PIN, INPUT_PULLUP);
// Initialize the TFT LCD display
tft.begin();
// Set background color and text color
tft.fillScreen(ILI9163_BLACK);
tft.setTextColor(ILI9163_WHITE);
tft.setTextSize(2);
// Print initial message
updateTurnsDisplay(); // Function to update the turns display on TFT
// Set up **Run** mode stepper (high speed and high acceleration)
stepperRun.setMaxSpeed(1600); // Set max speed for run mode
stepperRun.setAcceleration(4000); // Set acceleration for smoother starts/stops in run mode
// Set up **Advance** mode stepper (lower speed and acceleration)
stepperAdvance.setMaxSpeed(1200); // Set max speed for advance mode
stepperAdvance.setAcceleration(1000); // Lower acceleration for advance mode
// Initialize Serial communication
Serial.begin(9600);
Serial.println("Initialization complete.");
}
void loop() {
// Handle run button press and hold
if (!inputDisabled) {
handleRunButton();
}
// Handle advance button press
if (!inputDisabled) {
handleAdvanceButton();
}
// Handle count button press and hold
if (!inputDisabled) {
handleCountButton();
}
// Run the stepper motor if motorRunning is true (only for Run mode)
if (motorRunning) {
digitalWrite(ENABLE_PIN, LOW); // Enable the motor driver
stepperRun.run(); // Run the stepper motor for run mode
// Check if the motor is running and update TFT LCD display with the number of rotations completed
if (stepperRun.distanceToGo() == 0 && !stepperRun.isRunning()) {
digitalWrite(ENABLE_PIN, HIGH); // Disable the motor driver when not running
// Display "COMPLETE" message
displayCompleteMessage(false); // Display "COMPLETE"
// Update turns display after completing the run
updateTurnsDisplay();
// Reset motorRunning flag
motorRunning = false;
}
}
else if (advanceMode) {
// Run the motor at a fixed speed in advance mode
digitalWrite(ENABLE_PIN, LOW); // Enable motor driver
stepperAdvance.setSpeed(1200); // Set speed for advance mode
stepperAdvance.runSpeed(); // Run the motor at set speed
// Debugging: Confirm advance mode is active
Serial.println("Advance mode is active, running motor at set speed.");
}
else {
// Disable motor driver when neither Run nor Advance button is pressed
digitalWrite(ENABLE_PIN, HIGH); // Disable the motor driver
}
}
void handleRunButton() {
currentRunButtonState = digitalRead(RUN_BUTTON_PIN); // Read the state of the button
// Check if button was just pressed (falling edge detection)
if (currentRunButtonState == LOW && lastRunButtonState == HIGH) {
// Check if enough time has passed since last button press (cooldown)
if (millis() - lastButtonPressTime > buttonCooldown) {
lastButtonPressTime = millis(); // Update the last press time
if (!motorRunning) {
// Start motor if not already running
motorRunning = true;
stepperRun.setCurrentPosition(0); // Reset position to zero
stepperRun.moveTo(stepsPerRevolution * currentTurns); // Move to current turns
Serial.println("Motor started");
} else {
// Stop motor if running and reset position
motorRunning = false;
stepperRun.stop(); // Stop the motor
stepperRun.setCurrentPosition(0); // Reset position to zero
Serial.println("Motor stopped, position reset");
// Display "ABORTED" message
displayCompleteMessage(true); // Show the aborted message in red
}
}
}
// Update last button state to current state for the next loop
lastRunButtonState = currentRunButtonState;
}
void handleAdvanceButton() {
currentAdvanceButtonState = digitalRead(ADVANCE_BUTTON_PIN); // Read the state of the advance button
// If the advance button is pressed, enable advance mode
if (currentAdvanceButtonState == LOW && lastAdvanceButtonState == HIGH) {
advanceMode = true; // Set advance mode flag
stepperAdvance.setSpeed(1200); // Set speed for advance mode
Serial.println("Advance button pressed, motor running at 1200 speed"); // Debugging
}
// If the advance button is released, disable advance mode
if (currentAdvanceButtonState == HIGH && lastAdvanceButtonState == LOW) {
advanceMode = false; // Reset advance mode flag
stepperAdvance.setSpeed(0); // Stop the motor
Serial.println("Advance button released, motor stopped"); // Debugging
}
// Update last button state for the next loop
lastAdvanceButtonState = currentAdvanceButtonState;
}
void handleCountButton() {
int reading = digitalRead(COUNT_BUTTON_PIN); // Read the state of the count button
// Check for count button state change with debounce
if (reading != lastCountButtonState && millis() - lastCountButtonPressTime > debounceDelay) {
if (reading == LOW) { // Button pressed
// Record the button press time
lastCountButtonPressTime = millis();
// Increment currentTurns immediately for a short press
currentTurns++;
if (currentTurns > maxTurns) {
currentTurns = 1; // Wrap around to 1 if it exceeds maxTurns
}
// Update the TFT display
updateTurnsDisplay();
}
// Update last button state
lastCountButtonState = reading;
}
}
void updateTurnsDisplay() {
// Clear previous text on TFT display
tft.fillRect(10, 35, 120, 16, ILI9163_BLACK);
// Update TFT display with current turns count
tft.setCursor(20, 35);
tft.print("TURNS:");
tft.println(currentTurns); // Print updated turns count on TFT display
Serial.print("Current turns: ");
Serial.println(currentTurns); // Debugging: Print updated turns count to Serial Monitor
}
void displayCompleteMessage(bool aborted) {
unsigned long startTime = millis();
boolean showComplete = true;
// Disable input during message display
inputDisabled = true;
// Display "COMPLETE" or "ABORT" on TFT for 5 seconds
while (millis() - startTime < 5000) {
// Toggle visibility every 500 milliseconds
if ((millis() - startTime) % 1000 < 500) {
if (showComplete) {
if (aborted) {
tft.setCursor(25, 70);
tft.setTextColor(ILI9163_RED); // Set text color to red for "ABORT"
tft.println("ABORTED");
} else {
tft.setCursor(20, 70);
tft.setTextColor(ILI9163_GREEN); // Set text color to green for "COMPLETE"
tft.println("COMPLETE");
}
showComplete = false;
}
} else {
if (!showComplete) {
tft.fillRect(20, 70, 120, 16, ILI9163_BLACK); // Clear text area
showComplete = true;
}
}
// Allow other tasks to run during display
delay(10); // Small delay to prevent excessive loop iterations
}
// Clear "COMPLETE" or "ABORT" after 5 seconds
tft.fillRect(20, 70, 120, 16, ILI9163_BLACK);
tft.setTextColor(ILI9163_WHITE); // Reset text color to white
// Enable input after message display
inputDisabled = false;
}