// https://wokwi.com/projects/357333073299191809
// https://forum.arduino.cc/t/stepper-motors-limit-switches-and-buttons/1089118

// this has ben slightly modified from the version posted on the forum thread

// Define pins for stepper motor control
#define DIR_PIN         8
#define STEP_PIN        9

// Define pins for buttons and limit switches
#define UP_BUTTON       2
#define DOWN_BUTTON     3
#define STOP_BUTTON     4
#define LIMIT_SWITCH_1  5
#define LIMIT_SWITCH_2  6

enum MotorStates
{
    STOPPED=0,
    MOVING
};

#define BTN_PRESSED     LOW
#define LIMSW_PRESSED   HIGH

const uint32_t kFadeTime = 3000000ul;   //3-second ramp time
const uint32_t kInitSpeed = 1000ul;     //2x == uS/step
const uint32_t kFinalSpeed = 250ul;     //2x == uS/step
const uint32_t kAccelDelay = (uint32_t)(kFadeTime / (kInitSpeed - kFinalSpeed) );
uint32_t 
    ulStepDelay,
    tNow,
    tAccel = 0ul;
bool
    bAccel;

void setup() 
{
    // Set pins for motor control as output
    pinMode(DIR_PIN, OUTPUT);
    pinMode(STEP_PIN, OUTPUT);
    
    // Set pins for buttons and limit switches as input
    pinMode(UP_BUTTON, INPUT_PULLUP);
    pinMode(DOWN_BUTTON, INPUT_PULLUP);
    pinMode(STOP_BUTTON, INPUT_PULLUP);
    pinMode(LIMIT_SWITCH_1, INPUT_PULLUP);
    pinMode(LIMIT_SWITCH_2, INPUT_PULLUP);
    
    // Set initial state of DIR_PIN to LOW
    digitalWrite(DIR_PIN, LOW);

    bAccel = false;

    setupDisplay();
    
}//setup

void loop() 
{
    static uint8_t
        motorState = STOPPED,
        pinLimit;

    rackService(motorState, pinLimit);
            
    if( digitalRead( STOP_BUTTON ) == BTN_PRESSED )
    {
        motorState = STOPPED;
    }

    switch( motorState )
    {
        case    STOPPED:
            if( digitalRead( UP_BUTTON ) == BTN_PRESSED )
            {
                digitalWrite(DIR_PIN, LOW);
                pinLimit = LIMIT_SWITCH_1;
                ulStepDelay = kInitSpeed;
                tAccel = micros();
                bAccel = true;
                motorState = MOVING;
                
            }//if
            else if( digitalRead( DOWN_BUTTON ) == BTN_PRESSED )
            {
                digitalWrite(DIR_PIN, HIGH);
                pinLimit = LIMIT_SWITCH_2;
                ulStepDelay = kInitSpeed;
                tAccel = micros();
                bAccel = true;
                motorState = MOVING;
                
            }//if
            
        break;
        
        case    MOVING:
            tNow = micros();
            if( bAccel )
            {                
                if( (tNow - tAccel) >= kAccelDelay )
                {
                    tAccel = tNow;
                    ulStepDelay--;
                    if( ulStepDelay == kFinalSpeed )
                        bAccel = false;
                        
                }//if
                
            }//if
            
            if( digitalRead( pinLimit ) != LIMSW_PRESSED )
            {
                digitalWrite(STEP_PIN, HIGH);
                delayMicroseconds(ulStepDelay);
                digitalWrite(STEP_PIN, LOW);
                delayMicroseconds(ulStepDelay);
                
            }//if
            else
            {
                motorState = STOPPED;
            }//if
            
        break;
        
    }//switch
    
}//loop


//

# include <Adafruit_NeoPixel.h>

# define LED_PIN    A2
# define LED_COUNT  25

# define upperFakeLimit A0
# define lowerFakeLimit A1

# define SPEED 277  // ms per step

Adafruit_NeoPixel rack(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setupDisplay()
{
    pinMode(lowerFakeLimit, OUTPUT);
    pinMode(upperFakeLimit, OUTPUT);

    digitalWrite(lowerFakeLimit, LOW);
    digitalWrite(upperFakeLimit, LOW);
       
    rack.begin();

    rack.setPixelColor(LED_COUNT / 2, 0x00ff00);
    rack.show();
}

void rackService(uint8_t state, uint8_t direction)
{
  static byte position = LED_COUNT / 2;
  static unsigned long lastDisplay;

  unsigned long now = millis();

  if (state != MOVING) return;
  if (now - lastDisplay < SPEED) return;

  lastDisplay = now;

  rack.setPixelColor(position, 0x101010);
  if (direction == LIMIT_SWITCH_1)
    position++;
  else
    position--;

  if (position >= LED_COUNT) {
      position = LED_COUNT - 1;
  }

  if (position < 0) position = 0;

  digitalWrite(lowerFakeLimit, position <= 0 ? HIGH : LOW);
  digitalWrite(upperFakeLimit, position >= (LED_COUNT - 1) ? HIGH : LOW);
  
  rack.setPixelColor(position, 0x0000ff);
  rack.show();

  lastDisplay = now;
}

  
A4988
LIMIT (NC)
LIMIT (NC)
<-proxies
UPPER
LOWER