// Funktionsaufruf im Setup [ read_targetrpm();] wieder einkommentieren. Wurde auskommentiert, da wokwi die Werte im EEPROM nicht dauerhaft speichert.
// if(dauersave > 100 && dauerlastsuccessfullsave >= 20000) wieder auf 20000 setzen
// DIP-Schalter: 1 = Gaspedal (wenn Schalter ein, ist Gaspedal voll durchgedrückt, 0 V am PIN) 2 = Handbremse (wenn Schalter ein ist Handbremse betätigt, 0 V am PIN)
// Prüfen, ob Näherungssensor der Handbremse und des Gaspedals low_activ ist. Schaltung so anpassen, dass bei einem Signalabriss zum Näherungssensor die Drehzahlregelung aus geht.
// Muss ich bei den Endschaltern eine Prällzeit berücksichtigen? Vermutlich nicht...
// LCD auf Vierzeile umstellen und Ausgaben programmieren
// Maximalewerte für die Drehzahlen der Drehzahlspeicher festlegen
// Beim Drehzahlbutton is die Bouncingsimulation ausgeschalten
// der Drehzahlbutton kann auch über die Taste "d" bedient werden
// Die Bouncingsimulation ist auch bei den Buttons Anschlag_Gaspedal und Endschalter_Handbremse ausgeschalten
// Prüfen, ob die Näherungssensoren low_aktiv ist. d.h. wenn er einen Zahn erfasst, zieht er das Potential auf Masse. Siehe PDF Anschluss Drehzahlsensor (Ordner Allgemein) (betroffen sind der Drehzahlgeber und die Endschalter für Gaspedal und Handbremse)
// Schwelle für Drehzahlzunahme in der Funktion accelerationcalc festlegen
// Schwelle für counter anpassen (wenn Drehzahlspeicher aktiv ist und Drehzahl 0 ist)
// Signalart des Drehzahlsensors prüfen und ggf. im Code anpassen (zieht auf Masse oder legt Spannung an)? Pullup nötig? Interrupt dann auf Rising oder Falling setzen!
// Sliderpoti in Zeile 159 und 160
// Das Regelverhalten kann über die Variablen power, pwmmax und standbyband beeinflusst werden.
// Die Feinfühligkeit des Drehzahlsensors kann hier beeinflusst werden:  if (dauer > 2000)
// PIN 2: Eingang Drehzahlsensor
// PIN 3: Taster Drehzahlerhöhung Speicher 1
// PIN 4: Taster Drehzahlreduzierung Speicher 1
// PIN 5: Ausgang PWM-Signal für ausfahren des Aktuators
// PIN 6: Ausgang PWM-Signal für einfahren des Aktuators
// PIN 7: Taster Drehzahlreduzierung Speicher 2
// PIN 8: Taster Drehzahlerhöhung Speicher 2
// PIN 9: Schalter für Handbremse
// PIN 10: Taster Aktivierung Drehzahlspeicher 2
// PIN 11: Taster Aktivierung Drehzahlspeicher 1
// PIN 12: Taster Aktivierung Leerlaufdrehzahl
// PIN 13: Schalter für Gaspedal
// PIN A0: Schiebepoti zur Drehzahlsimulation
// Eventuall Drehzahländerung pro Zeit mit in PWM Berechnung einbeziehen, wenn z.B. der Motor stark beansprucht wird, die Drehzahl deswegen nach unten geht, der Aktuator ganz ausfährt, um die Drehzahl wieder zu erhöhen, die Leistung des Motors aber nicht reicht, dann der Aktuator ganz ausgefahren ist und dann die Belastung abrupt abnimmt, sodass der Traktor mit Vollgas beschleunigen würde. Diesem Zustand mit der Erfassung der Geschwindigkeit der Drehzahlzunahme entgegenwirken

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <math.h>
#include <EEPROM.h>
LiquidCrystal_I2C lcd(0x27,20,4);

