#include "SevSeg.h"
#include "Button.h"
#include <TM1637.h>
#include "HX711_ADC.h"
#include <Servo.h>

Servo ServoGate;

HX711_ADC LoadCell(A5, A4);
TM1637 SEGMENT_TM1, SEGMENT_TM2;

Button PIN_UP(A0);
Button PIN_DOWN(A1);
Button PIN_SETUP(A2); 
Button PIN_CANCEL(A3);

Button BTN_GATE_OPEN(2);
Button BTN_MACHINE_START(3);

byte directionPin = 12;
byte stepPin = 13;
int pulseWidthSec = 1;  // microsecondo

// How many of the shift registers
#define NUM_SHIFT_REGS 1

#define DATA_PIN  5  // Pin connected to DS of 74HC595
#define LATCH_PIN 6  // Pin connected to STCP of 74HC595
#define CLOCK_PIN 7  // Pin connected to SHCP of 74HC595

const uint8_t numOfRegisterPins = NUM_SHIFT_REGS * 8;

bool registers[numOfRegisterPins];

void clearRegisters() {
  // Reset all register pins
  for (int i = numOfRegisterPins - 1; i >= 0; i--) {
    registers[i] = LOW;
  }
}
void writeRegisters() {
  // Set and display registers
  digitalWrite(LATCH_PIN, LOW);

  for (int i = numOfRegisterPins - 1; i >= 0; i--) {
    digitalWrite(CLOCK_PIN, LOW);
    digitalWrite(DATA_PIN, registers[i]);
    digitalWrite(CLOCK_PIN, HIGH);
  }

  digitalWrite(LATCH_PIN, HIGH);
}

