#define buttonPin 12
#define revolutionPin A0
#define dirPin 2
#define stepPin 3

#define MIN_RAW 0
#define MAX_RAW 1023
#define MIN_REVOLUTION 1
#define MAX_REVOLUTION 1000
#define STEPS_PER_REVOLUTION 200
#define DEFAULT_REVOLUTION 60
#define INTERVAL_LCD_PRINT 250
#define ONE_SECOND 1010000UL

enum {
  CCW,
  CW
};

#define DEBOUNCE_PERIOD 10UL  //  Debounce Delay 10 milliseconds
typedef struct {
  private:
    bool input;
    unsigned long startTime;

  public:
    bool state;

    bool debounce(bool bounce) {
      unsigned long currentTime = millis();
      bool prevState = state;
      if (bounce) {
        state = true;
      } else {
        if (state) {
          if (input) {
            startTime = currentTime;
          }
          unsigned long elapsedTime = currentTime - startTime;
          if (elapsedTime >= DEBOUNCE_PERIOD) {
            state = false;
          }
        }
      }
      input = bounce;
      return state != prevState & state == true;
    }
} debounce_t;


debounce_t buttonDebounce;

bool start;
bool direction;
uint32_t timeDelay;

uint32_t startTime;

void ButtonCondition();
uint32_t rpm(long rpm, long steps);
void StepperMotor(bool enable, bool direction, uint32_t timeDelay = 10L);


void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);

  direction = CW;
  digitalWrite(dirPin, direction);
  digitalWrite(stepPin, LOW);
}

void loop() {
  Revolution();
  ButtonCondition();
  StepperMotor(start, direction, timeDelay);
}

void ButtonCondition() {
  bool pressed = buttonDebounce.debounce(!digitalRead(buttonPin));
  uint32_t currTime = micros();
  if (pressed) {
    start = !start;
    startTime = currTime;
  }
  if (start) {
    uint32_t elapsedTime = currTime - startTime;
    if ( elapsedTime >= ONE_SECOND) {
      start = false;
    }
  }
}

void Revolution() {
  long revo = analogRead(revolutionPin);
  revo = map(revo, 0, 1023, 60, 1000);
  timeDelay = rpm(revo, STEPS_PER_REVOLUTION);
}

void StepperMotor(bool en, bool dir, uint32_t timeDelay) {
  static bool enable;
  static uint32_t startTime;

  uint32_t currTime = micros();
  if (en & !enable) {
    startTime = currTime;
  }
  enable = en;
  if (enable) {
    uint32_t elapsedTime = currTime - startTime;
    if (elapsedTime >= timeDelay) {
      digitalWrite(dirPin, dir);
      digitalWrite(stepPin, HIGH);
      digitalWrite(stepPin, LOW);
      startTime = currTime;
    }
  }
}

// Revolution per minute
uint32_t rpm(long rpm, long steps) {
  unsigned long div = (unsigned long)rpm * (unsigned long)steps;
  return 60000000UL / div;
}
A4988