#define signalPIN 2                          // Interrupt-PIN 2 ist Eingang für Sensor
#define button1upPIN 3                       // PIN A3 für Taster Drehzahlerhöhung Drehzahlspeicher 1
#define button1downPIN 4                     // PIN A2 für Taster Drehzahlreduzierung Drehzahlspeicher 1
#define forwardpwmPIN 5                      // PIN 5 für PWM Signal ausfahren Aktuator
#define rewardpwmPIN 6                       // PIN 6 für PWM Signal einfahren Aktuator
#define button2downPIN 7                     // PIN 7 für Taster Drehzahlreduzierung Drehzahlspeicher 2
#define button2upPIN 8                       // PIN 8 für Taster Drehzahlerhöhung Drehzahlspeicher 2
#define buttonstorage1 11                    // PIN 11 Taster für Aktivierung der Solldrehzahl aus Speicher 1
#define buttonstorage2 10                    // PIN 10 Taster für Aktivierung der Solldrehzahl aus Speicher 2
#define buttonmakeidle 12                    // PIN 12 Taster für Leerlaufdrehzahl
#define slidepotiPIN A0                      // PIN A0 Slidepoti für Drehzahlsimulation
#define savebuttonPIN A1                     // PIN A1 zum Speichern der Drehzahlen in den EEPROM
#define cam 6                                // Anzahl Nocken
#define deadband 5                           // Totband des Aktuators (unter diesem PWM-Wert bewegt sich der Aktuator nicht)                   
#define standbyband 5                        // Drehzahldifferenzbereich in dem der Aktuator nicht ansprechen soll
#define pwmmaxmoveout 200                    // Maximum für PWM-Wert ausfahren
#define pwmmaxmovein  255                    // Maximum für PWM-Wert einfahren
#define targetrpmmax 1500                    // Höchstmögliche Wunschdrehzahl
#define buttoninterval 5                     // Drehzahl wird mit jedem Tastendruck um diesen Wert erhöht/verringert
#define bouncetime 50                        // Prellzeit für Taster
#define permanentbuttontime 200              // Zeitintervall beim dauerhaften Drücken des Tasters
#define idle 0                               // Case 0 bei Motorleerlauf, keine Ansteuerung des Aktuators
#define storage1 1                           // Case 1, Solldrehzahl ist Drehzahl aus Speicher 1
#define storage2 2                           // Case 2, Solldrehzahl ist Drehzahl aus Speicher 2
#define makeidle 3                           // Case 3, Aktuator mit Höchstgeschwindigkeit einfahren und Leerlaufdrehzahl herstellen
#define accelerationtohigh 4                 // Case 4, Aktuator so lange einfahren, bis die Beschleunigung bei 0 ist.
#define accelerationinterval 250             // Zunahme der Drehzahl soll spätestens nach verstreichen dieses Wertes in ms bestimmt werden
#define accerlerationincrease 400            // Wenn die Drehzahlzunahme pro Minute in einer Sekunde diesen Wert übersteigt
#define throttlePIN 13                       // PIN Endschalter Gaspedal
#define handbrakePIN 9                       // PIN für Handbremsschalter

volatile unsigned long dauer=0;               // microsekunden seit dem letzten Interrupt
volatile unsigned long last=0;                // Zählerwert beim letzten Interrupt
unsigned long lastLCD = 0;                    // Zählerwert bei letzter LCD Ausgabe
unsigned long dauerLCD = 0;                   // Millisekunden seit letzter LCD Ausgabe
unsigned long ms = 0;                         // Zeit seit Systemstart in Millisekunden
unsigned long lastbounce = 0;                 // Zählwert bei letzten Tastendruck in Millisekunden (Prellzeit Taster Speicher 1 + 2 Up und Down)
unsigned long dauerbounce = 0;                // Millisekunden seit dem letzten Tastendruck
unsigned long msbounce = 0;                   // Zeit seit Systemstart in Millisekunden
unsigned long lastbutton = 0;                 // Zählwert bei letztem Tastendruck in Millisekunden (Taster Speicher 1 + 2 Up und Down beim gedrückt halten)
unsigned long dauerbutton = 0;                // Millisekunden seit dem letzten Tastendruck
unsigned long msbutton = 0;                   // Zeit seit Systemstart in Millisekunden für Taster
unsigned long lastidlebutton = 0;             // Zählwert bei letztem Tastendruck in Millisekunden (Leerlauftaster)
unsigned long daueridlebutton = 0;            // Millisekunden seit dem letzten Tastendruck
unsigned long msidlebutton = 0;               // Zeit seit Systemstart in Millisekunden für Taster
unsigned long lastacceleration = 0;           // Zählwert bei letzter Berechnung der Drehzahlzunahme
unsigned long daueracceleration = 0;          // Millisekunden seit letzter Berechnung 
unsigned long msacceleration = 0;             // Zeit seit Systemstart in Millisekunden
unsigned long laststorage = 0;                // Zählwert bei letztem Phasendurchlauf
unsigned long dauerstorage = 0;               // Millisekunden seit letzter Berechnung
unsigned long msstorage = 0;                  // Zeit seit Systemstart in Millisekunden
unsigned int counter = 0;                     // Zähler für Sicherheitsfunktion, wenn Drehzahl 0 ist
unsigned long lastsave = 0;                   // Zählwert bei letztdem If-Durchlauf der Drezahlspeicherfunktion (EEPROM)
unsigned long dauersave = 0;                  // Millisekunden seit letztdem If-Durchlauf
unsigned long mssave = 0;                     // Zeit seit Systemstart in Millisekunden
unsigned int savecounter = 0;                 // Zähler für EEPROM Drehzahlspeicherfunktion
boolean savecountermodulo = 0;                // Wird zum Blinken der Bildschirmanzeigen beim Speichern benötigt
volatile unsigned long rpmArray[10];          // Array zur Glättung der Drehzahlen
byte position = 0;                            // Position im Array
byte ArraySizeactual = 0;                     // Anzahl der belegten Stellen im Array
volatile unsigned long Average = 0;           // gleitender Mittelwert (der Dauer)
int targetrpmactual = 0;                      // Aktuelle Solldrehzahl
int targetrpm1 = 550;                         // Solldrehzahl aus Speicher 1
int targetrpm2 = 650;                         // Solldrehzahl aus Speicher 2
byte byte_1 = 0;                              // wird für den EEPROM Speicher benötigt
byte byte_2 = 0;                              // wird für den EEPROM Speicher benötigt
volatile int rpmerror = 0;                    // Drehzahldifferenz Soll/Ist
volatile int pwmvalue = 0;                    // PWM-Wert zum Ansteuerung des Aktuators
const float power = 0.005;                    // Aggressivität des Tangens Hyperbolicus in Promille
volatile unsigned long drehzahl = 0;          // Variable für die Drehzahl
volatile unsigned long letztedrehzahl = 0;    // für die Bestimmung der Drehzahlzunahme pro Zeit
volatile float acceleration = 0;              // Drehzahlzunahme in U/min*s (Zunahme der Umdrehungen pro Minute in einer Sekunde)
byte phase = makeidle;                        // Variable für Fallunterscheidung, bei Neustart des Systems Aktuator einziehen, falls dieser ausgefahren ist.
byte lastphase = makeidle;                    // für die Sicherheitsfunktion, wenn die Drehzahl zu stark zunimmt pro Sekunde                                            
String phaseLCD = "makeidle";                            // Für LCD-Ausgabe der aktuellen Phase
const int rto = 5;                            // Rundungswert