void setup() {
  // Serial.begin(4800);
  Serial.begin(9600);
  ServoGate.attach(4);  // attaches the servo on pin 4 to the servo object

  SEGMENT_TM1.begin(8, 9, 4);       //  (clockpin, datapin, #digits)
  SEGMENT_TM1.displayClear();
  SEGMENT_TM1.setBrightness(7);     // full brightness, default is 3

  SEGMENT_TM2.begin(10, 11, 4);     //  clockpin, datapin, #digits
  SEGMENT_TM2.displayClear();
  SEGMENT_TM2.setBrightness(7);     // full brightness, default is 3

  int holding_button_time_trigger = 700;
  int holding_button_speed = 5;

  PIN_UP.begin();
  PIN_UP.set_repeat(holding_button_time_trigger, holding_button_speed);

  PIN_DOWN.begin();
  PIN_DOWN.set_repeat(holding_button_time_trigger, holding_button_speed);

  PIN_SETUP.begin();
  PIN_SETUP.set_repeat(holding_button_time_trigger, -1);

  PIN_CANCEL.begin();

  BTN_GATE_OPEN.begin();
  BTN_MACHINE_START.begin();
  // PIN_SETUP.set_repeat(holding_button_time_trigger, -1);

  LoadCell.begin();
  LoadCell.start(1000);
  LoadCell.setCalFactor(0.42);

  pinMode(directionPin, OUTPUT);
  pinMode(stepPin, OUTPUT);

  pinMode(DATA_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(LATCH_PIN, OUTPUT);

  clearRegisters();
  writeRegisters();
}

int target_weigth = 3000; // init target_weigth
int previos_value = 0;

int max_limit = 5000;
int min_limit = 100;

char buffer[5];

void display_amount(TM1637 seqment_display, String amt_value) {
  String amount = amt_value;
  int str_amt_len = amount.length();
  if (str_amt_len == 3) {
    amount = " " + amt_value;
  } else if (str_amt_len == 2) {
    amount = "  " + amt_value;
  } else if (str_amt_len == 1) {
    amount = "    " + amt_value;
  }

  buffer[0] = amount.charAt(0);
  buffer[1] = amount.charAt(1);
  buffer[2] = amount.charAt(2);
  buffer[3] = amount.charAt(3);
  buffer[4] = '\0';
  seqment_display.displayPChar(buffer);
}


void blink(TM1637 display, String blink_text) {
  for (int l = 0; l < 3; l++) {
    display_amount(display, "      ");
    delay(250);
    display_amount(display, blink_text);
    delay(250);
  }
  display_amount(display, "      ");
}

String state  = "IDLE";


void main_ctrl() {
  motor_status();

  if (state == "IDLE" ) {
    display_amount(SEGMENT_TM1, String(target_weigth));
  }
  if (state == "RUN") {
    display_amount(SEGMENT_TM1, String(target_weigth));

    motor_running();

  } else if (state == "SETTING") {
    if (PIN_UP.pressed() ) {
      previos_value += 1;
      if (previos_value > max_limit) {
        blink(SEGMENT_TM1, "Error---");
        delay(500);
        previos_value = max_limit;
      }
    }
    if (PIN_DOWN.pressed() && state == "SETTING") {
      previos_value -= 1;
      if (previos_value < min_limit) {
        blink(SEGMENT_TM1, "Error---");
        delay(500);
        previos_value = 100;
      }
    }
    display_amount(SEGMENT_TM1, String(previos_value));
  }

  if (BTN_MACHINE_START.pressed() ) {
    if (state == "RUN") {
      state = "STOP";
    } else if (state == "IDLE" || state == "STOP") {
      state = "RUN";
    }
    Serial.println("BTN_MACHINE_START :" + state);
  }

  if (PIN_SETUP.pressed()) {
    if (state == "IDLE" || state == "STOP") {
      // enter edit state
      blink(SEGMENT_TM1, "-------");
      previos_value = 0 + target_weigth;
      display_amount(SEGMENT_TM1, String(previos_value));
      state = "SETTING";

    } else if (state == "SETTING") {
      // save/exit edit state
      blink(SEGMENT_TM1, "Save");
      delay(1000);
      target_weigth = 0 + previos_value;
      display_amount(SEGMENT_TM1, String(target_weigth));
      state = "IDLE";
    }
    Serial.println("PIN_SETUP :" + state);
  }

  if (PIN_CANCEL.pressed() && state == "SETTING") {
    blink(SEGMENT_TM1, "XXXXXX");
    display_amount(SEGMENT_TM1, String(target_weigth));
    state = "IDLE";
    Serial.println("PIN_CANCEL :" + state);
  }
}

float get_weight() {
  LoadCell.update();
  return LoadCell.getData();
}


void setRegisterPin(int index, int value) {
  // Set an individual pin HIGH or LOW
  registers[index] = value;
}

String weight_status = "";

float current_weigth = 0;
float last_weight_stop = 0;


int GREEN_LED_GATE = 0;     // 74HC PIN 0
int YELLO_LED_RUNNING = 2;  // 74HC PIN 1
int RED_LED_GATE = 1;     // 74HC PIN 2

int MOTOR_MS1 = 4; // 74HC PIN 4
int MOTOR_MS2 = 5; // 74HC PIN 5
int MOTOR_MS3 = 6; // 74HC PIN 6

float led_blink = millis();
bool led_state = false;
void motor_status() {
  // YELLO_LED_RUNNING
  if (state == "RUN") {
    float step = millis();
    if (step - led_blink > 1000) {
      led_state = !led_state;
      led_blink = step;
    }
    ic74HC_Control(YELLO_LED_RUNNING, led_state);
  } else {
    ic74HC_Control(YELLO_LED_RUNNING, false);
  }
}

void motor_ms_control(bool ms1, bool ms2, bool ms3) {
  ic74HC_Control(MOTOR_MS1, ms1);
  ic74HC_Control(MOTOR_MS2, ms2);
  ic74HC_Control(MOTOR_MS3, ms3);
}

void motor_running() {
  float diff_weigth = target_weigth - current_weigth;
  Serial.println("diff_weigth :" + String(diff_weigth, 0));
  if (diff_weigth > 1000) {           // 200 Microsteps/revolution
    motor_ms_control(false, false, false);
  } else if (diff_weigth > 500) {     // 400 Microsteps/revolution
    motor_ms_control(true, false, false);
  } else if (diff_weigth > 200) {     // 800 Microsteps/revolution
    motor_ms_control( false, true, false);
  } else if (diff_weigth > 100) {     // 1600 Microsteps/revolution
    motor_ms_control( true, true, false);
  } else if (diff_weigth > 10) {      // 3200 Microsteps/revolution
    motor_ms_control( true, true, true);
  }
  digitalWrite(directionPin, HIGH);
  digitalWrite(stepPin, HIGH);
  delay(pulseWidthSec);
  digitalWrite(stepPin, LOW);

}


bool toggle = false;
void loop() {
  main_ctrl();

  current_weigth = get_weight();



  if (weight_status != "WEIGHT_OK") {
    display_amount(SEGMENT_TM2, String(current_weigth, 0));
  } else {
    display_amount(SEGMENT_TM2, String(last_weight_stop, 0));
  }

  if (state == "RUN") {
    current_weigth = get_weight();
    if (int(current_weigth) >= target_weigth  ) {
      weight_status = "WEIGHT_OK";
      state = "STOP"; // Motor STOP
      last_weight_stop = current_weigth;

      ic74HC_Control(GREEN_LED_GATE, true);
      blink(SEGMENT_TM2, String(current_weigth, 0));
    }
  }

  if (weight_status == "WEIGHT_OK") {
    if (BTN_GATE_OPEN.pressed()) {
      Serial.println("BTN_GATE_OPEN :" + state);

      ic74HC_Control(GREEN_LED_GATE, false);
      ic74HC_Control(RED_LED_GATE, true);
      delay(1000);
      servoState("OPEN");
      delay(1000);
      ic74HC_Control(RED_LED_GATE, false);
      servoState("CLOSE");
      delay(1000);
      weight_status = "";
      state = "RUN";
    }
  } else {
    ic74HC_Control(GREEN_LED_GATE, false);
  }
}


void ic74HC_Control(int idx, bool state) {
  setRegisterPin(idx, state);
  writeRegisters();
}

int pos = 270;    // variable to store the servo position
void servoState(String status) {
  int degrees_idx = 270;
  if (status == "OPEN") {
    for (pos = 0; pos <= degrees_idx; pos += 1) { // goes from 0 degrees to NNN degrees
      // in steps of 1 degree
      ServoGate.write(pos);              // tell servo to go to position in variable 'pos'
      delay(1);                       // waits 15ms for the servo to reach the position
    }
  } else if (status == "CLOSE") {
    for (pos = degrees_idx; pos >= 0; pos -= 1) { // goes from NNN degrees to 0 degrees
      ServoGate.write(pos);              // tell servo to go to position in variable 'pos'
      delay(1);                       // waits 15ms for the servo to reach the position
    }
  }
}
4-Digit Display
4-Digit Display
74HC595
A4988