/*
  Forum: https://forum.arduino.cc/t/millis-mit-if-bedingung/1290488/5
  Wokwi: https://wokwi.com/projects/405851025462290433

*/

#include <Keypad.h>
#include <AccelStepper.h>
#include <PololuMaestro.h>

// Größe des Keypads definieren
const byte COLS = 4;  // 4 Spalten
const byte ROWS = 4;  // 4 Zeilen

// Die Ziffern und Zeichen des Keypads
char hexaKeys[ROWS][COLS] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};

byte colPins[COLS] = { 11, 12, 4, 5 };  // Pins für die 4 Spalten
byte rowPins[ROWS] = { 7, 6, 8, 9 };    // Pins für die 4 Zeilen

char Taste;  // Variable für die jeweils gedrückte Taste
Keypad Tastenfeld = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

const int relayPins[] = { A0, A1, A2, A3, A4, A5, A6 };
const int numRelays = sizeof(relayPins) / sizeof(relayPins[0]);

unsigned long previousMillis = 0;
int currentPatternIndex = 0;
bool patternActive = false;
int patternLength = 0;

// Die folgende Struktur beinhaltet
// die Nummer des zu schaltenden Relais und
// die Dauer des Einschaltens (Schaltintervall) in [ms]
struct relayType {
  int relay;
  unsigned long interval;
};

relayType *currentPattern = nullptr;

// Beispielmuster
// linker Wert = Relaisnummer
// rechter Wert = Schaltintervall
const relayType pattern0[] =
{ {0, 4000},
  {1, 4000},
  {3, 4000}
};

const relayType pattern1[] =
{ {2, 2000},
  {3, 3000}
};

const relayType pattern2[] =
{
  {0, 4000},
  {1, 4000},
  {3, 4000}
};

// Das folgende Macro ergibt die Anzahl der Einträge in den Arrays vom Typ "relayType"
#define ANZAHL(x) sizeof(x)/sizeof(relayType)


#ifdef SERIAL_PORT_HARDWARE_OPEN
#define maestroSerial SERIAL_PORT_HARDWARE_OPEN
#else
#include <SoftwareSerial.h>
SoftwareSerial maestroSerial(18, 19);
#endif

MicroMaestro maestro(maestroSerial);
const int open = 8000;
const int close = 6000;

AccelStepper stepper(1, 2, 3);


int endstop_1 = A14;
int endstop_2 = A15;

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

  //Endstop
  pinMode(endstop_1, INPUT);
  pinMode(endstop_2, INPUT);

  stepper.setMaxSpeed(1000);
  stepper.setAcceleration(1000);
  stepper.setSpeed(100);

  for (int i = 0; i < numRelays; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], HIGH);  // Relais anfangs ausschalten (HIGH bedeutet ausgeschaltet für ein Relaismodul)
  }
}

void loop() {
  Taste = Tastenfeld.getKey();

  if (Taste) {  // Wenn eine Taste gedrückt wurde
    Serial.print("Die Taste ");
    Serial.print(Taste);
    Serial.println(" wurde gedrückt");
    switch (Taste) {
      case '0': if (digitalRead(endstop_1) != LOW) break;  // Wenn endstop_1 ungleich LOW, geht's nicht weiter
        prepare(); // Hier wird alles abgerufen, was bei der Taste '0' vor dem Schalten der Relais  erfolgt
        startPattern(pattern0, ANZAHL(pattern0));
        break;
      case '1' : startPattern(pattern1, ANZAHL(pattern1));
        break;
      case  '2': startPattern(pattern2, ANZAHL(pattern2));
        break;
    }
  }

  if (patternActive) {
    if (millis() - previousMillis >= currentPattern[currentPatternIndex].interval) {
      switchRelay(HIGH);
      currentPatternIndex++;
      if (currentPatternIndex < patternLength) {
        switchRelay(LOW);
      } else {
        patternActive = false;  // Muster ist beendet
        Serial.println("Muster abgeschlossen");
      }
    }
  }
}

// Vorgeplänkel im Fall der Taste '0'
void prepare() {
  maestro.setSpeed(50, 50);
  maestro.setAcceleration(10, 10);
  setStepperAndMaestro(800, -3730, true);
  setStepperAndMaestro(400, -3250, true);
  setStepperAndMaestro(400, -2680, true);
  setStepperAndMaestro(400, -1800, false);
}


// Diese Funktion wird an mehreren Stellen aufgerufen, um Relais ein- und auszuschalten
void switchRelay (byte state) {
  previousMillis = millis();  // Hier wird die Schaltzeit für das Intervall gesetzt
  digitalWrite(relayPins[currentPattern[currentPatternIndex].relay], LOW);  // Nächstes Relais einschalten
  Serial.print("Relais ");
  Serial.print(currentPattern[currentPatternIndex].relay + 1);
  Serial.println((state == LOW) ? " eingeschaltet" : " ausgeschaltet");
}

void startPattern(const struct relayType pattern[], int length) {
  currentPattern = pattern;
  patternLength = length;
  currentPatternIndex = 0;
  patternActive = true;
  switchRelay(LOW);
}

// Wird bei prepare() mehrfach aufgerufen
void setStepperAndMaestro(int maxSpeed, int newPos, boolean setMaestro) {
  stepper.setMaxSpeed(maxSpeed);
  stepper.runToNewPosition(newPos);
  if (setMaestro) {
    maestro.setTarget(0, open);
    delay(3000);
    maestro.setTarget(0, close);
  }
}