void setup()
{
  Serial.begin(9600);
  pinMode(signalPIN, INPUT_PULLUP);                    // PIN 2 auf Eingang setzen und Pullup-Widerstand einschalten
  pinMode(forwardpwmPIN, OUTPUT);                      // PIN 5 auf Ausgang setzen
  pinMode(rewardpwmPIN, OUTPUT);                       // PIN 6 auf Ausgang setzen
  pinMode(button1upPIN, INPUT_PULLUP);                 // PIN 3 auf Eingang setzen und Pullup-Widerstand einschalten
  pinMode(button1downPIN, INPUT_PULLUP);               // PIN 4 auf Eingang setzen und Pullup-Widerstand einschalten
  pinMode(button2downPIN, INPUT_PULLUP);               // PIN 5 auf Eingang setzen und Pullup-Widerstand einschalten
  pinMode(button2upPIN, INPUT_PULLUP);                 // PIN 6 auf Eingang setzen und Pullup-Widerstand einschalten
  pinMode(handbrakePIN, INPUT_PULLUP);                 // PIN 9 auf Eingang setzen und Pullup-Widerstand einschalten
  pinMode(buttonstorage1, INPUT_PULLUP);               // PIN 11 auf Eingang setzen und Pullup-Widerstand einschalten 
  pinMode(buttonstorage2, INPUT_PULLUP);               // PIN 10 auf Eingang setzen und Pullup-Widerstand einschalten 
  pinMode(buttonmakeidle, INPUT_PULLUP);               // PIN 12 auf Eingang setzen und Pullup-Widerstand einschalten    
  pinMode(throttlePIN, INPUT_PULLUP);                  // PIN 13 auf Eingang setzen und Pullup-Widerstand einschalten
  pinMode(slidepotiPIN, INPUT);                        // PIN A0 auf INPUT setzen (müsste man nicht, ist standardmäß eh auf INPUT)
  pinMode(savebuttonPIN, INPUT_PULLUP);                // PIN A1 auf Eingang setzen und Pullup-Widerstand einschalten
  lcd.init();
  lcd.backlight();
  //read_targetrpm();                                    // liest die Werte für die beiden Drehzahlspeicher aus dem EEPROM aus 
  attachInterrupt(digitalPinToInterrupt(signalPIN), readmicros, RISING );    // Interrupt 0 auf Routine readmillis setzen
}


