/*
  Arduino | coding-help
  4 digit 7 segment display not printing

  ThisIan — January 11, 2025 at 9:37 PM
  I'm in the process of making a timer up to 1 hour and as I try
  to integrate seconds in my timer the display stops working.
  I can confirm this is not a hardware/physical issue. Please feel
  free to ask whatever any part of my code does.

  NOTE: Digit pins should have transistor drivers!
*/

// constants
const uint8_t MAX_DIGITS = 4;
const uint8_t DP_POSITION = 1;  // DP on second digit
const unsigned long INTERVAL = 1000;
const boolean DIGIT_BITS[][8] = {
  // A  B  C  D  E  F  G  DP
  {1, 1, 1, 1, 1, 1, 0, 0}, // 0
  {0, 1, 1, 0, 0, 0, 0, 0}, // 1
  {1, 1, 0, 1, 1, 0, 1, 0}, // 2
  {1, 1, 1, 1, 0, 0, 1, 0}, // 3
  {0, 1, 1, 0, 0, 1, 1, 0}, // 4
  {1, 0, 1, 1, 0, 1, 1, 0}, // 5
  {1, 0, 1, 1, 1, 1, 1, 0}, // 6
  {1, 1, 1, 0, 0, 0, 0, 0}, // 7
  {1, 1, 1, 1, 1, 1, 1, 0}, // 8
  {1, 1, 1, 1, 0, 1, 1, 0}, // 9
  (0, 0, 0, 0, 0, 0, 0, 0)  // blank
};
// pin definitions
const uint8_t BUZZ_PIN = 41;
const uint8_t CLK_PIN = 3;
const uint8_t DT_PIN = 2;
const uint8_t SW_PIN = 39;
const uint8_t DIGIT_PINS[MAX_DIGITS] = {5, 6, 7, 4};
const uint8_t SEG_PINS[] = {37, 33, 29, 25, 23, 35, 31, 27}; // a - g, dp

// global variables
// timer
unsigned long previousTime = 0;
unsigned long startTime = 600; // 3599 = 59:59
volatile unsigned long timeCount = startTime;
uint8_t mode = 0;

// encoder
volatile uint8_t counter = 0;
volatile uint8_t currStateCLK;
volatile uint8_t lastStateCLK;
uint8_t oldBtnState = HIGH; // pin idles HIGH
String currentDir = "";

// function returns true if encoder button is pressed
bool checkButton()  {
  bool isPressed = false;

  int btnState = digitalRead(SW_PIN);
  if (btnState != oldBtnState)  {
    oldBtnState = btnState;
    if (btnState == LOW)  {
      isPressed = true;
      Serial.println("Switch pressed");
    }
    delay(20); // debounce
  }
  return isPressed;
}

void playSound()  {
  tone(BUZZ_PIN, 880);
  delay(250);
  tone(BUZZ_PIN, 440);
  delay(250);
  tone(BUZZ_PIN, 220);
  delay(250);
  noTone(BUZZ_PIN);
}

// for hardware test only
void testDisplay()  {
  writeDigitSegs(0, 1);
  writeDigitSegs(1, 2);
  writeDigitSegs(2, 3);
  writeDigitSegs(3, 4);
}

void updateDisplay(int value)  {
  int minute = (value / 60);
  int second = value % 60;
  value = (minute * 100) + second;
  //Serial.println(value);
  // display results here
  // with leading zero blanking (10 is a blank char)
  int displayValK = value < 1000 ? 10 : (value % 10000) / 1000; // kilo
  int displayValH = (value % 1000) / 100;    // hecto
  int displayValD = (value % 100) / 10;      // deka
  int displayValU = value % 10;              // units
  // write values to display
  writeDigitSegs(0, displayValK);
  writeDigitSegs(1, displayValH);
  writeDigitSegs(2, displayValD);
  writeDigitSegs(3, displayValU);
}

void updateEncoder() {
  // Read the current state of CLK
  currStateCLK = digitalRead(CLK_PIN);

  // If last and current state of CLK are different, then pulse occurred
  // React to only 1 state change to avoid double count
  if (currStateCLK != lastStateCLK  && currStateCLK == 1) {

    // If the DT state is different than the CLK state then
    // the encoder is rotating CCW so decrement
    if (digitalRead(DT_PIN) != currStateCLK) {
      if (mode == 1)  {
        if (timeCount > 60) timeCount = timeCount - 60;
      //} else  {
        //if (timeCount > 0)  timeCount--;
      }
      counter --;
      currentDir = "CCW";
    } else {
      // Encoder is rotating CW so increment
      if (mode == 2)  {
        if (timeCount < 3539) timeCount = timeCount + 60;
      } else  {
        if (timeCount < 3599) timeCount++;
      }
      counter ++;
      currentDir = "CW";
    }

    Serial.print("Direction: ");
    Serial.print(currentDir);
    Serial.print(" | Counter: ");
    Serial.println(counter);
  }

  // Remember last CLK state
  lastStateCLK = currStateCLK;
}

// sets proper segments for "number"
void writeDigitSegs(uint8_t digit, uint8_t number)  {
  for (uint8_t segment = 0; segment < 8; segment++) {
    digitalWrite(SEG_PINS[segment], DIGIT_BITS[number][segment]);
    if (segment == 7 && digit == DP_POSITION) digitalWrite(SEG_PINS[segment], HIGH);  // sets DP
  }
  digitalWrite(DIGIT_PINS[digit], LOW);
  digitalWrite(DIGIT_PINS[digit], HIGH);
}

void setup() {
  Serial.begin(115200);
  for (int i = 0; i < MAX_DIGITS; i++)  {
    pinMode(DIGIT_PINS[i], OUTPUT);
    digitalWrite(DIGIT_PINS[i], HIGH);
  }
  for (int i = 0; i < 8; i++)  {
    pinMode(SEG_PINS[i], OUTPUT);
  }
  pinMode(BUZZ_PIN, OUTPUT);
  pinMode(CLK_PIN, INPUT);
  pinMode(DT_PIN, INPUT);
  pinMode(SW_PIN, INPUT_PULLUP);

  // Read the initial state of CLK
  lastStateCLK = digitalRead(CLK_PIN);

  // Call updateEncoder() when any high/low changed seen
  // on interrupt 0 (pin 2), or interrupt 1 (pin 3)
  attachInterrupt(digitalPinToInterrupt(2), updateEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(3), updateEncoder, CHANGE);
}

void loop() {
  if (checkButton())  {
    mode++;
    if (mode == 3) mode = 0;
    Serial.print("Mode: ");
    Serial.println(mode);
  }

  if (mode == 0 && timeCount > 0)  {
    if (millis() - previousTime >= INTERVAL)  {
      previousTime = millis();
      int minute = (timeCount / 60);
      int second = timeCount % 60;
      /*
        Serial.print("Time count: ");
        Serial.print(timeCount);
        Serial.print("\t\t");
        Serial.print("Display: ");
        Serial.print(minute);
        Serial.print(":");
        if (second < 10) Serial.print("0");
        Serial.println(second);
      */
      timeCount--;
      if (timeCount <= 0)  {

        playSound();
        //timeCount = 0;
      }
    }
  }
  //testDisplay();
  updateDisplay(timeCount);
}