/* Definicje pinów
  Pin definitions */
// 40 Button, 10 column, 4 row, 5 x 74HC165
// Wejścia przycisków (Serial Load)
// Button input
const int REG_SL = 2;
// Zegar dla przycisków (CK - Clock)
// Input clock
const int REG_CK = 3;
// Dane wejściowe przycisków (DI - Data In)
// Input data
const int REG_DI = 4;

// 40 Relay, 5 x 74HC595
// Wyjście sterujące przekaźnikami (Latch Pin)
// Relay control output
const int latchPin = 5;
// Zegar dla przekaźników
// Relay output clock
const int clockPin = 6;
// Dane wyjściowe przekaźników
// Relay output data
const int dataPin = 7;

// 40 Feedback Relay, 5 x 74HC165
// Feedback przekaźników
// Feedback state input
const int REG_SL2 = 8;
// Zegar stanu Feedback
// Feedback state clock
const int REG_CK2 = 9;
// Dane stanu Feedback
// Feedback state data
const int REG_DI2 = 10;

// 40 LED, 5 x 74HC595
// Wyjście sterujące diodami LED
// LED control output
const int latchPin2 = 11;
// Zegar wyjściowy LED
// LED output clock
const int clockPin2 = 12;
// Dane wyjściowe LED
// LED output data
const int dataPin2 = 13;

byte buttonStates[5];     // Stany przycisków (5 bajtów)
byte relayStates[5];      // Stany przekaźników (5 bajtów)
byte feedbackStates[5];   // Stany feedbacku przekaźników (5 bajtów)
byte ledStates[5];        // Stany diod LED (5 bajtów)
byte prevLedStates[5];    // Poprzednie stany LED
byte prevFeedbackStates[5]; // Poprzednie stany feedbacku

const int pulseDelay = 2; // Opóźnienie zegara
const unsigned long relayOnTime = 500; // Czas włączenia przekaźnika (1 sekunda)
unsigned long relayTimers[40]; // Tablica czasów włączenia przekaźników

void setup() {
  Serial.begin(9600);
  pinMode(REG_SL, OUTPUT);
  pinMode(REG_CK, OUTPUT);
  pinMode(REG_DI, INPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(REG_SL2, OUTPUT);
  pinMode(REG_CK2, OUTPUT);
  pinMode(REG_DI2, INPUT);
  pinMode(latchPin2, OUTPUT);
  pinMode(clockPin2, OUTPUT);
  pinMode(dataPin2, OUTPUT);
  //Serial.println("READY");
}

void loop() {
  ReadButtons();
  HandleButtons();
  ReadFeedback();
  UpdateRelays();
  UpdateLEDs();
  delay(50);
}

// Funkcje dla pierwszego Arduino (odczyt przycisków i wysyłanie poleceń)
void ReadButtons() {
  digitalWrite(REG_SL, LOW);
  delayMicroseconds(pulseDelay);
  digitalWrite(REG_SL, HIGH);
  delayMicroseconds(pulseDelay);

  for (int i = 0; i < 5; i++) {
    byte currentState = ReadOne165(REG_DI, REG_CK);
    buttonStates[i] = currentState;
  }
}

void HandleButtons() {
  for (int i = 0; i < 5; i++) {
    for (int bit = 0; bit < 8; bit++) {
      if (buttonStates[i] & (1 << bit)) {
        int buttonNumber = (bit + 1) + (i * 8);
        //Serial.print("Button ");
        Serial.print(buttonNumber);
        //Serial.println(" pressed.");
        sendRelayCommand(buttonNumber, true); // Wyślij polecenie do drugiego Arduino
        turnRelay(buttonNumber, true); // Włącz przekaźnik na pierwszym Arduino
        ledStates[i] ^= (1 << bit); // Przełącz stan LED
        UpdateLEDs(); // Aktualizacja LED
      }
    }
  }
}

void sendRelayCommand(int relayNumber, bool state) {
  //Serial.print("RELAY: ");
  Serial.print(relayNumber);
  //Serial.println(state ? " ON" : " OFF");
}

// Funkcje dla drugiego Arduino (sterowanie przekaźnikami)
void turnRelay(int relayNumber, bool state) {
  int byteIndex = (relayNumber - 1) / 8;
  int bitIndex = (relayNumber - 1) % 8;

  if (state) {
    bitSet(relayStates[byteIndex], bitIndex);
    relayTimers[relayNumber - 1] = millis();
  } else {
    bitClear(relayStates[byteIndex], bitIndex);
    relayTimers[relayNumber - 1] = 0;
  }

  digitalWrite(latchPin, LOW);
  for (int i = 4; i >= 0; i--) {
    shiftOut(dataPin, clockPin, MSBFIRST, relayStates[i]);
  }
  digitalWrite(latchPin, HIGH);

  //Serial.print("Relay ");
  Serial.print(relayNumber);
  //Serial.println(state ? " ON." : " OFF.");
}

void UpdateRelays() {
  for (int i = 0; i < 40; i++) {
    if (relayTimers[i] != 0 && millis() - relayTimers[i] >= relayOnTime) {
      turnRelay(i + 1, false);
    }
  }
}

// Funkcje wspólne (odczyt feedbacku i aktualizacja LED)
void ReadFeedback() {
  digitalWrite(REG_SL2, LOW);
  delayMicroseconds(pulseDelay);
  digitalWrite(REG_SL2, HIGH);
  delayMicroseconds(pulseDelay);

  for (int i = 0; i < 5; i++) {
    byte currentState = ReadOne165(REG_DI2, REG_CK2);
    if (currentState != feedbackStates[i]) {
      feedbackStates[i] = currentState;
      UpdateLEDs();
    }
  }
}

void UpdateLEDs() {
  if (memcmp(ledStates, prevLedStates, 5) != 0) { // Sprawdź, czy stan LED się zmienił
    digitalWrite(latchPin2, LOW);
    for (int i = 4; i >= 0; i--) {
      shiftOut(dataPin2, clockPin2, MSBFIRST, ledStates[i]);
    }
    digitalWrite(latchPin2, HIGH);
    for (int i = 0; i < 40; i++) {
      int byteIndex = i / 8;
      int bitIndex = i % 8;
      if (bitRead(ledStates[byteIndex], bitIndex) != bitRead(prevLedStates[byteIndex], bitIndex)) { // Sprawdź, czy stan LED się zmienił
        //Serial.print("LED nr ");
        Serial.print(i + 1);
        //Serial.println(bitRead(ledStates[byteIndex], bitIndex) ? " ON" : " OFF"); // Wyświetl tylko zmienione LEDy
      }
    }
    memcpy(prevLedStates, ledStates, 5); // Aktualizuj poprzedni stan LED
  }
}

// Funkcja pomocnicza
byte ReadOne165(int dataPin, int clockPin) {
  byte ret = 0;
  for (int i = 0; i < 8; i++) {
    ret = (ret << 1) | digitalRead(dataPin);
    digitalWrite(clockPin, HIGH);
    delayMicroseconds(pulseDelay);
    digitalWrite(clockPin, LOW);
  }
  return ret;
}
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC595
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
74HC165
DS: dataPin (cyan) SHCP: clockPin (violet) STCP: latchPin (limegreen)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
1 ... 2 ... 3 ... 4 ... 5 ... 6 ... 7 ... 8 ... 9 ... 10
11 . 12 .13 . 14 . 15 .16 . 17 . 18 .19 . 20
21 . 22 .23 . 24 . 25 .26 . 27 . 28 .29 . 30
31 . 32 .33 . 34 . 35 .36 . 37 . 38 .39 . 40
RELAY 1
RELAY 40