void loop()
{ 
  // Serial.print("Aktuelle Solldrehzahl: ");
  // Serial.println(targetrpmactual);
  // Serial.println("");
 /* Serial.println("Phase: ");
  Serial.print(phase);
  Serial.println("");  
  Serial.println("targetrpmactual");
  Serial.println(targetrpmactual);
  Serial.println(""); 
  Serial.println("rpmerror");
  Serial.println(rpmerror);
  Serial.println(""); */

  // Drehzahlen der Drehzahlspeicher begrenzen
  targetrpm1 = constrain(targetrpm1, 0, targetrpmmax);  
  targetrpm2 = constrain(targetrpm2, 0, targetrpmmax);
  
  // Auslesen der Taster 
  
  if (digitalRead(buttonstorage1) == 0 && digitalRead(handbrakePIN) == 0 && drehzahl > 1)       // Wenn Schalter für Aktivierung Speicher 1 gedrückt ist (0 = gedrückt) und Handbremse angezogen (0 = angezogen) und die Drehzahl größer 1 ist
  phase = storage1;
  if (digitalRead(buttonstorage2) == 0 && digitalRead(handbrakePIN) == 0 && drehzahl > 1)       // Wenn Schalter für Aktivierung Speicher 2 gedrückt ist (0 = gedrückt) und Handbremse angezogen (0 = angezogen) und die Drehzahl größer 1 ist
  phase = storage2;
  if (digitalRead(button1upPIN) == 0)         // Wenn Schalter für Drehzahlerhöhung Speicher 1 gedrückt ist (0 = gedrückt)
  button1up();
  if (digitalRead(button1downPIN) == 0)       // Wenn Schalter für Drehzahlreduzierung Speicher 1 gedrückt ist (0 = gedrückt)
  button1down();
  if (digitalRead(button2downPIN) == 0)       // Wenn Schalter für Drehzahlreduzierung Speicher 2 gedrückt ist (0 = gedrückt)
  button2down();
  if (digitalRead(button2upPIN) == 0)         // Wenn Schalter für Drehzahlerhöhung Speicher 2 gedrückt ist (0 = gedrückt)
  button2up();
 
  if(digitalRead(savebuttonPIN) == 0)         // Ist der Taster zum Speichern die Drehzahlen in den EEPROM gedrückt? (low_active)
  {
    mssave = millis();
    dauersave = mssave - lastsave;
    if(dauersave > 100)                                            // Wenn der Speichertaster länger als 100 ms gedrückt wurde 
    {
      if(savecounter <= 19)                                        // Wenn Taster länger als 20 Zähler gehalten wird, soll dies nicht mehr im LCD dargestellt werden
      {
        lcd.setCursor(savecounter, 3);                             // LCD ist bei savecounter 19 voll ausgefüllt, da bei der Cursorposition im LCD bei 0 angefangen wird zu zählen
        lcd.print("*");
      }
      savecounter++;                                               // Zähler um den Wert 1 erhöhen
      Serial.println(savecounter);                                 // Zähler Serialmonitor ausgeben
      if (savecounter == 20)                                       // Wenn die Speichertaste eine Sekunde oder länger gedrückt wurde, wird die Funktion zum Speichern im EEPROM aufgerufen
        {                                                          // wird die Taste weiterhin gedrückt, erfolgt keine weitere Speicherung
          write_targetrpm();                                       // Aufruf der Funktion zum Speichern der Drehzahlen in den EEPROM
          lcd.setCursor(0, 3);
          lcd.print("                    ");
        }
        else if (savecounter > 20)
          {
            savecountermodulo = savecounter % 2;                    // Hier wird geprüft, ob der Wert der Variablen savecounter gerade oder ungerade ist
            if (savecountermodulo == 0)                             // wenn Wert von savecounter gerade
            {
              lcd.setCursor(0, 3);
              lcd.print("    gespeichert!    ");
            }
              else                                                  // wenn Wert von savecounter ungerade
              {
                lcd.setCursor(0,3);
                lcd.print("                    ");
              }


          }
    lastsave = mssave;                                             // um zu prüfen, ob der Taster gehalen wird, siehe weiter oben
    }
  }
    else
    {
      savecounter = 0;                                            // Wenn die Speichertaste nicht gedrück wird, wird der Zähler resettet
      lcd.setCursor(0, 3);
      lcd.print("                    ");
    }

  if (digitalRead(handbrakePIN) == 1 && lastphase == storage1 || digitalRead(handbrakePIN) == 1 && lastphase == storage2)         // Bei Wert 1 ist die Handbremse nicht angezogen.
  {                                                                                             
    phase = makeidle;                                                                           // Wenn die Handbremse geöffnet wird und sich vorher im Storage1 oder Storage2 Modus befunden hat, dann makeidle Phase aufrufen
    lastidlebutton = millis();                                                                  // damit makeidle die 5 Sekunden zählen kann
  }  
  if (digitalRead(buttonmakeidle) == 0)       // Wenn Schalter für Leerlaufdrehzahl gedrückt ist (0 = gedrückt)
  {
    delay(bouncetime);                        // Prellzeit für Taster
    if (digitalRead(buttonmakeidle) == 0)
      {
      phase = makeidle;
      lastidlebutton = millis();
      }
  }

  char buf[20];                               // Pufferstring für sprintf
  drehzahl=0;                                 // selbstredend
  if (dauer != 0) 
  {
    drehzahl = myround(60000000/cam / Average);   // Drehzahl ausrechnen und runden
  } 
  else 
  {
    drehzahl = 0;                             // keine Messung? -> Stillstand
  }
  // zu Testzwecken eingefügt
  drehzahl = analogRead(slidepotiPIN);        
  drehzahl = map(drehzahl, 0, 1023, 0, 2500); // Hier wird der Wert des Slidepotis in den Wertebereich 0 bis 2500 transferiert
  
  Serial.print("Phase: ");
  Serial.println(phase);

  switch(phase)
  {
    case idle:                        // Zustand Leerlaufdrehzahl, Stillstand des Aktuators
    lastphase = idle;                 // Für Sicherheitsschaltung der Handbremse
    targetrpmactual = 0;              // Eigentlich nicht 0, sondern angestrepte Leerlaufdrehzahl
    pwmvalue = 0;
    actuator();
    break;

    case storage1:                         // Drehzahlspeicher 1 aktiviert       
    targetrpmactual = targetrpm1;          // aktuelle Solldrehzahl = Solldrehzahl aus Speicher 1
    pwmcalc();
    actuator();
    lastphase = storage1;                  // für Sicherheitsfunktion wegen zu starker Beschleunigung
    accelerationcalc ();                   // Drehzahlzunahme pro Zeit berechnen
     if (drehzahl == 0)                    // Sicherheitsfunktion, wenn Drehzahlspeicher aktiv aber Drehzahl 0 ist
      {
        msstorage = millis();
        dauerstorage = msstorage - laststorage;
        if (dauerstorage > 100)       // Alle 100 ms soll der Counter erhöht werden. Wenn die Drehzahl für eine Sekunde 0 ist, soll die Funktion makeidle aufgerufen werden
        {
          lcd.setCursor(0, 0);                        // Drehzahlanzeige fängt das Blinken an
          lcd.print("                    ");
          counter++;
          Serial.println(counter);
          if (counter >= 10)
          {
            phase = makeidle;
            lastidlebutton = millis();    // Für Zeitmessung in der Phase makeidle
            counter = 0;                  // counter Reset
            // lcd.setCursor(0, 3);
            // lcd.print("                    ");
          }
          laststorage = msstorage; 
        }
      }
      else 
       {
        counter = 0;                   // counter Reset 
       /* if (digitalRead(savebuttonPIN) == 1)           // Speichertaster nicht aktiv
        {
          lcd.setCursor(0, 3);
          lcd.print("                    ");
        } */
       }
    break;

    case storage2:
    targetrpmactual = targetrpm2;
    pwmcalc();
    actuator();
    lastphase = storage2;             // für Sicherheitsfunktion wegen zu starker Beschleunigung
    accelerationcalc ();              // Drehzahlzunahme pro Zeit berechnen
    if (drehzahl == 0)               // Sicherheitsfunktion, wenn Drehzahlspeicher aktiv aber Drehzahl 0 ist
      {
        msstorage = millis();
        dauerstorage = msstorage - laststorage;
        if (dauerstorage > 100)       // Alle 100 ms soll der Counter erhöht werden. Wenn die Drehzahl für eine Sekunde 0 ist, soll die Funktion makeidle aufgerufen werden
        {
          lcd.setCursor(0, 0);                        // Drehzahlanzeige fängt das Blinken an
          lcd.print("                    ");
          counter++;
          Serial.println(counter);
         
          if (counter >= 10)
          {
            phase = makeidle;
            lastidlebutton = millis();    // Für Zeitmessung in der Phase makeidle
            counter = 0;                  // counter Reset
           // lcd.setCursor(0, 3);
           // lcd.print("                    ");
          }
          laststorage = msstorage; 
        }
      }
      else 
       {
        counter = 0;                   // counter Reset 
       /* if (digitalRead(savebuttonPIN) == 1)           // Speichertaster nicht aktiv
        {
          lcd.setCursor(0, 3);
          lcd.print("                    ");
        } */
       }
    break;

    case makeidle:
    lastphase = makeidle;                         // Für Sicherheitsschaltung der Handbremse
    targetrpmactual = 0;                          // Eigentlich nicht 0, sondern angestrepte Leerlaufdrehzahl
    msidlebutton = millis();
    daueridlebutton = msidlebutton - lastidlebutton;
      if (daueridlebutton < 5000)
      { 
        pwmvalue = -255;
        analogWrite(forwardpwmPIN, 0);             // PWM-Signal für ausfahren auf 0 setzen        
        analogWrite(rewardpwmPIN, pwmvalue);       // PWM-Signal für einfahren setzen  
      }
        else 
        {
          phase = idle;
        }
    break;

    case accelerationtohigh:                   // zu starke Drehzahlzunahme pro Sekunde
    pwmvalue = -255;
    analogWrite(forwardpwmPIN, 0);             // PWM-Signal für ausfahren auf 0 setzen        
    analogWrite(rewardpwmPIN, pwmvalue);       // PWM-Signal für einfahren setzen  
    accelerationcalc ();                       // Drehzahlzunahme bestimmen
    if (acceleration < 1)
      {
        phase = lastphase;                     // wenn die Beschleunigung fast 0 ist (kleiner 1), zur vorigen Phase zurückkehren
      }
    break;
  }

  // LCD Ausgabe
  ms = millis();
  if (ms < lastLCD)                           // Zählerüberlauf
  {
    dauerLCD = 4294967295 - lastLCD + ms; 
  }
    else
    {
      dauerLCD = ms - lastLCD;
    }
  if (dauerLCD > 300)                         // Erneute LCD Ausgabe, wenn die angegebenen Millisekunden verstrichen sind
  {
  sprintf(buf, "      %4lu rpm",drehzahl);  // als 4stellig formatierte Zahl in den Puffer schreiben
  lcd.setCursor(0, 0);                        // cursor an den Anfang der 1. Zeile (fängt mit 0 an)
  lcd.print(buf);                             // Puffer ausgeben
  dauer >>= 10;                               // Flag für Stillstand ( : 1024 )

  lastLCD = ms;
  }  

  if (digitalRead(handbrakePIN) == 1) 
  {
   lcd.setCursor(0, 1);
   lcd.print("Handbremse anziehen!");
  }
    else
    {
      sprintf(buf, "%4u rpm    %4u rpm", targetrpm1, targetrpm2);  // als 4stellig formatierte Zahl in den Puffer schreiben
      lcd.setCursor(0, 1);                        // cursor an den Anfang der 2. Zeile (fängt mit 0 an)
      lcd.print(buf);                             // Puffer ausgeben
    }
  if (phase == 0)                             // definieren der Variablen phaseLCD für LCD-Ausgabe der aktuellen Phase
    phaseLCD = "      Leerlauf      ";
    else if (phase == 1)
      phaseLCD = " Drehzahlspeicher 1 ";
      else if (phase == 2)
        phaseLCD = " Drehzahlspeicher 2 ";
        else if (phase == 3)
          phaseLCD = " Leerlauf ansteuern ";
          else if (phase == 4)
            phaseLCD = "  Entschleunigung   ";
   lcd.setCursor(0, 2);                        // cursor an den Anfang der 3. Zeile (fängt mit 0 an)
  lcd.print(phaseLCD);
}                                             



