#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ModbusMaster.h>

// Struktur data timer
struct timer_data {
  bool IN;
  bool Q;
  bool BP1;
  unsigned long TN;
  int PT;
  int ET;
};

LiquidCrystal_I2C lcd(0x27, 20, 4);

// Pin untuk tombol dan relay
const int numButtons = 25;
const int buttonPins[numButtons] = { 15, 14, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10 };
bool lastButtonState[numButtons] = { false };

const int numRelays = 25;
const int relayPins[numRelays] = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 };

bool S[26] = { false };
bool S_P[26] = { false };
bool R[26] = { false };
bool B[26] = { false };
bool BM[26] = { false };

bool S_PD[26] = { false };
bool R_PD[26] = { false };
int PT_PD[26] = { 0 };
int ET_PD[26] = { 0 };

unsigned long lastDebounceTime[numButtons];  // Waktu debounce terakhir untuk setiap tombol
const unsigned long debounceDelay = 50;      // Penundaan debounce dalam milidetik

timer_data T[26];  //TON
char data = 0;

// Modbus
bool coils[26];
bool discreteInputs[50];
uint16_t holdingRegisters[50];
uint16_t inputRegisters[26];
ModbusMaster modbus;

// Fungsi delay timer
void TON(timer_data& Var) {
  if (Var.IN) {
    if (!Var.BP1) {
      Var.TN = millis();
      Var.BP1 = true;
    }
    if (!Var.Q) {
      Var.ET = (millis() - Var.TN) / 6000;
      if (Var.ET >= Var.PT) Var.Q = true;
    }
  } else {
    Var.Q = false;
    Var.BP1 = false;
    Var.ET = 0;
  }
}

// Memperbarui status tombol
void DI_Update() {
  for (int i = 0; i < numButtons; i++) {
    int currentState = !digitalRead(buttonPins[i]);
    if (currentState != lastButtonState[i]) {
      lastDebounceTime[i] = millis();
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      S[i + 1] = !currentState;
    }
    lastButtonState[i] = currentState;
  }
}

// Memperbarui status relay
void DO_Update() {
  for (int i = 1; i < numRelays + 1; i++) {
    digitalWrite(relayPins[i - 1], R[i]);
  }
}

// Memperbarui timer
void Timer_Update() {
  for (int i = 1; i < numRelays + 1; i++) {
    if (BM[i]) {
      if (!S_P[i]) {
        S_P[i] = true;
        R[i] = !R[i];
        LCD_Update();
      }
    } else {
      S_P[i] = false;
    }
    T[i].IN = R[i];
    TON(T[i]);
    if (T[i].Q) {
      R[i] = false;
      LCD_Update();
    }
  }
}

// Status relay sebagai string
String R_Status(bool status) {
  if (status) {
    return "ON";
  } else {
    return "OFF";
  }
}

// Memperbarui LCD
void LCD_Update() {
  lcd.setCursor(3, 0);
  lcd.print("CONTROL PENGENDALI");
  lcd.setCursor(0, 1);
  lcd.print("AUTO1:" + R_Status(R[1]) + ":" + "KM13/L1:" + R_Status(R[3]));
  lcd.setCursor(0, 2);
  lcd.print("AUTO2:" + R_Status(R[2]) + ":" + "KM14/L2:" + R_Status(R[4]));
  lcd.setCursor(0, 3);
  lcd.print("KM1/L1:" + R_Status(R[5]) + ":" + "KM1/L2:" + R_Status(R[6]));
}

// Memeriksa perubahan data
void CheckDataChanges() {
  for (int i = 0; i < numRelays + 1; i++) {
    if (S[i] != S_PD[i]) {
      SendDatatoESP32();
      S_PD[i] = S[i];
      Serial.println("update1");
      break;
    }
    if (R[i] != R_PD[i]) {
      SendDatatoESP32();
      R_PD[i] = R[i];
      break;
    }
    if (T[i].PT != PT_PD[i]) {
      SendDatatoESP32();
      EEPROM.put((i * 2), T[i].PT);
      PT_PD[i] = T[i].PT;
      break;
    }
    if (T[i].ET != ET_PD[i]) {
      SendDatatoESP32();
      ET_PD[i] = T[i].ET;
      break;
    }
  }
}

// Menerima data dari ESP32
void ReceiveDataformESP32() {
  for (int i = 1; i < numRelays + 1; i++) {
    B[i] = false;
  }
  if (Serial2.available() > 0) {
    Serial.println("ok");
    String input = Serial2.readStringUntil('\n');
    Serial.println(input);
    for (int i = 0; i < 8; i++) {
      if (input == String(i)) {
        B[i] = true;
        break;
      }
      if (input.startsWith(String("t") + String(i) + "-")) {
        String valueStr = input.substring(3);
        bool isNumeric = true;
        for (size_t j = 0; j < valueStr.length(); j++) {
          if (!isDigit(valueStr.charAt(j))) {
            isNumeric = false;
            break;
          }
        }
        if (isNumeric) {
          T[i].PT = valueStr.toInt();
          Serial.println(T[i].PT);
        }
      }
    }
  }
}

// Memperbarui Modbus
void Modbus_Update() {
  for (int i = numRelays + 1; i < 40; i++) {
    discreteInputs[i] = true;
  }
  for (int i = 0; i < numRelays; i++) {
    discreteInputs[i] = R[i + 1];
    inputRegisters[i] = (T[i + 1].PT - T[i + 1].ET);
    modbus.poll();
    BM[i + 1] = coils[i];
    if (holdingRegisters[i] != T[i + 1].PT) {
      T[i + 1].PT = holdingRegisters[i];
      holdingRegisters[i] = T[i + 1].PT;
    }
    delay(10);
  }
}

// Mengirim data ke ESP32
void SendDatatoESP32() {
  char buffer[51];
  buffer[0] = 0;
  buffer[2] = 0;
  for (int i = 0; i < 8; i++) {
    bitWrite(buffer[0], i, S[i]);
    bitWrite(buffer[2], i, R[i]);
  }
  buffer[1] = ' ';
  for (int i = 0; i < 8; i++) {
    int j = (i * 3) + 3;
    int k = (i * 3) + 27;
    buffer[j] = ' ';
    buffer[j + 1] = (byte)(T[i].PT >> 8);
    buffer[j + 2] = (byte)T[i].PT;
    buffer[k] = ' ';
    buffer[k + 1] = (byte)(T[i].ET >> 8);
    buffer[k + 2] = (byte)T[i].ET;
  }
  for (int i = 0; i < 51; i++) {
    Serial2.print((byte)buffer[i]);
  }
  Serial2.print("\n");
}

void setup() {
  lcd.begin(20, 4);
  lcd.backlight();
  for (int i = 0; i < numButtons; i++) {
    pinMode(buttonPins[i], INPUT);
  }
  for (int i = 0; i < numRelays; i++) {
    pinMode(relayPins[i], OUTPUT);
  }
  Serial.begin(115200);
  Serial2.begin(9600);
  modbus.begin(1, Serial);

  for (int i = 0; i < numRelays; i++) {
    int test;
    EEPROM.get((i * 2), test);
    T[i + 1].PT = (test < 0) ? 200 : test;
    S_PD[i] = S[i];
    R_PD[i] = R[i];
    PT_PD[i] = T[i + 1].PT;
    ET_PD[i] = T[i + 1].ET;
  }
}

void loop() {
  DI_Update();
  Timer_Update();
  DO_Update();
  CheckDataChanges();
  Modbus_Update();
  ReceiveDataformESP32();
}
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module