// libraries import
#include "HX711.h"

// Rotary Encoder:
int pEncCLK = 2;
int pEncDT = 5; // also as interrupt??
int pEncSW = 3;  // button -> interrupt
int goalValue = 0;
boolean direction;  // true: clockwise, false: counterclockwise
int lastCLK;
int currentCLK;

// Relay
int pRelIn =  4;

// HX711
int pLoadDT = 13;
int pLoadSCK = 12;
int currentWeight;
HX711 scale;

// 7 segment 1
int pSeg1Latch = 10;  // STCP
int pSeg1Data = 9;  // DS
int pSeg1Clock = 11;  //SHCP
// 7 segment 2
int pSeg2Latch = 7;
int pSeg2Data = 6;
int pSeg2Clock = 8;

int digits [10][8]{
// a,b,c,d,e,f,g,.
  {0,0,0,0,0,1,0,1}, // 0
  {1,0,0,1,1,1,1,1}, // 1
  {0,0,1,0,0,0,1,1}, // 2
  {0,0,0,0,1,0,1,1}, // 3
  {1,0,0,1,1,0,0,1}, // 4
  {0,1,0,0,1,0,0,1}, // 5
  {0,1,0,0,0,0,0,1}, // 6
  {0,0,0,1,1,1,1,1}, // 7
  {0,0,0,0,0,0,0,1}, // 8
  {0,0,0,0,1,0,0,1}  // 9
};

// additional variables
boolean choiceMade = false;
boolean running = false;
boolean start = false;
float timeout = 20*1000;


void setup() {
  Serial.begin(9600);

  pinMode(pEncCLK, INPUT_PULLUP);
  pinMode(pEncDT, INPUT_PULLUP);
  pinMode(pEncSW, INPUT_PULLUP);

  pinMode(pRelIn, OUTPUT);

  pinMode(pLoadDT, INPUT);
  pinMode(pLoadSCK, INPUT);

  pinMode(pSeg1Latch, OUTPUT);
  pinMode(pSeg1Data, OUTPUT);
  pinMode(pSeg1Clock, OUTPUT);

  pinMode(pSeg2Latch, OUTPUT);
  pinMode(pSeg2Data, OUTPUT);
  pinMode(pSeg2Clock, OUTPUT);

  attachInterrupt(digitalPinToInterrupt(pEncCLK), getEncoderValue, CHANGE);
  attachInterrupt(digitalPinToInterrupt(pEncSW), encoderClick, RISING);

  scale.begin(pLoadDT, pLoadSCK);
  lastCLK = digitalRead(pEncCLK);

  DisplayNumber(0);
}

void loop() {
  runningRoutine();
}

// Display 1
void DisplayDigitSeg1(int Digit)
{
  digitalWrite(pSeg1Latch,LOW);
  if ( Digit < 0 || Digit > 9 ) {
    for (int i = 7; i>=0; i--) {
      digitalWrite(pSeg1Clock, LOW);
      digitalWrite(pSeg1Data, LOW); 
      digitalWrite(pSeg1Clock, HIGH);
    }
  }
  else {
    for (int i = 7; i>=0; i--) {
      digitalWrite(pSeg1Clock, LOW);
      if (digits[Digit][i]==1) digitalWrite(pSeg1Data, LOW); 
      if (digits[Digit][i]==0) digitalWrite(pSeg1Data, HIGH);
      digitalWrite(pSeg1Clock, HIGH);
    }
  }
  digitalWrite(pSeg1Latch, HIGH); 
}

// Display 2
void DisplayDigitSeg2(int Digit)
{
  digitalWrite(pSeg2Latch,LOW);
  if ( Digit < 0 || Digit > 9 ) {
    for (int i = 7; i>=0; i--) {
      digitalWrite(pSeg2Clock, LOW);
      digitalWrite(pSeg2Data, LOW); 
      digitalWrite(pSeg2Clock, HIGH);
    }
  }
  else {
    for (int i = 7; i>=0; i--) {
      digitalWrite(pSeg2Clock, LOW);
      if (digits[Digit][i]==1) digitalWrite(pSeg2Data, LOW); 
      if (digits[Digit][i]==0) digitalWrite(pSeg2Data, HIGH);
      digitalWrite(pSeg2Clock, HIGH);
    }
  }
  digitalWrite(pSeg2Latch, HIGH); 
}

// Convert 2 digit number to the 2 displays
void DisplayNumber(int number) {
  if ( number/10 == 0 )
    DisplayDigitSeg1(-1);
  else
    DisplayDigitSeg1(number/10);
  DisplayDigitSeg2(number%10);
}

// Rotary encoder changed, get value and change goalValue accordingly
void getEncoderValue() {
  choiceMade = true;
  currentCLK = digitalRead(pEncCLK);
  if (currentCLK != lastCLK) {     
    if (digitalRead(pEncDT) != currentCLK) {  
      goalValue ++;
      direction = true;
    }
    else {
      direction = false;
      if (goalValue > 0)
        goalValue--;
    }
  }
  DisplayNumber(goalValue);
}

// button click of encoder, either starts, stops or goes into choice mode
void encoderClick() {
  if ( !running && !start && choiceMade ) {
    start = true;
  }
  else if ( !running && !choiceMade ) {
    DisplayNumber(goalValue);
    choiceMade = true;
  }
  else {
    start = false;
    digitalWrite(pRelIn, LOW);
    running = false;
  }
}

// routine for grinding, depending on start signal, measured weight, stop signal and timeout 
void runningRoutine() {
  if ( start && !running ) {
    detachInterrupt(pEncCLK);
    choiceMade = false;
    running = true;
    float timeStart = millis();
    float currentTime = millis() - timeStart;
    boolean tOut = false;
    digitalWrite(pRelIn, HIGH);
    currentWeight = scale.get_value(5)/420*1000;
    while ( currentWeight < goalValue && start ) {
      currentWeight = scale.get_value(5)/420*1000;
      DisplayNumber(currentWeight);
      currentTime = millis() - timeStart;
      if ( currentTime > timeout ) {
        tOut = true;
        break;
      }
    }
    start = false;
    digitalWrite(pRelIn, LOW);
    running = false;
    if ( tOut ) 
      breakRun();
    DisplayNumber(currentWeight);
    attachInterrupt(digitalPinToInterrupt(pEncCLK), getEncoderValue, CHANGE);
  }
}

// display the timeout by blinking the display
void breakRun () {
  for ( int i = 0; i < 4; i++) {
    DisplayNumber(currentWeight);
    delay(500);
    DisplayDigitSeg1(-1);
    DisplayDigitSeg2(-1);
    delay(500);
  }
}
NOCOMNCVCCGNDINLED1PWRRelay Module
74HC595
74HC595