void readmicros() {                           // Interrupt-Routine
  detachInterrupt(digitalPinToInterrupt(2));  // Interrupt ausschalten damit er uns nicht beißt
 
  unsigned long us = micros();                // Microsekundenzähler auslesen
  if (last == 0) {                            // erster Messwert?  
    last = us;                                // merken und nicht weiter bearbeiten
  } else { 
    if ( us < last ) {                        // Zählerüberlauf
      dauer = 4294967295 - last + us;         // erzeugt einen Fehler von 1µS - vernachlässigbar
    } else {
      dauer = us - last;                      // Differenz zum letzten Durchlauf berechnen
    }
    if (dauer > 2000) {                       // ignorieren wenn <= 5ms (Kontaktpreller)
      if (position > 9)
      {
        position = 0;                         // wenn position größer 9, dann resetten
      }
      rpmArray[position] = dauer;             // dauer ins Array schreiben
      position++;                             // Position beim nächsten Eintrag auf nächste Stelle setzen
      
      // Anzahl belegter Stellen im Array bestimmten
      ArraySizeactual = 10;
      for(int i = 0; i < 10; i++)
      {
         if (rpmArray[i] == 0)
         ArraySizeactual--;
      }

      // gleitenden Mittelwert berechnen
      Average = 0;                            // Mittelwert resetten
      for(int i = 0; i < 10; i++)
      {
        Average += rpmArray[i];
      }
      Average /= ArraySizeactual;             // Average durch die Anzahl der im Array belegten Stellen teilen um so den gleitenden Mittelwert zu erhalten

      // letzten Wert merken
      last = us;                             
    }
  }
  attachInterrupt(digitalPinToInterrupt(signalPIN), readmicros, RISING );    // Interrupt wieder einschalten.
}


