#include <ESP32Servo.h>
#include "Arduino.h"
#include "Trommel.h"
#include "LED.h"


//  Arduino UNO pin Signal Kabel

//  1 NeoPixel RGB LED
//  2 Endschalter von der PM
//  4 digitale signal vom TCRT5000
//  7 Signal für PM startknopf
//  6 Stop Taster (der zweite pin vom Taster kommt an GND)
//  5 Start Taster  (der zweite pin vom Taster kommt an GND)
//  8 Das Signalkabel vom Hallsensor
//  9 ServoLaden (servo der die Hülse auf die PowerMatic schiebt)
//  10  ServoStart (servo der Start drückt an der PowerMatic)
//  12  Step pin vom Schritttreiber
//  13  Dir oder direction pin vom Schritttreiber

extern float trommelpos = 0.00; 
float stepdegree;
//Trommel-------------------------------------------------------------------------
//Wieviele schritte der motor braucht für eine komplette umdrehung
//mit einem Schrittmotor der 200(1.8 Grad pro schritt) schritte braucht für eine umdrehung:
//200 ohne microstepping, 400 mit 1/2 microstepping, 800 mit 1/4 microstepping usw.
#define StepperStepsPR 800
//drehrichtung vom motor. 0 = gegen uhrzeigersinn, 1 = mit uhrzeigersinn
#define StepperDir 0
//Wie schnell der motor drehen soll
//kleinerer wert = schneller(min. 100), höher = langsamer
#define StepperSpeed 50000  //def 5000
//falls die position der Trommel nicht ganz stimmt
//Dann macht der motor soviele Schritte mehr, es sind auch negative zahlen erlaubt
//Dreht sie nicht weit genug positive zahl, dreht sie zu weit negative zahl
#define TrommelPosKorrektur -50 //def 4

//Servo Laden-------------------------------------------------------------------------
//Die Servo anfangs position und end position in Grad
//min. 0, max. 180.   7-8 Grad entsprechen etwa 1mm.
#define ServoLadenAnfangPos 2   //Std:2
#define ServoLadenEndPos 163
//Wie schnell der Servo drehen soll
//2 = min. um so höher die zahl um so langsamer, 10 ist schon ziemlich langsam
#define ServoLadenSpeed 6

//Servo Start-------------------------------------------------------------------------
//gibt an ob ein Servo für start drücken an der PM verwendet wird oder
//der PowerMatic start knopf angeklemmt wurde
//0 = ohne servo, 1 = mit servo
#define ServoStartEnable 1
//Die Servo anfangs position und end position in Grad
//min. 0, max. 180
#define ServoStartAnfangPos 2
#define ServoStartEndPos 45
//Wie schnell der Servo drehen soll
//2 = min. um so höher die zahl um so langsamer, 10 ist schon ziemlich langsam
#define ServoStartSpeed 2

//IR TCRT5000-----------------------------------------------------------------
//0 = ohne IR sensor, 1 = mit IR sensor
#define IREnable 1


//LED-------------------------------------------------------------------------
//0 = aus(min.),  255 = Hell(max.)
#define LEDHelligkeit 180


//..............Servo..............
Servo servoLaden;
Servo servoStart;
#define pinServoLaden 19
#define pinServoStart 18
//..............Schrittmotor..............
#define pinStepperDir 13
#define pinStepperStep 12
//..............Taster..............
#define pinTasterStop 27
#define pinTasterStart 26
//..............NeoPixel LED..............
#define pinNeoPixel 4
//..............PowerMatic..............
#define pinEndschalter 34
#define pinPMStart 7
//..............Trommel Schalter..............
#define pinTrommelEndschalter 32
//..............IR sensor..............
#define pinIRd 35



clsLED LED(pinNeoPixel, LEDHelligkeit);
clsTrommel Trommel(pinStepperDir, pinStepperStep, pinTrommelEndschalter, StepperStepsPR, 
                   StepperSpeed, StepperDir, TrommelPosKorrektur);

bool ServoLadenAnfangPosOK = false;  //hat nur true wenn der servo an der anfangs position ist
bool ServoStartAnfangPosOK = false;  //hat nur true wenn der servo an der anfangs position ist
int last_zustand = 99;


void setup() 
{
  Serial.begin(9600);
  pinMode(pinTasterStart, INPUT_PULLUP);
  pinMode(pinTasterStop, INPUT_PULLUP);

  pinMode(pinEndschalter, INPUT);
  if(IREnable) pinMode(pinIRd, INPUT);


  if(ServoStartEnable)
  {
    servoStart.attach(pinServoStart);
    while(!runStartServo(ServoStartAnfangPos)){} //sicher stellen das der servo in der richtigen pos. ist
  }else{
    ServoStartAnfangPosOK = true;
    pinMode(pinPMStart, OUTPUT);
  }

  servoLaden.attach(pinServoLaden);
  while(!runLadenServo(ServoLadenAnfangPos)){} //sicher stellen das der servo in der richtigen pos. ist
  stepdegree = 3600.00/StepperStepsPR;
  Serial.println(stepdegree);
  Trommel.begin();
  LED.begin();

  delay(10);

}

