#include <Stepper.h>

#define BUTTON_START_STOP 7
#define BUTTON_LEFT 5
#define BUTTON_RIGHT 6
#define MAX_SPEED 100

#define PIN_POTENTIOMETER A0

const int stepsPerRevolution = 200;

class Motor : public Stepper
{
  public:
  Motor(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4) : 
  Stepper(number_of_steps, motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4)
  {
    _motorSpeed = 0;
    _directionRight = true;
    _working = false;    
  }
  ~Motor() = default;
  
  void startStop()
  {
    if(_working)
    {
      _working = false;
    }
    else
    {
      _working = true;
    }
  }

  void setRotationRight()
  {
    _directionRight = true;
  }

  void setRotationLeft()
  {
    _directionRight = false;
  }

  void setSpeed(long whatSpeed)
  { 
    if(whatSpeed > 0)
    {
      _motorSpeed = whatSpeed;
      Stepper::setSpeed(whatSpeed);   
    }
    else
    {
      _motorSpeed = 0;
    }    
  }

  void step(int numberSteps)
  {
    if(_working && _motorSpeed > 0)
    {
      if(_directionRight)
      {
        Stepper::step(numberSteps);
      }
      else
      {
        Stepper::step(-numberSteps);
      }      
    }
  }

  private:
  int _motorSpeed;
  bool _directionRight;
  bool _working;  
};

template<int samplesNum>
class AverageFilter
{
  public:
  AverageFilter(int pin)
  {
    pinIn = pin;
  }

  int operator()()
  {
    sum -= samples[curIndex];
    samples[curIndex] = analogRead(pinIn);
    sum += samples[curIndex];
    curIndex = (curIndex + 1) % samplesNum;  
    return sum/samplesNum;
  }

  private:
  int pinIn;
  long long sum = 0;
  long long samples[samplesNum]{};
  int curIndex = 0;
};

template<typename T>
class Button
{
public:
  Button(T* object, void (T::*action)(void), int pinB)
  {
    _object = object;
    _action =action;
    _lastButton = LOW;
    _curButton = LOW;
    _pinButton = pinB;
    pinMode(pinB, INPUT_PULLUP);
  }

  ~Button() = default;

  void process()
  {
    _curButton = debounce();
    if (_lastButton == HIGH && _curButton == LOW)
    {
      (_object->*_action)();
    }
    _lastButton = _curButton;
  }

private:
  T* _object;
  void (T::*_action)(void);
  int _lastButton;
  int _curButton;
  int _pinButton;

  int debounce ()
  {
    int current = digitalRead(_pinButton);
    if(_lastButton != current)
    {
      delay(5);
      current = digitalRead(_pinButton);
    }
    return current;
  }
};

Motor motor(stepsPerRevolution, 8, 9, 10, 11);
AverageFilter<10> filter(PIN_POTENTIOMETER);
Button<Motor> startStopButton(&motor, &Motor::startStop, BUTTON_START_STOP);
Button<Motor> goRightButton(&motor, &Motor::setRotationRight, BUTTON_RIGHT);
Button<Motor> goLeftButton(&motor, &Motor::setRotationLeft, BUTTON_LEFT);

void setup() {
  pinMode(PIN_POTENTIOMETER, INPUT);
  analogReference(DEFAULT);
}

void loop() 
{
  const int k = 2;//кол-во шагов двигателя за один цикл Arduino
  startStopButton.process();
  goRightButton.process();
  goLeftButton.process();
  int motorSpeed = map(filter(), 0, 1023, 0, MAX_SPEED);
  motor.setSpeed(motorSpeed);
  motor.step(k);    
}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
stepper1:A-
stepper1:A+
stepper1:B+
stepper1:B-
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r
btn2:1.l
btn2:2.l
btn2:1.r
btn2:2.r
btn3:1.l
btn3:2.l
btn3:1.r
btn3:2.r
pot1:GND
pot1:SIG
pot1:VCC