unsigned long myround(unsigned long value) // Drehzahl in Fünferschritten runden
{  
  value += (rto >> 1);                    // halben roundto Wert addieren
  value /= rto;                           // integer division  
  value *= rto;                           // integer multiplikation
  return (value);
}


void accelerationcalc ()
{
  msacceleration = millis();     
  daueracceleration = msacceleration - lastacceleration;        // Zeit seit letzter Messung der Drehzahlzunahme pro Zeit         
  if (daueracceleration > accelerationinterval)                 // Drehzahlzunahme bestimmen, wenn die letzte Berechnung länger als eine halbe Sekunde her ist
    {
      if (drehzahl > letztedrehzahl)                            // Es soll nur die Beschleunigung, nich die Verzögerung bestimmt werden
      {               
        acceleration = drehzahl - letztedrehzahl;                 // Drehzahlzunahme
        acceleration = acceleration * (1000 / daueracceleration); // Wie viele Umdrehungen pro Minute wurden in einer Sekunde zugenommen
        acceleration = round(acceleration);                       // runden
          if (acceleration > accerlerationincrease)               // Wenn die Drehzahl mehr als ... Umdrehungen pro Minute in einer Sekunde zugenommen hat
          {
            phase = accelerationtohigh;
          }
      }
      else 
        {
          acceleration = 0;
        }

      lastacceleration = msacceleration;                        // Zählwert bei aktueller Berechnung als letzten Wert abspeichern
      letztedrehzahl = drehzahl;                                // aktuelle Drehzahl als letzte Drehzahl speichern
 
      Serial.print("                                Beschleunigung:");
      Serial.println(acceleration);
      Serial.println();
    }
 
}