void loop()
{
  static enum {AUS, EIN, AUSRICHTEN, STEPPER, LADEN, STOPFEN, FEHLER} zustand = AUS;
  const char* zustand_txt[] = {"AUS", "EIN", "Ausrichten", "Trommel bewegen", "Hülse laden", "Stopfen", "Fehler"};
  static bool OneCycle = false;
  static bool HuelseDrauf = false;
  uint8_t tmpTasterPressed = 0;

  tmpTasterPressed = checkTaster(true);
  //Serial.println(digitalRead(pinTrommelEndschalter));

  switch(zustand)
  {
    case AUS:
      switch(tmpTasterPressed)
      {
        case pinTasterStop:  
          Serial.println("Stoptaste");
          Trommel.trommelPosOK(true);
          zustand = AUSRICHTEN;  
          break;
        case pinTasterStart:
          Serial.println("Starttaste");
          Serial.println(ServoLadenAnfangPosOK);
          Serial.println(ServoStartAnfangPosOK);
          if(Trommel.trommelPosOK(false) && ServoLadenAnfangPosOK && ServoStartAnfangPosOK) 
            {zustand = EIN;}
          break;
        default: 
          if(Trommel.trommelPosOK(false) && ServoLadenAnfangPosOK && ServoStartAnfangPosOK)
            {LED.setFarbe(clsLED::ROT);}
          else
            {LED.blink(250, clsLED::ROT);}
      }
      break;
    case EIN:
      zustand = STEPPER;
      break;
    case AUSRICHTEN:     
      LED.setFarbe(clsLED::BLAU);
      if(ServoStartEnable)
      { 
        if(!runStartServo(ServoStartAnfangPos)) {
          //Serial.println("StartServo nicht in Anfangsposition");
          break;
        }
      }

      if(runLadenServo(ServoLadenAnfangPos))
      {
        if(!Trommel.trommelPosOK(false))
        {
          if(Trommel.move45Grad()) zustand = AUS;
        }else{
          zustand = AUS;
          trommelpos = 0.00;
        }
      }
      break;
    case STEPPER:
      if(Trommel.move45Grad())  //Schrittmotor 45Grad drehen
      {
        if(Trommel.trommelPosOK(false))   //prüfen ob pos. erreicht wurde
        {
          zustand = LADEN;
        }else{
          zustand = FEHLER;
          Serial.println("Trommel-Endpunkt nicht erreicht (Hall-Sensor)");
        }
      }
      break;
    case LADEN:
        if(!HuelseDrauf)
        {
          if(runLadenServo(ServoLadenEndPos))   //Zigihülse ganz reinstecken
          {
            HuelseDrauf = true;
            if(!IREnable) break;

            delay(20);
            if(digitalRead(pinIRd) == HIGH) 
            {
              HuelseDrauf = false;
              zustand = FEHLER;
              Serial.println("Fehler bei Laden: Huelse von IR nicht erkannt");
            }
          }
        }else{
          if(runLadenServo(ServoLadenAnfangPos))   //servo in ausgangs pos. zurückdrehen
          {
            HuelseDrauf = false;
            zustand = STOPFEN;
          }
        }
      break;
    case STOPFEN:
      switch(StopfMaschine(false, tmpTasterPressed))
      {
        case 0:
          zustand = FEHLER;
          Serial.println("Fehler bei Stopfen: Endschalterfehler");
          break;
        case 2:
          if(OneCycle)
            {zustand = AUS;}
          else
            {zustand = STEPPER;} // Nächste zigi machen
          break;
      }
      break;
    case FEHLER:
      LED.blink(250, clsLED::ROT);
      if(tmpTasterPressed == pinTasterStop)
      {
        StopfMaschine(true, tmpTasterPressed);
        HuelseDrauf = false;

        zustand = AUSRICHTEN;
      }
      break;
  }
  if (zustand != last_zustand){
    Serial.print("Neuer Zustand: ");
    Serial.println(zustand_txt[zustand]);
    last_zustand = zustand;
  }
  //Serial.println(zustand + "\t" + last_zustand);

  if(zustand != AUS && zustand != AUSRICHTEN && zustand != FEHLER)
  {
    switch(tmpTasterPressed)
    {
      case pinTasterStop:
        if(OneCycle && zustand != STOPFEN) 
        {
          zustand = FEHLER;
        }else{ 
          OneCycle = true;
          LED.setFarbe(clsLED::ORANGE);
        }
        break;
      case pinTasterStart:
        OneCycle = false;
        LED.setFarbe(clsLED::GRUEN);
    }
  }
}