void pwmcalc ()                               // PWM-Signal für Aktuatoransteuerung kalkulieren
{
rpmerror = targetrpmactual - drehzahl;              // Drehzahldifferenz Soll zu Ist
pwmvalue = round(tanh(rpmerror * power) * 255 ); // Tangens Hyperbolicus berechnen
  
  if (abs(rpmerror) <= standbyband )          // Wenn die Drehzahldifferenz gering ist (Variable standbyband), soll der Aktuator ruhen
  {
    pwmvalue = 0;
  }
  //Serial.print("                                    pwmvalue: ");
  //Serial.println(pwmvalue);
  //Serial.println("");
}

void actuator ()
{
  if (pwmvalue == 0)                         // Stillstand des Aktuators, diese Abfrage wird benötigt, dass bei pwmvalue gleich 0 der deadband Wert nicht dazuaddiert wird.
  {
  analogWrite(rewardpwmPIN, 0);         
  analogWrite(forwardpwmPIN, 0);    
  Serial.print("          pwm: ");
  Serial.print(pwmvalue);
  Serial.println(" Stillstand");    
  }

    else if (pwmvalue > 0)
    {
    pwmvalue += deadband;                              // Totband addieren 
    pwmvalue = constrain(pwmvalue, 0, pwmmaxmoveout);  // Wertebereich zwischen 0 und pwmmaxmoveout begrenzen
    if (digitalRead(throttlePIN) == 0)                 // Endschalter für Gaspedal ist low_aktiv (Massepotential, wenn Schalter aktiv, d.h. wenn das Gaspedal voll durchgedrückt ist)
      {
        pwmvalue = 0;                                  // Wenn das Gaspedal ganz durchgedrückt ist, soll der Aktuator nicht weiter ausfahren
      }
    analogWrite(rewardpwmPIN, 0);                      // PWM-Signal für einfahren auf 0 setzen
    analogWrite(forwardpwmPIN, pwmvalue);              // PWM-Signal für ausfahren setzen
    Serial.print("          pwm: ");
    Serial.print(pwmvalue);
    Serial.println(" ausfahren");
    }

      else                                       // Wenn pwmvalue kleiner 0
      {
      pwmvalue = abs(pwmvalue);
      pwmvalue += deadband;                            // Totband addieren 
      pwmvalue = constrain(pwmvalue, 0, pwmmaxmovein); // Wertebereich zwischen 0 und pwmmaxmovein begrenzen
      analogWrite(forwardpwmPIN, 0);                   // PWM-Signal für ausfahren auf 0 setzen        
      analogWrite(rewardpwmPIN, pwmvalue);             // PWM-Signal für einfahren setzen  
      Serial.print("          pwm: ");
      Serial.print(pwmvalue);
      Serial.println(" einfahren");
      }
}

void button1up()
{
  msbounce = millis();                                          // Nicht über delay, da der Programmablauf nicht beeinflusst werden soll
  dauerbounce = msbounce - lastbounce;

    if (dauerbounce > bouncetime && digitalRead(button1upPIN) == 0)     // Prellzeit 
    {
      lastbounce = millis();
      msbutton = millis();
      dauerbutton = msbutton - lastbutton; 

        if (dauerbutton > permanentbuttontime)                   // Verhalten, wenn Schalter gehalten wird
        {
          targetrpm1 += buttoninterval;                          // Solldrehzahl ändern
          if (targetrpm1 > targetrpmmax)                         // Maximalen Wert des Drehzahlspeicher 1 begrenzen
            targetrpm1 = targetrpmmax;
          lastbutton = millis();
        }
       Serial.println(targetrpm1);
    }
        if (digitalRead(button1upPIN) == 1)                     // Intervall zwischen möglichen Tasterbetätigungen verkürzen
         {
          lastbutton = 0;                                       // reset
         }
}


void button1down()
{
  msbounce = millis();                                          // Nicht über delay, da der Programmablauf nicht beeinflusst werden soll
  dauerbounce = msbounce - lastbounce;

    if (dauerbounce > bouncetime && digitalRead(button1downPIN) == 0)     // Prellzeit 
    {
      lastbounce = millis();
      msbutton = millis();
      dauerbutton = msbutton - lastbutton; 

        if (dauerbutton > permanentbuttontime)                   // Verhalten, wenn Schalter gehalten wird
        {
          targetrpm1 -= buttoninterval;                          // Solldrehzahl ändern
          if (targetrpm1 < 0)                                    // Solldrehzahl kann nur positiv sein
          {
            targetrpm1 = 0; 
          }        
          lastbutton = millis();
        }
      Serial.println(targetrpm1);
    }
        if (digitalRead(button1downPIN) == 1)                     // Intervall zwischen möglichen Tasterbetätigungen verkürzen
         {
          lastbutton = 0;                                         // reset
         }
}


void button2down()
{
  msbounce = millis();                                          // Nicht über delay, da der Programmablauf nicht beeinflusst werden soll
  dauerbounce = msbounce - lastbounce;

    if (dauerbounce > bouncetime && digitalRead(button2downPIN) == 0)     // Prellzeit 
    {
      lastbounce = millis();
      msbutton = millis();
      dauerbutton = msbutton - lastbutton; 

        if (dauerbutton > permanentbuttontime)                   // Verhalten, wenn Schalter gehalten wird
        {
          targetrpm2 -= buttoninterval;                          // Solldrehzahl ändern
          if (targetrpm2 < 0)                                    // Solldrehzahl kann nur positiv sein
          {
            targetrpm2 = 0; 
          } 
          lastbutton = millis();
        }
       Serial.print("          "); 
      Serial.println(targetrpm2);
    }
        if (digitalRead(button2downPIN) == 1)                     // Intervall zwischen möglichen Tasterbetätigungen verkürzen
         {
          lastbutton = 0;                                         // reset
         }
}


void button2up()
{
  msbounce = millis();                                          // Nicht über delay, da der Programmablauf nicht beeinflusst werden soll
  dauerbounce = msbounce - lastbounce;

    if (dauerbounce > bouncetime && digitalRead(button2upPIN) == 0)     // Prellzeit 
    {
      lastbounce = millis();
      msbutton = millis();
      dauerbutton = msbutton - lastbutton; 

        if (dauerbutton > permanentbuttontime)                   // Verhalten, wenn Schalter gehalten wird
        {
          targetrpm2 += buttoninterval;                          // Solldrehzahl ändern
           if (targetrpm2 > targetrpmmax)                        // Maximalen Wert des Drehzahlspeicher 1 begrenzen
            targetrpm2 = targetrpmmax;
          lastbutton = millis();
        }
       Serial.print("          ");  
       Serial.println(targetrpm2);
    }
        if (digitalRead(button2upPIN) == 1)                     // Intervall zwischen möglichen Tasterbetätigungen verkürzen
         {
          lastbutton = 0;                                       // reset
         }
}


void read_targetrpm()                                          // Funktion zum Auslesen des EEPROMS (für die beiden Drehzahlspeicher)
{
  byte_2 = EEPROM.read(0);
  byte_1 = EEPROM.read(1);
  targetrpm1 = ((byte_2 << 0) & 0xFFFFFF) + ((byte_1 << 8) & 0xFFFFFFFF);
  if (targetrpm1 < 0)
    targetrpm1 = 0;
  Serial.print("targetrpm1: ");
  Serial.print(targetrpm1);
  Serial.print("          ");
  

  byte_2 = EEPROM.read(2);
  byte_1 = EEPROM.read(3);
  targetrpm2 = ((byte_2 << 0) & 0xFFFFFF) + ((byte_1 << 8) & 0xFFFFFFFF);
  if (targetrpm2 < 0)
    targetrpm2 = 0;
  Serial.print("targetrpm2: ");
  Serial.print(targetrpm2);
  Serial.println("          ");
}


void write_targetrpm()                                          // Funktion zum Schreiben der beiden Werte von den Drehzahlspeichern in den EEPROM
{
  byte_2 = (targetrpm1 & 0xFF);
  byte_1 = ((targetrpm1 >> 8) & 0xFF);
  EEPROM.update(0, byte_2);
  EEPROM.update(1, byte_1);

  byte_2 = (targetrpm2 & 0xFF);
  byte_1 = ((targetrpm2 >> 8) & 0xFF);
  EEPROM.update(2, byte_2);
  EEPROM.update(3, byte_1);
}