//rückgabe wert: 0 = fehler,  1 = noch nicht fertig, 2 = fertig
uint8_t StopfMaschine(bool resetZustand, uint8_t &tmpTasterPressed) 
{
  static enum { AUS, EIN, START, STOPFEN, FEHLER } zustand = AUS;
  static bool ServoEndPosOk = false;

  if(resetZustand)
  {
    zustand = AUS;
    ServoEndPosOk = false;
    return 2;
  }

  if(!ServoStartEnable)
  {
    if(zustand == START || zustand == STOPFEN)
    {
      //falls die PM die HOPPER meldung hat oder die PM pausieren will
      if(tmpTasterPressed == pinTasterStart)  
      {
        digitalWrite(pinPMStart, HIGH);   //Signal an die PM senden
        delay(20);
        digitalWrite(pinPMStart, LOW);

        tmpTasterPressed = 0; //Löschen damit in der loop nix passiert
      }
    } 
  }

  switch (zustand) 
  {
  case AUS:
    if (checkEndschalter()) 
      {zustand = EIN;}
    else 
      {zustand = FEHLER;}
    break;
  case EIN:
    if(ServoStartEnable)
    {
      if(!ServoEndPosOk)
      {
        if(runStartServo(ServoStartEndPos)) ServoEndPosOk = true;
      }else{
        if(runStartServo(ServoStartAnfangPos)) 
        {
          ServoEndPosOk = false;
          zustand = START;
        }
      }
    }else{
      digitalWrite(pinPMStart, HIGH);   //Signal an die PM senden zum Starten
      delay(20);
      digitalWrite(pinPMStart, LOW);
      zustand = START;
    }
    break;
  case START:
    if (!checkEndschalter()) zustand = STOPFEN;
    break;
  case STOPFEN:
    if (checkEndschalter()) 
    {
      zustand = AUS;
      return 2;
    }
    break;
  case FEHLER:
    zustand = AUS;
    return 0;
  }
  return 1;
}




bool runLadenServo(uint8_t endWinkel)
{
  static unsigned long prevMillis = 0;
  static uint8_t istWinkel = 0;
 
  if(millis() - prevMillis >= ServoLadenSpeed)
  {
    prevMillis = millis();
    if(istWinkel == endWinkel) 
    {
      if(istWinkel == ServoLadenAnfangPos)
        {ServoLadenAnfangPosOK = true;}
      else
        {ServoLadenAnfangPosOK = false;}

      return true;
    }else{
      if(istWinkel < endWinkel)
        {istWinkel += 1;}
      else
        {istWinkel -= 1;}
      
      ServoLadenAnfangPosOK = false;
      /*Serial.print("Winkel: ");
      Serial.print(istWinkel);
      Serial.print("\t Endwinkel: ");
      Serial.print(endWinkel);
      Serial.print("\t x=");
      Serial.println(map(istWinkel, ServoLadenAnfangPos, ServoLadenEndPos, 0, 100));*/
      servoLaden.write(istWinkel);
    }
  }
  return false;
}

bool runStartServo(uint8_t endWinkel)
{
  static unsigned long prevMillis = 0;
  static uint8_t istWinkel = 0;
 
  if(millis() - prevMillis >= ServoStartSpeed)
  {
    prevMillis = millis();
    if(istWinkel == endWinkel) 
    {
      if(istWinkel == ServoStartAnfangPos)
        {ServoStartAnfangPosOK = true;}
      else
        {ServoStartAnfangPosOK = false;}

      return true;
    }else{
      if(istWinkel < endWinkel)
        {istWinkel += 1;}
      else
        {istWinkel -= 1;}
      
      ServoStartAnfangPosOK = false;
      servoStart.write(istWinkel);
    }
  }
  return false;
}


uint8_t checkTaster(bool delayEin)
{ 
  static unsigned long prevMillis = 0;
  static bool isTasterPressed = false;

  //Tasten überprüfung alle 50 millisek.  20x die sek.
  if(millis() - prevMillis >= 50 || !delayEin)
  {
    prevMillis = millis();

    if(digitalRead(pinTasterStart) == LOW)
    {
      if(isTasterPressed && delayEin) return 0;
      isTasterPressed = true; 
      return pinTasterStart;
    }else if(digitalRead(pinTasterStop) == LOW)
    {
      if(isTasterPressed && delayEin) return 0; 
      isTasterPressed = true; 
      return pinTasterStop;
    }else{
      isTasterPressed = false;
      prevMillis += 50;
    }
  }
  return 0;
}


bool checkEndschalter()
{
  static unsigned long prevMillis = 0;
  static bool lastState = false; 
  
  if(millis() - prevMillis >= 50)
  {
    prevMillis = millis();
    if(digitalRead(pinEndschalter) == HIGH)
      {lastState = true;}
    else
      {lastState = false;}
  }
  return lastState;
}
A4988
Hall-Sensor Off - On
PM-Endschalter Off - On
IR Off - On
Lade-Servo
Start-Servo
Trommel