Serielle Datenstring vom Computer im Adruino Mega einlesen und aufteilen

Ja.
Und das sehr gut :wink:
Darum sollte das ja noch Kleinteiliger werden.
Wenn das richtig sortiert ist, oder auf tabs je nach Funktion verteilt ist, sieht das nicht mehr so chaotisch aus.
Ist eben nicht nur nen blinkblink auf Knopfdruck...

Hallo Tommy,
es wird/ist ein wundervolles Projekt geworden, Dank aller hier im Forum. Dickes Lob an alle die dieses Ermöglicht haben. Ein besonders Dankeschön an XY Projekt, er ist die treibende Kraft, er hat auch mich immer wieder aus meinem "Aufgeben wollen" rausgeholt.

Es ist zwar ungewollt durch das wachsende Wissen was möglich ist, ständig größer geworden aber alle Stunden bzw. Tage.....ähh Monate !?....Jahr(e) :wink: haben sich wirklich für das Ergebnis gelohnt.

Hier bewahrheitet sich Dein Spruch "Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken."

Es wird aber noch ein Weilchen zu lesen,denken und zu programmieren geben....aber ein Ende ist in unmittelbarer Reichweite. :smiley:

Gruß Benziner

Das freut mich für Euch und weiterhin viel Erfolg und Freude an der Sache.

Gruß Tommy

Danke...
Ich:

  • werde das weiterhin komplett öffentlich begleiten um zu zeigen, das
    -- ein Dreizeiler eskalieren kann
    -- die Ideen mitwachsen
    -- eigene Fehler gerne auch einzugestehen sind

Nur um das auch für die Nachwelt und die Mitleser klarzustellen. Und Ansporn zu sein....:wink:

Das sind 3 gute Punkte für erfolgreiche Arbeit.
1 und 2 erleben wir alle regelmäßig.
Mit 3 hat so mancher ein Problem.

Gruß Tommy

Es war mir wichtig darauf hinzuweisen, das dieser gesamte Thread rein öffentlich gelaufen ist.
Und er wird es auch weiterhin.
Jeder Fehler, jede verkackte Funktion und jede Sackgasse.

DAS habe ich mir eigentlich für jeden Thread vorgenommen. Mit dem TO endlich einen Partner gefunden, der das auch mitmacht.
Auch wenn man sich nicht einig ist oder vollkommen komplett andere Ansichten hat - das nächste Post ist wieder sachbezogen.

Mal sehen, wie das hier endet. Aber ich bin mir sicher, das es ein Kandidat für die geilen Projekte sein könnte.(*)

(*)obwohl die Tankstelle um die Ecke wohl nicht zu toppen sein wird....

Es gibt immer wieder neue Projekte dafür (zum Glück)

Gruß Tommy

Schmeichelt mich sehr, ist aber im Grunde auch nur eine einfache API-Abfrage. Bin eher neidisch auf die ganzen anderen tollen Projekte hier. Alleine euer Projekt ist doch schon sehr viel komplexer. :blush:

Zunächst wünsche ich Euch allen ein fröhlichen 4'ten Advent und hoffe das Ihr alle gesund geblieben seit.
Nun denn, ich hab's ja versprochen....Es geht nach kurzen Winterschlaf und Knie-OP endlich weiter.(Bauch und Knie passen jetzt gut zusammen...beides Dick :joy: ) Hab in der Zwischenzeit zwar auch so einiges versucht und ausprobiert aber letztendlich hapert es immer noch an Intelligenz bzw. Logik ....Ich glaub ich stehe mir so manches mal selber im Weg.

Daher mein lieber @my_xy_projekt möchte ich Dich um Unterstützung bitten, weil Du wahrscheinlich der einzige bist der alles nachvollziehen kann.

Ich bin gerade wieder mit dem Sender beschäftigt, warum? Na zum einen weil ich den Turm entlasten wollte und zum anderen die Tages-Gesamtrunden nun vom Sender an "Alle" versenden möchte damit die anderen Display's nicht auch noch rechen müssen.

Funktioniert soweit auch schon....Aber nun würde ich gern den Sender zuerst komplett Fertig machen wollen mit all meinen Vorstellungen und Möglichkeiten.
Da wäre noch der Spannungsteiler..... und der SPS-Ausgabe. Ich bin nicht der Elektroniker, für ein paar Sachen reicht es aber für andere Dinge eben auch nicht.

Ich hoffe das Uwefed mir vielleicht da noch ein wenig unter die Arme greifen kann und möchte...
aber später erst. Jetzt geht es mal wieder um Menü / RotaryDecoder und Uhr stellen.

Ich hänge an der Logik..... hab da mal was vorbereitet. Es klemmt bei mir an der Auswahl von Stunden und Minuten und Übernahmehäkchen deren Veränderung über Rotary. Würde gern über Rotary das Feld aussuchen, mit Button bestätigen, bearbeiten, und mit dem bestätigen des Feld Übernahmehäkchen Uhrzeit im RTC schreiben und Menüpunkt verlassen.

uint8_t Uhrstellen(uint8_t menuRotary)                             // TAB SubMenu Uhrzeit einstellen
{
  const uint8_t rotaryMin = 1;
  const uint8_t rotaryMax = 3;
  enum {st, mi, ende};                   // die einzelnen Schritte
  static uint8_t auswahl = st;           // Vorbelegung 1. Schritt
  //static time_t lasttime = now();        // Vergleichvariable
  static uint8_t _st = 0;                // lokaler Speicher fuer UhrVariablen
  static uint8_t _mi = 0;
  static uint8_t _se = 0;
  static uint8_t _ta = 0;
  static uint8_t _mo = 0;
  static uint8_t _ja = 0;
  if (menuRotary == 0)                   // uebernahme der aktuellen Daten in lokale Variablen
  {
    //lasttime = now();
    _st = hour();
    _mi = minute();
    _se = 0; //second();
    _ta = day();
    _mo = month();
    _ja = shortYear();

    LCD_LineTwo_Sub_Uhrstellen(menuRotary, _st, _mi, _se);
  }
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    auswahl = tikRotary;
    LCD_LineTwo_Sub_Uhrstellen(tikRotary, _st, _mi, _se);             // untere Zeile im lcd
  }

  if (r.buttonPressedReleased(bounce) && auswahl == 3) // Wenn Cursor auf Übernahmehäkchen steht
  {
    Serial.println(F("Button pressed Uhr / Return -> Änderungen übernehmen und Werte in die Uhr schreiben"));
    lcdMenuLine(0, 0);                        // Zeige in der oberen Zeile wieder Hauptmenü an !
    lcdMenuLine(1, 1);                        // Zeige in untere Zeile Menüpunkt 1 an !
    lcd.noBlink();
    
// Ab hier wird in die Uhr geschrieben
    Serial.print("timeset: "); Serial.println(now());
    Serial.print(_st);Serial.print(_mi);Serial.println(_se);
    Serial.print(_ta);Serial.print(_mo);Serial.println(_ja);
/*  
    setTime(_st, _mi, _se, _ta, _mo, _ja); // uebernimmt alle Werte - egal ob veraendert oder nicht
    RTC.set(now());                        // schreibt in die RTC
*/   
    menuRotary = 0;
    return false;
  }
  return menuRotary;
}
void LCD_LineTwo_Sub_Uhrstellen(uint8_t menuRotary, uint8_t _st, uint8_t _mi, uint8_t _se )               // Uhrzeit stellen LCD 2'te Zeile
{
  lcd.setCursor(0, 1);                 // setzen des Cursor auf Zeile-2
  lcd.print(F("   "));                 // Leerzeichen zwischen Zeilenanfang und Stunde
  char st_[2];
  sprintf(st_, "%02d", _st);
  lcd.print(st_);                      // Ausgabe Stunden
  lcd.print(F(":"));                   // Ausgabe Doppelpunkt
  char mi_[2];
  sprintf(mi_, "%02d", _mi);
  lcd.print(mi_);                      // Ausgabe Minuten
  lcd.print(F(":"));                   // Ausgabe Doppelpunkt
  char se_[2];
  sprintf(se_, "%02d", _se);
  lcd.print(se_);                      // Ausgabe Sekunden
  lcd.print(F("   "));                 // Leerzeichen zwischen Sekunden und Übernahmehäkchen
  lcd.print(F("\332"));                // Ausgabe Übernahmehäkchen
  lcd.print(F(" "));                   // Rest der Zeile löschen

  // Cursor-Blink Position setzen
  lcd.blink();                       // Cursor soll blinken
  if (menuRotary == 1) lcd.setCursor(4, 1);
  if (menuRotary == 2) lcd.setCursor(7, 1);
  if (menuRotary == 3) lcd.setCursor(14, 1);
}

Der ganze Sketch im Anhang, glaube
Racecontrol_V2.5.ino (50.2 KB)
wäre sonst zu verwirrend :wink:

Warum nutzt Ihr eigentlich für die Menüsteuerung nicht die bewährte Lib von Jomelo (Forumsbeitrag dazu)?

Gruß Tommy

:wink:
Ach, schau mal wie das angefangen hat. Da war das nur ne Zeile.
Das Baby wächst.
Und wenn es älter geworden ist, schaun wa mal ob zur Jugendweihe/Konfirmation irgendwer spendabel ist.

Jaja... Du weisst auch, wie Du ein Stichwort richtig einsetzt :slight_smile:
Ich versuch mich mal einzulesen - aber nicht mehr heute in Gänze.
Stecke noch an einem eigenen LogikProblem.

Hallo Tommy,
mit dem Gedanken hatte ich am Anfang auch schon gespielt, jedoch für einen Anfänger....
ganz schwere Kost :wink:

Wie my_xy_projekt schon bemerkte....Am Anfang war's nur eine Hand voll..... :roll_eyes:

@my_xy_projekt
es müsste Dir alles sehr bekannt vorkommen..... :grin:

Ich weiß echt nicht wie Ihr es immer wieder schafft um die Runde Ecke zu denken :wink:
Für mein Teil verzettle ich mich immer wieder an solchen Dingen.

In der Ruhe liegt die Kraft, lass Dir Zeit....alles kann nichts muss.

Ja danke, wobei Winterzauber und Weihnachtsmarkt den Bauch jetzt besser ins Weihnachtsmannkostüm passen lassen würden. Nichts geht spurlos an einem vorbei, immer bleibt was hängen :blush:

Vermutlich hast Du was verschlafen, denn wir haben heute schon den vierten Advent, also schnell noch eine Kerze mehr anzünden :rofl:

Gute Besserung!

...aber meist an den falschen Stellen. :wink:

Upps, war da die Narkose doch etwas länger ... :joy:

So, nun brennt auch das 4'te Kerzlein....und Danke, wird schon.

So, ich habe diesmal auf den Kalender geschaut ....... :wink: heut ist Heiligabend.
Ich wünsche Euch allen besinnliche Weihnachtstage, viele schöne Stunden bei dem, was auch immer ihr vorhabt. Genießt das Leben und eure Gesundheit.

@my_xy_projekt
hab da nochmal was versucht......aber auch da bleibe hängen, mir fehlt einfach Zuviel Wissen oder können....wie heißt das Ding im Schädel was für das denken zuständig ist? Ahh, Hirn... :rofl:
Racecontrol_V2.51.ino (58.2 KB)

1 Like

Ich auch - ist schon wieder Sonntag...
Heute wohl eher nicht mehr. (ich schau aktuell mal "wo unser Wetter entsteht")
Aber ich sehe, das da irgendwie einiges fehlt/umgebaut ist...

Na mal sehen.
Ich wollte nur ein Lebenszeichen abgeben. - es wird. Hab meinen Encoder gefunden mit aufgelötetem Widerstand und nen Mega-Board :wink:

Hallo my_xy_projekt,
puh, hab schon gedacht das Du vor lauter Weihnachts-Schlemmerei nicht mehr an die Tastatur kommst.......spreche da aus eigener Erfahrung. :wink: :sweat_smile:

....wollte es auch versuchen etwas selber auf die Kette zu bekommen, leider misslungen.
Irgendwie fehlen mir immer die richtigen Ansätze....oder versuche für meinen Wissenstand, das unmögliche. Ach Xy Projekt, was würde ich ohne Dich nur machen.

Aber es gibt schon wieder eine neue Version....jetzt mit Ausschaltverzögerung für die SPS-Ausgänge. An Uhr / Datum Einstellung nochmal versucht....Aussehen finde gut, Funktionen.....
reden wir nicht drüber. :weary:

[code]
// Racecontrol auf MEGA 2560
const char ver[] = {"Racecontrol V2.53"}; // Stand 19.12.2021

#include <Wire.h>
#include <EEPROM.h>                                 // Zum Abspeichern der Einstellungen

uint8_t watt = EEPROM.read(1);                      // Sendeleistung für NRF24 Sender
uint8_t kanal = EEPROM.read(2);                     // Kanal für NRF24 Sender
uint8_t AnzahlSpuren = EEPROM.read(5);              // Anzahl der Fahrspuren
uint8_t exitMenu = false;                           // Aufruf (true) im Hauptmenü = verlassen, wenn kein subMenu

//DHT22 Sensor
#include "DHT.h"                                    // Lib für den Temperatur und Luftfeuchtikeits Sensor
DHT dht(8, DHT22);                                  // Sensor an Pin 8

//Uhr
#include <DS1307RTCB.h>                             // auf die TimeLibB angepasste 1307 von Paul Stoffregen
#include <TimeLibB.h>                               // auf deutsch umgeschriebene TimeLib von Paul Stoffregen
uint8_t minutealt = 61;                             // Zwangsaktuallisierung Uhrzeit! - nur alle Minute neue Abfrage

// Sender
#include <nRF24L01.h>                               // Lib für den Funkadapter NRF24
#include <RF24.h>
#include <printf.h>                                 // für die nrf-Lib debug-Ausgabe erforderlich
RF24 radio(4, 5);                                   // CE, CSN - MISO 50, MOSI 51, SCK 52
const uint8_t address[6] = "00001";                 // Sender-Adresse

// Vorbereitung LCD-Menu Zeichenketten
constexpr unsigned menueStrLen {12};                // Anzahl Zeichen für obere Zeile
constexpr unsigned hauptMenuePunkte {8};            // Anzahl der Sub-Menu's
using ConstStrPtr  = const char *;                  // na wenn ich das wüsste....
using FlashStrPtr  = const __FlashStringHelper *;   // na wenn ich das wüsste....
using MenuePunkt   = const char[menueStrLen + 1];   // na wenn ich das wüsste....

// LCD
#include <LiquidCrystal_I2C.h>                      // Lib für das LCD-Display
const uint8_t lcdRow = 16;
const uint8_t lcdCol = 2;
LiquidCrystal_I2C lcd(0x27, lcdRow, lcdCol);

// Tasterelemente
const uint8_t TsLinks = 45;                         // Taster links als INPUT_PULLUP PIN-45
const uint8_t TsRechts = 44;                        // Taster rechts als INPUT_PULLUP PIN-44
const uint8_t TsLinksLED = 47;                      // LED im Taster links an PIN-47
const uint8_t TsRechtsLED = 46;                     // LED im Taster rechts an PIN-46
const uint32_t bounceTime = 100;                    // hoeher gewaehlt auch wegen LCD-Geschwindigkeit
const uint32_t timeout = 30000;                     // Zeit die ohne Tastendruck vergehen muss um einen Abbruch zu erzwingen


// RotaryEncoder
#include <rotary.h>
Rotary r = Rotary(2, 3, 10);                        // Rotary(Encoder Pin 2, Encoder Pin 3, Button Pin 10)
const uint8_t bounce = 10;                          // Button Entprell-Zeit

// Renndaten
const uint8_t maxSpuren = 8;                        // Anzahl Max.Spuren
uint8_t spuren = AnzahlSpuren;                      // Anzahl Spuren der Bahn
const uint8_t charNum = 26;                         // max Anzahl Zeichen (derzeit Fahrername (25+1))
const uint8_t maxPSize = 32;                        // maximale Anzahl Zeichen, die mit einem Mal versandt werden können

//                                                  // AR - RZ - F werden mehrere Datensätze
uint16_t AR[maxSpuren + 1];                         // Runden in der Spur
uint16_t RZvk[maxSpuren + 1];                       // Rundenzeit Vorkomma in der Spur
uint16_t RZnk[maxSpuren + 1];                       // Rundenzeit Nachkomma in der Spur
char F[maxSpuren + 1][charNum];                     // Fahrername in der Spur
uint8_t temp;                                       // Temperatur als Ganzzahl
uint8_t humy;                                       // Luftfeuchtigkeit als Ganzzahl

// Ampeldaten
const uint8_t ampelPin[] = {23, 25, 27, 29, 31};    // Eingänge für Ampel
bool ampel[sizeof(ampelPin)] = {false};


// Spannungsdaten
const uint8_t voltagePin[] = {A8, A9, A10, A11, A12, A13 };   // Spannungsmessung über Spannungsteiler an A8 für Spur1....bis A15 für Spur 8
uint8_t voltage[sizeof(voltagePin) + 1] = {0};      // Alle voltage Eingänge auf 0 setzen

// SPS-Ausgang
const uint8_t spsPin[] = {22, 24, 26, 28, 30, 32, 34, 36};     // Ausgänge für SPS-Steuerung, diese soll Rundenimpulse von jeder Spur bekommen
uint32_t spsMillis[4];

// Widerstandswerte für Spannungsteiler
// R1 = 9980.0;  // Widerstand =10 KOhm
// R2 = 990.0;    // Widerstand =1,5  KOhm

// Gesamtzähler
uint16_t TagZahl[maxSpuren + 1] = {0};            // Tageszähler, zählt alle Runden pro Spur zusammen
uint16_t lastAR[maxSpuren + 1] = {0};             // Vergleicher, für Tageszähler
uint16_t lastTagesrunden[maxSpuren + 1] = {0};    // Vergleicher, für Tageszähler Anzeigetafel
uint16_t maxRunden = 0;                           // Merker für größte Rundenzahl
uint8_t Poleposition ;                            // Aktuelle führende Spur
bool ausgabe = false;                             // Wartet auf Vollständigkeit für SerMo

void setup()
{
  Serial.begin(115200);                           // Serialausgabe und z.Zeit Steuerbefehle
  Serial.println(F("Start..."));
  Serial1.begin(9600);                            // Steuerbefehle kommen später über 9-Pol TTL
  Serial3.begin(9600);                            // Renndaten kommen über USB-Adapter

  // Tasten
  pinMode(TsLinks, INPUT_PULLUP);
  pinMode(TsRechts, INPUT_PULLUP);

  // SPS Pin-Out
  for (uint8_t i = 0; i < sizeof(spsPin); i++)  // Eingänge für Ampel als Pullup setzen
  {
    pinMode(spsPin[i], OUTPUT);
  }

  // Uhr
  setSyncProvider(RTC.get);                       // Festlegen der Zeituebergabe an timeLib
  //bool x = false;                                 // Uhr stellen wenn true

  //DHT Sensor
  dht.begin();

  setSyncInterval(3000);                          // default festgelegt: 3000 ms
  if (timeStatus() != timeSet)
    Serial.print("Keine ");
  Serial.println("RTC gefunden");

  // Init AmpelPin
  for (uint8_t i = 0; i < sizeof(ampelPin); i++)  // Eingänge für Ampel als Pullup setzen
  {
    pinMode(ampelPin[i], INPUT_PULLUP);
  }

  // Display
  lcd.init();
  lcd.backlight();
  lcd.clear();

  // Sender
  printf_begin();                          // muss fuer debugging rein
  pinMode(53, OUTPUT);                     // verhindert den SPI-SlaveMode
  radio.begin();                           // Start der 2,4 GHz Wireless Kommunikation
  radio.enableDynamicPayloads();
  if (watt == 1) {
    radio.setPALevel(RF24_PA_MIN);
    Serial.println(F("Sendeleistung auf MIN"));
  }
  if (watt == 2) {
    radio.setPALevel(RF24_PA_LOW);
    Serial.println(F("Sendeleistung auf LOW"));
  }
  if (watt == 3) {
    radio.setPALevel(RF24_PA_HIGH);
    Serial.println(F("Sendeleistung auf HIGH"));
  }
  if (watt == 4) {
    radio.setPALevel(RF24_PA_MAX);
    Serial.println(F("Sendeleistung auf MAX"));
  }
  radio.setChannel(kanal);                 // Sendekanal
  radio.setDataRate(RF24_1MBPS);           // Setzen der Übertragungsgeschwindigkeit
  radio.setAutoAck(0);                     // Nicht auf Bestätigungsantwort warten
  radio.openWritingPipe(address);          // Setzen der Sendeadresse
  radio.stopListening();                   // Das angeschlossene Modul als Sender konfiguriert
  startBild();                             // Zeige für 5 Sec. Startbild
  while (millis() < 2000)
  {
    sendeZeit(50);                         // Sendet die Zeit an alle Client
  }

  Serial.println(F("Let's go running..."));

}
void loop()
{
  if (!menue())        // fragt das menu ab
  {
    displayKalender(); // wenn kein menu dann Kalender
  }
  leseBahn();                              // liest fortlaufend vom Renncomputer ein 
  sendeBahn(5);                            // sende alle (x) ms ein Datenfeld aus den Bahndaten
  merkeRunden();  
  leseAmpel();                             // liest fortlaufend die Eingänge für AMPEL ein
  leseBefehl();                            // anstelle eines Menu
  leseVoltage();                           // liest an AnalogPins die Spannung der Spuren
  leseTemp();                              // liest an Pin 8 die Temperatur und Luftfeutigkeit
  sendeZeit(1000);                         // sende alle (xxxx) ms die Uhrzeit
  sendeTemp(1000);                         // sende alle (xxxx) ms die Temperatur
  SpsSignalzeit();                         // Signalverlängerung für SPS-Ausgänge
  //serMon();
}
bool menue()                                                                                              // gibt true zurück, wenn ein Menu aufgemacht - sonst false
{
  static uint8_t nextMenu = 0;
  if (!nextMenu && r.buttonPressedReleased(bounce)) // Kein Menu? aber auf Button gedrückt
  {
    Serial.println("Button pressed Aufruf Hauptmenue");
    nextMenu = Hauptmenue(0);                       // übergebe Startzeichen für Menu
  }
  else  if (nextMenu != 0)                          // wenn doch Menu
  {
    nextMenu = Hauptmenue(nextMenu);                // ... dann einmal durchs Menu
  }
  return nextMenu;                                  // gib Zustand zurück (0=false - alles andere ist true)
}
uint8_t getRotary(uint8_t rotaryWert, const uint8_t rotaryMin, const uint8_t rotaryMax)                   // Abfrage des RotaryDecoder
{
  // *INDENT-OFF*
  //uint8_t lastRotary = rotaryWert;  // fuer debugausgabe
  uint8_t val = r.process();     // Check the encoder
  //DEBUG_PRINTLN(val);
  if (val == r.clockwise()) { rotaryWert ++; }
  else if (val == r.counterClockwise() && rotaryWert>0)
  { rotaryWert --; }
  if (rotaryWert > rotaryMax) { rotaryWert = rotaryMax; }
  else if (rotaryWert < rotaryMin) { rotaryWert = rotaryMin; }
  // if (lastRotary != rotaryWert) DEBUG_PRINTLN(rotaryWert); // debugausgabe
  return (rotaryWert);
  // *INDENT-ON*
}
MenuePunkt hauptMenu[] PROGMEM                                                                            // TAB Hauptmenu
{
  {"Hauptmen\365"},                                                       // \365=ü, Convert Binary to Octal aus HD44780U (LCD-II) Datenblatt
  {"Set Uhr"},
  {"Set Datum"},
  {"Set Spuren"},
  {"Set Kanal"},
  {"Set Sender"},
  {"WerksWerte"},
  {"Verlassen"},
};
uint8_t Hauptmenue(uint8_t menuRotary)                                                                    // Auswahl Einstellungen...
{
  const uint8_t rotaryMin = 1;                                            // erster Eintrag
  const uint8_t rotaryMax = sizeof(hauptMenu) / sizeof(hauptMenu[0]) - 1; // letzer Menueintrag -> Menu verlassen
  static uint8_t subMenu = 0;                                             // Merker auf switch/case
  static uint8_t nextMenu = 0;
  static bool lcdInitMerker = false;                                      // fuer erstmalige Anzeige des UnterMenu
  if (menuRotary == 0)                                                    // Initialisiert Variablen
  {
    lcdMenuLine(0, 0);
    subMenu = 0;
    nextMenu = 0;
    menuRotary++;
    DEBUG_PRINTLN(rotaryMax);
  }
  if (!subMenu)                                                           // bisher kein SubMenu ausgewählt
  {
    uint8_t tikRotary = getRotary(nextMenu, rotaryMin, rotaryMax);        // Abfrage RotaryEncoder
    if (nextMenu != tikRotary)                                            // neuer Wert? dann zeige an
    {
      nextMenu = tikRotary;                                               // merke den aktuellen RotaryWert
      DEBUG_PRINT("nextMenu=");
      DEBUG_PRINTLN(nextMenu);
      if (nextMenu <= rotaryMax)                                          // Untermenu geht nur bis vorletzten Eintrag
        lcdMenuLine(1, nextMenu);                                         // untere Zeile im lcd
    }
    if (r.buttonPressedReleased(bounce))                                  // EncoderButton ausgelöst?
    {
      if (nextMenu < rotaryMax)
        lcdMenuLine(0, nextMenu);                                         // schreibt erste Zeile mit nächstem Menupunkt
      subMenu = nextMenu;                                                 // übernimmt den Wert zur Weitergabe
      Serial.print("switch Menu: ");
      Serial.println(nextMenu);
    }
    if (exitMenu == true)
    {
      lcd.setCursor(0, 0);
      lcd.print(ver);
      minutealt = 61;
      subMenu = false;
      exitMenu = false;
      menuRotary = 0;
    }
  }
  if (subMenu)
    switch (nextMenu)
    {
      case rotaryMin:
        if (!lcdInitMerker)subMenu = Uhrstellen(false);
        subMenu = Uhrstellen(subMenu);                                   // Gehe zu Uhr stellen-Menue
        break;
      case 2:
        if (!lcdInitMerker)subMenu = Datumstellen(false);
        subMenu = Datumstellen(subMenu);                                 // Gehe zu  Datum stellen-Menue
        break;
      case 3:
        if (!lcdInitMerker)subMenu = Fahrspuren(false);
        subMenu = Fahrspuren(subMenu);                                   // Gehe zu  Funkkanal-Menue
        break;
      case 4:
        if (!lcdInitMerker)subMenu = Funkkanal(false);
        subMenu = Funkkanal(subMenu);                                   // Gehe zu  Funkkanal-Menue
        break;
      case 5:
        if (!lcdInitMerker)subMenu = Sendeleistung(false);
        subMenu = Sendeleistung(subMenu);                               // Gehe zu  Sendeleistung-Menue
        break;
      case 6:
        if (!lcdInitMerker)WerksWerte(false);
        subMenu = WerksWerte(subMenu);                                  // Gehe zu  WerksWerte-Menue
        break;
      case rotaryMax:                                                   // Hauptmenue verlassen abfrage speichern J/N
        if (!lcdInitMerker)Speichern(false);
        subMenu = Speichern(subMenu);
        break;
    }
  if (subMenu) lcdInitMerker = true;
  else lcdInitMerker = false;
  return (menuRotary);
}
void lcdMenuLine(const uint8_t line, const uint8_t auswahl)                                               // Inhalt der ersten Zeile des LCD-Menu
{
  lcd.setCursor(0, line);
  if (line == 0)
  {
    lcd.print(F("*")); //DEBUG_PRINT("*");
    lcdLineZentriert(auswahl); //DEBUG_PRINT(auswahl);
    lcd.print(F("*")); //DEBUG_PRINTLN("*");
  }
  else if (line == 1)
  {
    if (auswahl > 1) lcd.print(F("\177")); else lcd.print(F(" "));        // \177=Pfeil nach links, Convert Binary to Octal aus HD44780U (LCD-II) Datenblatt
    lcdLineZentriert(auswahl);
    if (auswahl < sizeof(hauptMenu) / sizeof(hauptMenu[0]) - 1) lcd.print(F("\176")); else lcd.print(F(" "));// \176=Pfeil nach rechts, Convert Binary to Octal aus HD44780U (LCD-II) Datenblatt
  }
}
void lcdLineZentriert(uint8_t ausgabe)
{
  const uint8_t gesamt = lcdRow - 2;
  uint8_t vor = 0;
  uint8_t nach = 0;
  vor = (gesamt - txtlaenge(ausgabe)) / 2;
  nach = gesamt - txtlaenge(ausgabe) - vor;
  for (; vor >= 1; vor--)
  {
    lcd.print(F(" ")); //DEBUG_PRINT(" ");
  }
  lcd.print(lcdHauptMenu(ausgabe));
  // DEBUG_PRINT(lcdHauptMenu(ausgabe));
  for (; nach >= 1; nach--)
  {
    lcd.print(F(" "));// DEBUG_PRINT(" ");
  }
  //DEBUG_PRINTLN();
}
FlashStrPtr lcdHauptMenu(byte index)
{
  return FlashStrPtr(&hauptMenu[index]);
}
uint8_t txtlaenge(byte index)
{
  return strlen_P(ConstStrPtr(&hauptMenu[index]));
}
uint8_t Fahrspuren(uint8_t menuRotary)                                                                    // TAB SubMenu Anzahl der Fahrspuren einstellen
{
  const uint8_t rotaryMin = 1;
  const uint8_t rotaryMax = 4;
  if (menuRotary == 0)
  {
    lcd.blink();
    menuRotary = AnzahlSpuren / 2;
    LCD_LineTwo_Sub_Spuren(menuRotary);
  }
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    LCD_LineTwo_Sub_Spuren(tikRotary);             // untere Zeile im lcd
  }
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed Fahrspuren -> Werte übernehmen und zurück ins Hauptmenu"));
    lcdMenuLine(0, 0);
    lcdMenuLine(1, 3); //
    AnzahlSpuren = tikRotary * 2;
    Serial.print("Eingestelle Spuren=");
    Serial.println(AnzahlSpuren);
    lcd.noBlink();
    menuRotary = 0;
    return false;
  }
  return menuRotary;
}
uint8_t Uhrstellen(uint8_t menuRotary)                                                                    // TAB SubMenu Uhrzeit einstellen
{
  static time_t lasttime = now();                                           // Vergleichvariable
  static uint8_t rotaryMin = 1;
  static uint8_t rotaryMax = 4;                                             // Ende und speichern
  static uint8_t unterMenu = 0;                                             // Merker auf switch/case
  static uint8_t nextMenu = 0;
  static uint8_t _st = 0;                                                  // lokaler Speicher fuer UhrVariablen
  static uint8_t _mi = 0;
  static uint8_t _se = 0;
  static uint8_t _ta = 0;
  static uint8_t _mo = 0;
  static uint8_t _ja = 0;
  static bool lcdInitMerker = false;                                      // fuer erstmalige Anzeige des UnterMenu

  if (menuRotary == 0)                                                    // Initialisiert Variablen

  {
    lcd.noBlink();                       // ... ausmachen ...
    if (lasttime != now())               // ... wenn neue Zeit ...
    {
      lasttime = now();                   // ... merken
    }
    unterMenu = 0;
    nextMenu = 0;
    menuRotary++;
    _st = hour();                     // uebernahme der aktuelle Stunden in lokale Variablen "_st"
    _mi = minute();
    _se = 0; //second();
    _ta = day();
    _mo = month();
    _ja = shortYear();
    DEBUG_PRINTLN(_st);
    DEBUG_PRINTLN(_mi);
    LCD_LineTwo_Sub_Uhrstellen(menuRotary, _st, _mi, _se);                   // untere Zeile im lcd
  }
  if (!unterMenu)                                                           // bisher kein SubMenu ausgewählt
  {
    lcd.blink();                        // Cursor-Position zum blinken setzen
    if (nextMenu == 1) lcd.setCursor(2, 1);
    if (nextMenu == 2) lcd.setCursor(5, 1);
    if (nextMenu == 3) lcd.setCursor(12, 1);
    if (nextMenu == 4) lcd.setCursor(14, 1);

    uint8_t tikRotary = getRotary(nextMenu, rotaryMin, rotaryMax);          // Abfrage RotaryEncoder
    if (nextMenu != tikRotary)                                              // neuer Wert? dann zeige an
    {
      nextMenu = tikRotary;                                                 // merke den aktuellen RotaryWert
      DEBUG_PRINT("nextMenu=");
      DEBUG_PRINTLN(nextMenu);
    }
    if (r.buttonPressedReleased(bounce))                                    // EncoderButton ausgelöst?
    {
      if (nextMenu < rotaryMax)
      lcdMenuLine(0, nextMenu);                                             // schreibt erste Zeile mit nächstem Menupunkt
      unterMenu = nextMenu;                                                 // übernimmt den Wert zur Weitergabe
      Serial.print("Untermenü ");
      Serial.print(nextMenu);
      Serial.println(" ausgewählt");
    }

    if (unterMenu == 3)                                                     // Hier wird die Uhrzeit in RTC geschrieben werden und Menüpunkt verlassen !
    {
     

      Serial.println(F("Werte übernehmen und zurück ins Hauptmenü"));
      Serial.print(_st); Serial.print(":"); Serial.print(_mi); Serial.print(":"); Serial.println(_se);
      Serial.print(_ta); Serial.print(":"); Serial.print(_mo); Serial.print(":"); Serial.println(_ja);
      /* Hier in die RTC schreiben
        _ta = day();
        _mo = month();
        _ja = Year();       
        setTime(_st, _mi, _se, _ta, _mo, _ja); // uebernimmt alle Werte - egal ob veraendert oder nicht
        RTC.set(now());                        // schreibt in die RTC
      */
      lcdMenuLine(0, 0);
      lcdMenuLine(1, 1);
      unterMenu = false;
      exitMenu = false;
      menuRotary = 0;
    }    
      
    if (unterMenu == 4)                                                     // Hier wird die Uhrzeit nicht geschrieben aber der Menüpunkt verlassen !
    {
      

      Serial.println(F("Werte nicht übernehmen und zurück ins Hauptmenü"));
      lcdMenuLine(0, 0);
      lcdMenuLine(1, 1);
      unterMenu = false;
      exitMenu = false;
      menuRotary = 0;

    }
  }
  if (unterMenu)
    switch (nextMenu)
    {
      case 1:
        if (!lcdInitMerker)unterMenu = Stundenstellen(false, _st, _mi, _se);
        unterMenu = Stundenstellen(unterMenu, _st, _mi, _se);                                // Gehe zu Uhr stellen-Menue
        break;
      case 2:
        if (!lcdInitMerker)unterMenu = Minutenstellen(false, _st, _mi, _se);
        unterMenu = Minutenstellen(unterMenu, _st, _mi, _se);                                // Gehe zu  Datum stellen-Menue
        break;
      case 3:
        //        if (!lcdInitMerker)unterMenu = Uhrsichern(false, _st, _mi, _se);           // Aus Menü zurück und Uhr nicht speichern
        //        unterMenu = Uhrsichern(unterMenu,_st, _mi, _se);
        break;
      case 4:
        //        if (!lcdInitMerker)unterMenu = Uhrsichern(false, _st, _mi, _se);           // Aus Menü zurück und Uhr speichern
        //        unterMenu = Uhrsichern(unterMenu,_st, _mi, _se);
        break;       
    }
  if (unterMenu) lcdInitMerker = true;
  else lcdInitMerker = false;
  return (menuRotary);
}
uint8_t Stundenstellen(uint8_t menuRotary, uint8_t _st, uint8_t _mi, uint8_t _se)                         // TAB SubMenu Uhrzeit Stunden einstellen
{
  static uint8_t rotaryMin = 1;
  static uint8_t rotaryMax = 23;
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    _st=tikRotary;
    LCD_LineTwo_Sub_Uhrstellen(menuRotary,_st, _mi, _se);             // untere Zeile im lcd
  }
  if (r.buttonPressedReleased(bounce)) {
    _st = tikRotary;
    Serial.print(F("Button pressed Stunde_Neu = "));
    Serial.println(_st);
    return false;

  }
  return menuRotary;
}
uint8_t Minutenstellen(uint8_t menuRotary, uint8_t _st, uint8_t _mi, uint8_t _se)                         // TAB SubMenu Uhrzeit Minuten einstellen
{
  static uint8_t rotaryMin = 1;
  static uint8_t rotaryMax = 59;
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    _mi = tikRotary;
    LCD_LineTwo_Sub_Uhrstellen(menuRotary,_st, _mi, _se);                                                     // untere Zeile im lcd
  }
  if (r.buttonPressedReleased(bounce)) {
    _mi = tikRotary;
    Serial.print(F("Button pressed Minute_Neu = "));
    Serial.println(_mi);
    return false;
  }
  return menuRotary;
}
uint8_t Datumstellen(uint8_t menuRotary)                                                                  // TAB SubMenu Datum einstellen
{
  const uint8_t rotaryMin = 1;
  const uint8_t rotaryMax = 5;
  enum {ta, mo, ja, ende};                    // die einzelnen Schritte
  static uint8_t auswahl = ta;                // Vorbelegung 1. Schritt
  //static time_t lasttime = now();             // Vergleichvariable
  static uint8_t _ta = 0;                     // lokaler Speicher fuer Uhr-Variable Tag
  static uint8_t _mo = 0;                     // lokaler Speicher fuer Uhr-Variable Monat
  static uint8_t _ja = 0;                     // lokaler Speicher fuer Uhr-Variable Jahr

  if (menuRotary == 0)                            // Übernahme der aktuellen Daten in lokale Variablen
  {
    //lasttime = now();
    _ta = day();
    _mo = month();
    _ja = shortYear();

    LCD_LineTwo_Sub_Datumstellen(menuRotary, _ta, _mo, _ja);
  }


  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    auswahl = tikRotary;
    LCD_LineTwo_Sub_Datumstellen(tikRotary, _ta, _mo, _ja);             // untere Zeile im lcd
  }

  if (r.buttonPressedReleased(bounce) && auswahl == 4)
  {
    Serial.println(F("Button pressed Datum / Return -> Änderungen übernehmen und Werte in die Uhr schreiben"));
    lcdMenuLine(0, 0);                        // Zeige in der oberen Zeile wieder Hauptmenü an !
    lcdMenuLine(1, 2);                        // Zeige in untere Zeile Menüpunkt 2 an !
    lcd.noBlink();
    menuRotary = 0;
    return false;
  }
  return menuRotary;
}
uint8_t Funkkanal(uint8_t menuRotary)                                                                     // TAB SubMenu Funkkanal einstellen
{
  const uint8_t rotaryMin = 1;
  const uint8_t rotaryMax = 127;
  if (menuRotary == 0)
  {
    lcd.blink();
    menuRotary = kanal;
    LCD_LineTwo_Sub_kanal(menuRotary);
  }
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    LCD_LineTwo_Sub_kanal(tikRotary);             // untere Zeile im lcd
  }
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed FunkKanal -> zurück ins Hauptmenu"));
    lcdMenuLine(0, 0);
    lcdMenuLine(1, 4);
    kanal = tikRotary;
    menuRotary = 0;
    lcd.noBlink();
    return false;
  }
  return menuRotary;
}
uint8_t Sendeleistung(uint8_t menuRotary)                                                                 // TAB SubMenu Sendeleistung einstellen
{
  const uint8_t rotaryMin = 1;
  const uint8_t rotaryMax = 4;
  if (menuRotary == 0)
  {
    lcd.blink();
    menuRotary = watt;
    LCD_LineTwo_Sub_Sendeleistung(menuRotary);
  }
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    LCD_LineTwo_Sub_Sendeleistung(tikRotary);             // untere Zeile im lcd
  }
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed Sendeleistung -> zurück ins Hauptmenu"));
    lcdMenuLine(0, 0);
    lcdMenuLine(1, 5);
    //radio.setChannel(kanal);
    watt = tikRotary;
    menuRotary = 0; //0
    lcd.noBlink();
    //exitMenu = true;
    return false;
  }
  return menuRotary;
}
uint8_t WerksWerte(uint8_t menuRotary)                                                                    // TAB SubMenu Einstellungen auf Werkswerte zurücksetzen
{
  const uint8_t rotaryMin = 1;
  const uint8_t rotaryMax = 2;
  if (menuRotary == 0)
  {
    lcd.blink();
  }
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    LCD_LineTwo_Sub_WerksWerte(tikRotary);             // untere Zeile im lcd
  }
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed WerksWerte -> zurück ins Hauptmenu"));
    lcdMenuLine(0, 0);
    lcdMenuLine(1, 6);
    if (tikRotary == 1)
    {
      Werkseinstellung();
      Serial.print("Es werden Daten wiederhergestellt");
    }
    if (tikRotary == 2)
    {
      Serial.println("Es wurden keine Daten wiederhergestellt");
    }
    exitMenu = true;
    return false;
  }
  return menuRotary;
}
uint8_t Speichern(uint8_t menuRotary)                                                                     // TAB SubMenu Geänderte Einstellungen ins EEprom abspeichern
{
  const uint8_t rotaryMin = 1;
  const uint8_t rotaryMax = 2;
  if (menuRotary == 0)
  {
    lcd.blink();
  }
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    LCD_LineTwo_Sub_Speichern(tikRotary);             // untere Zeile im lcd
  }
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed Speichern -> zurück ins Hauptmenu"));
    lcdMenuLine(0, 0);
    lcdMenuLine(1, 7);
    if (tikRotary == 1)
    {
      Datensicherung();
      Serial.print("Es wurden Daten gesichert");
    }
    if (tikRotary == 2)
    {
      Serial.print("Es wurden keine Daten gesichert");
    }
    exitMenu = true;
    return false;
  }
  return menuRotary;
}
void LCD_LineTwo_Sub_Uhrstellen(uint8_t menuRotary,uint8_t _st, uint8_t _mi, uint8_t _se )                // Uhrzeit stellen LCD 2'te Zeile
{
  lcd.setCursor(0, 1);                 // setzen des Cursor auf Zeile-2
  lcd.print(F(" "));                   // Leerzeichen zwischen Zeilenanfang und Stunde
  char st_[2];
  sprintf(st_, "%02d", _st);
  lcd.print(st_);                      // Ausgabe Stunden
  lcd.print(F(":"));                   // Ausgabe Doppelpunkt
  char mi_[2];
  sprintf(mi_, "%02d", _mi);
  lcd.print(mi_);                      // Ausgabe Minuten
  lcd.print(F(":"));                   // Ausgabe Doppelpunkt
  char se_[2];
  sprintf(se_, "%02d", _se);
  lcd.print(se_);                      // Ausgabe Sekunden
  lcd.print(F("   "));                 // Leerzeichen zwischen Sekunden und Abfrage
  lcd.print(F("J/N"));                 // Abfrage ob Änderung speichern Ja / Nein
  lcd.print(F(" "));                   // Rest der Zeile löschen
  lcd.blink();                        // corsor Blink-Position setzen
    if (menuRotary == 1) lcd.setCursor(2, 1);
    if (menuRotary == 2) lcd.setCursor(5, 1);
    if (menuRotary == 3) lcd.setCursor(12, 1);
    if (menuRotary == 4) lcd.setCursor(14, 1); 
}
void LCD_LineTwo_Sub_Datumstellen(uint8_t menuRotary, uint8_t _ta, uint8_t _mo, uint8_t _ja )             // Datum stellen LCD 2'te Zeile
{
  lcd.setCursor(0, 1);               // setzen des Cursor auf Anfang Zeile-2
  lcd.print(F(" "));                // Leerzeichen zwischen Zeilenanfang und Tag
  char ta_[2];
  sprintf(ta_, "%02d", _ta);
  lcd.print(ta_);                    // Ausgabe Tag
  lcd.print(F("."));                    // Ausgabe Punkt
  char mo_[2];
  sprintf(mo_, "%02d", _mo);
  lcd.print(mo_);                    // Ausgabe Monat
  lcd.print(F("."));                    // Ausgabe Ppunkt
  char ja_[2];
  sprintf(ja_, "%02d", _ja);
  lcd.print(F("20"));
  lcd.print(ja_);                   // Ausgabe Jahr
    lcd.print(F("  "));               // Leerzeichen zwischen Sekunden und Abfrage
  lcd.print(F("J/N"));                 // Abfrage ob Änderung speichern Ja / Nein
  lcd.print(F(" "));                   // Rest der Zeile löschen

  // corsor Blink-Position setzen
  lcd.blink();
  if (menuRotary == 1) lcd.setCursor(2, 1);
  if (menuRotary == 2) lcd.setCursor(5, 1);
  if (menuRotary == 3) lcd.setCursor(10, 1);
  if (menuRotary == 4) lcd.setCursor(13, 1);
  if (menuRotary == 5) lcd.setCursor(15, 1);
}
void LCD_LineTwo_Sub_Spuren(uint8_t menuRotary)                                                           // Spuren stellen LCD 2'te Zeile
{
  lcd.setCursor(0, 1);
  //lcd.print(F(" "));                  // 4 Stellen merken
  switch (menuRotary)
  {
    case 1:                           // Zeige Aktuellen Wert in untere Zeile an
      lcd.print(F(" 2-Spurige Bahn "));
      break;
    case 2:
      lcd.print(F(" 4-Spurige Bahn "));
      break;
    case 3:
      lcd.print(F(" 6-Spurige Bahn "));
      break;
    case 4:
      lcd.print(F(" 8-Spurige Bahn "));
      break;
  }
  lcd.setCursor(1, 1); //Cursor Blinkposition


  //  lcd.print(F(" Ist="));     // 5 Stellen merken
  //  lcd.print(AnzahlSpuren);   // Anzahl Stellen wird errechnet
  //  lcd.print(F("   Soll="));  // 8 Stellen merken
  //  lcd.print(menuRotary);     // Stellen wird auch errecnet
  //  uint8_t curPos = 13;       // "Ist = " + " Soll = "!!! gemerkt ;)
  //  if (AnzahlSpuren > 99) curPos++;   // Jede Stelle gibt einen weiteren +Punkt
  //  if (AnzahlSpuren > 9) curPos++;
  //  curPos++;
  //  if (menuRotary > 99) curPos++;
  //  if (menuRotary > 9) curPos++;
  //  curPos++;
  //  // Bis zum Ende der Zeile löschen:
  //  for (uint8_t i = curPos; i < lcdRow; i++) lcd.print(F(" ")); // Bis zum ende der Zeile Leerzeichen setzen!
  //  lcd.setCursor(curPos - 1, 1); // Ach schau es blinkt!
}
void LCD_LineTwo_Sub_kanal(uint8_t menuRotary)                                                            // Funkkanal stellen LCD 2'te Zeile
{
  lcd.setCursor(0, 1);
  lcd.print(F(" Sendekanal "));      // 4 Stellen merken
  lcd.print(menuRotary);             // Stellen wird auch errecnet
  uint8_t curPos = 10;               // "Ist = " + " Soll = "!!! gemerkt ;)
  if (kanal > 99) curPos++;         // Jede Stelle gibt einen weiteren +Punkt   id?
  if (kanal > 9) curPos++;
  curPos++;
  if (menuRotary > 99) curPos++;
  if (menuRotary > 9) curPos++;
  curPos++;
  // Bis zum Ende der Zeile löschen:
  for (uint8_t i = curPos; i < lcdRow + 1; i++) lcd.print(" "); // Bis zum ende der Zeile Leerzeichen setzen!
  lcd.setCursor(curPos - 2 , 1); // Ach schau es blinkt!
}
void LCD_LineTwo_Sub_Sendeleistung(uint8_t menuRotary)                                                    // Senderleistung stellen LCD 2'te Zeile
{
  lcd.setCursor(0, 1);
  lcd.print(F(" "));                  // 4 Stellen merken
  switch (watt)
  {
    case 1:                           // Zeige Aktuellen Wert in untere Zeile an
      lcd.print(F("MIN"));
      break;
    case 2:
      lcd.print(F("LOW"));
      break;
    case 3:
      lcd.print(F("HIGH"));
      break;
    case 4:
      lcd.print(F("MAX"));
      break;
  }


  lcd.print(F("   >   "));                  // Neuer Wert

  switch (menuRotary)
  {
    case 1:                           // Zeige neuen Wert in untere Zeile an
      lcd.print(F("MIN"));
      break;
    case 2:
      lcd.print(F("LOW"));
      break;
    case 3:
      lcd.print(F("HIGH"));
      break;
    case 4:
      lcd.print(F("MAX"));
      break;
  }

  //lcd.print(menuRotary);     // Stellen wird auch errecnet
  uint8_t curPos = 10;       // "Ist = " + " Soll = "!!! gemerkt ;)
  //if (watt > 99) curPos++;     // Jede Stelle gibt einen weiteren +Punkt   id?
  if (watt > 9) curPos++;
  curPos++;
  //if (menuRotary > 99) curPos++;
  if (menuRotary > 9) curPos++;
  curPos++;
  // Bis zum Ende der Zeile löschen:
  for (uint8_t i = curPos; i < lcdRow + 1; i++) lcd.print(F(" ")); // Bis zum ende der Zeile Leerzeichen setzen!
  lcd.setCursor(curPos - 1 , 1); // Ach schau es blinkt!
}
void LCD_LineTwo_Sub_Speichern(uint8_t menuRotary)                                                        // Speichern stellen LCD 2'te Zeile
{
  for (uint8_t i = 0; i < 16; i++) lcd.print(F(" ")); // Bis zum ende der Zeile Leerzeichen setzen!
  lcd.setCursor(0, 1);
  lcd.print(F("  Speichern J/N"));     // 15 Stellen merken
  uint8_t curPos = 15;                 // "Ist = " + " Soll = "!!! gemerkt ;)
  if (menuRotary == 1) curPos = curPos - 3; // Jede Stelle gibt einen weiteren +Punkt
  if (menuRotary == 2) curPos = curPos - 1;
  lcd.setCursor(curPos , 1); // Nu schau es blinkt an verschiedenen Stellen!
}
void LCD_LineTwo_Sub_WerksWerte(uint8_t menuRotary)                                                       // Werkswerte zurücksetzen LCD 2'te Zeile
{
  for (uint8_t i = 0; i < 16; i++) lcd.print(F(" ")); // Bis zum ende der unteren Zeile Leerzeichen setzen!
  lcd.setCursor(0, 1);                 // Untere Zeile
  lcd.print(F("Werkeinstell J/N"));    // 16 Stellen
  uint8_t curPos = 16;                 // "J = -3  N = -1)
  if (menuRotary == 1) curPos = curPos - 3;
  if (menuRotary == 2) curPos = curPos - 1;
  lcd.setCursor(curPos , 1); // Nu schau es blinkt an verschiedenen Stellen!
}
void leseTemp()                                                                                           // Einlesen des DHT22 Temperatur und Luftfeuchtigkeit
{
  humy = dht.readHumidity();
  temp = dht.readTemperature();
}
void leseBefehl()                                                                                         // Einlesen der Steuerbefehle
{
  char readChar;                           // Einzelnes Zeichen
  static uint8_t x = 0;                    // Position im Array
  static char buf[charNum] = {0};          // Zwischenspeicher
  while (Serial.available() > 0 )
  {
    readChar = Serial.read();              // Einlesen
    //Serial.print(readChar);
    if (!isControl(readChar))              // Zeichen ist kein Steuerzeichen ...
    {
      buf[x] = readChar;                   // ... dann aufnehmen ...
      x++;                                 // ... neue Position setzen
    }
    else                                   // ... ist Steuerzeichen
    {
      if (isLowerCase(buf[0]))             // erstes Zeichen Kleinbuchstabe? ...
      {

        sendData(buf);                     // ... verschicken ...
        memset(buf, 0, sizeof(buf));       // ... Puffer löschen und ...
        x = 0;                             // ... zurücksetzen
      }
    }
  }
}
void leseBahn()                                                                                           // Datensatz vom RennPC
{
  //const bool serialdebug = false;
  const uint32_t breakTimeRead = 50;       // Abbruchzeit in ms
  char readChar;                           // Einzelnes Zeichen
  static uint8_t x = 0;                    // Position im Array
  static char buf[charNum] = {0};          // Zwischenspeicher
  uint32_t lastmillis = millis();          // Startzeit merken...
  while (Serial1.available() > 0 && millis() - lastmillis < breakTimeRead)
  {
    readChar = Serial1.read();             // Einlesen
    //if (serialdebug) Serial.print(readChar);
    if (readChar == ';')                   // Feldende? ...
    {
      buf[x] = '\0';                       // ... CharArray abschliessen ...
      teileBuf(buf);                       // ... Übergeben zum teilen und senden ...
      x = 0;                               // ... Position im Array rücksetzen
    }
    else if (!isControl(readChar))         // Zeichen ist kein Steuerzeichen ...
    {
      buf[x] = readChar;                   // ... dann aufnehmen ...
      x++;                                 // ... neue Position setzen
    }
  }
}
void teileBuf(char *buf)                                                                                  // Teilt den (Feld)Puffer
{
  //  Serial.println(buf);
  static bool istGleich = true;            // Merker letzter/aktueller Wert gleich?
  char *c;                                 // Teilstring
  char lokalBuf[charNum] = {0};            // Zwischenspeicher für Vergleich (Zeichenkette) Array
  uint16_t lokalInt = 0;                   // Zwischenspeicher für Vergleich (Zahl) Array
  c = strtok(buf, ":");                    // Übernehme bis Trennzeichen 1
  //                                       // Beste Rundenzeit - Aufteilung kommentiert
  if (!strncmp(c, "BRZ", 3))               // Vergleich auf Feldname ...
  {
    lokalInt = atoi(strtok(NULL, ","));    // ... Erste Zahl - Trenner ist ,
    if (RZvk[0] != lokalInt)               // bisheriger Wert ist anders? ...
    {
      RZvk[0] = lokalInt;                  // ... dann setze den Wert ...
      istGleich = false;                   // ... merke, das neuer Wert gesetzt
    }
    lokalInt = atoi(strtok(NULL, ","));    // ... zweite Zahl ...
    if (RZnk[0] != lokalInt)               // ... wie vor
    {
      RZnk[0] = lokalInt;
      istGleich = false;
    }
    if (!istGleich)                        // Neue Daten bekommen? ...
    {
      sendFeld(0, 'Z');                    // ... sende Daten! ...
      istGleich = true;                    // ... setze Merker zurück
    }
  }
  else if (!strncmp(c, "BRF", 3))          // Beste Runde von Fahrername
  {
    strcpy(lokalBuf, strtok(NULL, ":"));   // Bester Fahrername
    if (F[0] != lokalBuf)                  // Inhalt geändert? ...
    {
      memcpy(F[0], lokalBuf, sizeof(lokalBuf)); // ... schreibe ins Array ...
      sendFeld(0, 'F');                         // ... sende Daten
    }
  }
  else if (!strncmp(c, "BRS", 3))          // Beste Runde auf Spur
  {
    lokalInt = atoi(strtok(NULL, ":"));    // Beste Spur
    if (AR[0] != lokalInt)                 // Inhalt geändert? ...
    {
      AR[0] = lokalInt;                    // ... nehme Daten auf ...
      sendFeld(0, 'R');                    // ... sende Daten: Spur0 Runden
      istGleich = false;                   // ..
    }
  }
  else if (!strncmp(c, "AR", 2))           // Rundennummer
  {
    uint8_t s = c[2] - '0';                // ermittelt die Spur
    lokalInt = atoi(strtok(NULL, ":"));
    if (AR[s] != lokalInt)
    {
      AR[s] = lokalInt;                    // ... nehme Daten auf ...
      sendFeld(s, 'R');                    // ... sende Daten: Spur s Anzahl Runden
      sendFeld(s, 'G');                    // ... sende Daten: Spur s Gesammtzahl Runden(Tagesrunden der Spur)
    }
  }
  else if (!strncmp(c, "RZ", 2))           // Rundenzeit
  {
    uint8_t s = c[2] - '0';
    lokalInt = atoi(strtok(NULL, ","));
    if (RZvk[s] != lokalInt)
    {
      RZvk[s] = lokalInt;
      istGleich = false;
    }
    lokalInt = atoi(strtok(NULL, ","));
    if (RZnk[s] != lokalInt)
    {
      RZnk[s] = lokalInt;
      istGleich = false;
    }
    if (!istGleich)
    {
      // ACHTUNG Nicht verrückt machen, der Fahrername für die Spur ist hier noch nicht ermittelt!
      // das sieht etwas komisch in der Ausgabe aus
      //      serMon(0);
      sendFeld(s, 'Z');
      istGleich = true;
    }
  }
  else if (!strncmp(c, "F", 1))            // Fahrer
  {
    uint8_t s = c[1] - '0';
    strcpy(lokalBuf, strtok(NULL, ":"));
    if (F[s] != lokalBuf)
    {
      memcpy(F[s], lokalBuf, sizeof(lokalBuf));
      sendFeld(s, 'F');
    }
  }
}
void sendeBahn(const uint32_t sendTimer) // versendet ein Feld aus den gespeicherten Daten ; sendTimer: Zeit in ms - in welchen Abständen die Funktion ein(!) Feld sendet ; Ein Umlauf: Anzahl Spuren(5) * Felder je Spur(3) * Zeit(5ms) ca. 75ms
{
  static uint32_t lastMillis = 0;                // Merker für letzte Sendung
  if (millis() - lastMillis > sendTimer)         //
  {
    enum {runde, zeit, Tagesrunde, fahrer, volt};            // was soll versandt werden
    static uint32_t lastFahrerSend = 0;                      // (Zeit-)Merker für den Versand eines Fahrernamen
    static uint32_t lastVoltageSend = 0;                     // (Zeit-)Merker für den Versand der Voltagedaten
    static uint8_t feld = runde;                             // Merker Welches Feld versandt wird
    static uint8_t spur = 0;                                 // aus welcher Spur
    lastMillis = millis();
    switch (feld)
    {
      case runde:                          // sende Anzahl Runden der Spur
        sendFeld(spur, 'R');
        break;
      case zeit:                           // sende Rundenzeit der Spur
        sendFeld(spur, 'Z');
        break;
      case Tagesrunde:                    // sende Tagesrunden der Spur
        sendFeld(spur, 'G');
        break;
      case fahrer:                         // sende Fahrername ser Spur
        if (millis() - lastFahrerSend > 1000)
        {
          sendFeld(spur, 'F');
          lastFahrerSend = millis();
        }
        break;
      case volt:                           // sende Netzteil-Spannung
        if (millis() - lastVoltageSend > 100)
        {
          sendFeld(spur, 'V');
          lastVoltageSend = millis();
        }
        spur++;
        if (spur >= spuren + 1)
        {
          spur = 0;
        }
        break;
    }
    feld++;
    if (feld > volt)
    {
      feld = runde;
    }
  }
}
void sendFeld(const uint8_t spurNummer, const char feldName)
{
  char lokalBuf[maxPSize] = {0};
  switch (feldName)
  {
    case 'F':                              // sendet Fahrernamen
      sprintf(lokalBuf, "%uF %s", spurNummer, F[spurNummer]);
      break;
    case 'R':                              // sendet aktuelle Runde
      sprintf(lokalBuf, "%uR %u", spurNummer, AR[spurNummer]);
      break;
    case 'Z':                              // sendet aktuelle Rundenzeit
      sprintf(lokalBuf, "%uZ %u %u", spurNummer, RZvk[spurNummer], RZnk[spurNummer]);
      break;
    case 'G':                              // sendet aktuelle Tagesrunde
      sprintf(lokalBuf, "%uG %u", spurNummer, TagZahl[spurNummer]);
      break;
    case 'V':                              // sendet aktuelle Voltage
      sprintf(lokalBuf, "%uV %u", spurNummer, voltage[spurNummer]);
      break;

  }
  if (strlen(lokalBuf) > 0)                // Nur wenn was im Puffer steht senden!
  {
    sendData(lokalBuf);
    // Serial.println(lokalBuf);
  }
}
void sendData(char *sendbuffer)
{
  radio.write(sendbuffer, strlen(sendbuffer));
//  Serial.print(F("Daten gesendet: "));
//  Serial.println(sendbuffer);
}
void sendeZeit(const uint32_t sendTimer)                                                                   // Versendet LokalZeit
{
  static uint32_t lastzeitMillis = 0;          // Merker wann das letzte Mal
  if (millis() - lastzeitMillis >= sendTimer)
  {
    char zeit[14] = {0};                   // Puffer
    lastzeitMillis = millis();
    sprintf(zeit, "T %ld", now());         // Datenpaket zusammenstellen
    sendData(zeit);
  }
}
void sendeTemp(const uint32_t sendTimer)                                                                    // Versendet LokalTemperatur
{
  static uint32_t lasttempMillis = 0;          // Merker wann das letzte Mal
  if (millis() - lasttempMillis >= sendTimer)
  {
    char Grad[2] = {0};                   // Puffer
    char Luft[2] = {0};                   // Puffer
    lasttempMillis = millis();
    sprintf(Grad, "C %u", temp);            // Datenpaket zusammenstellen lesen der Temperatur in Celsius
    sprintf(Luft, "H %u", humy);            // Datenpaket zusammenstellen lesen der Luftfeuchtigkeit in %
    sendData(Grad);
    sendData(Luft);
  }
}
void startBild()                                                                                          // LCD Anzeige beim Einschalten;
{
  // ************ Anfang LCD Start-Anzeige ***********
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("  Test-Version  "));
  lcd.setCursor(0, 1);
  lcd.print(F(" Rotary-Version "));
  delay(1000);
  // *************Ende LCD Start-Anzeige *************
}
void displayKalender()                                                                                    // LCD im Sendemodus; wird alle 60 sek aktualisiert
{
  if (minute() != minutealt)
  {
    minutealt = minute();
    char zeileAnzeige[16] = {0};
    memset(zeileAnzeige, 0, sizeof(zeileAnzeige - 1));
    sprintf(zeileAnzeige, "%s %02u.%02u.  %02u:%02u", dayShortStr(weekday()), day(), month(), hour(), minute());
    lcd.setCursor(0, 0);
    lcd.print(ver);
    lcd.setCursor(0, 1);
    lcd.print(zeileAnzeige);
    // DEBUG_PRINTLN(zeileAnzeige);
  }
}// Ende Start-Tab
uint8_t shortYear()                                                                                       // Kürze Jahr auf 2 Ziffern
{
  return (year() - 2000);
}
void leseAmpel()                                                                                          // Lese Eingangssignal der AMPEL ein
{
  bool ampelTemp[sizeof(ampelPin)] = {0};                   // temporäre Variable vorher bool ampelTemp[sizeof(spuren)] = {0};
  char sendAmpel[10] = {0};                  // Für Übergabe
  for (uint8_t i = sizeof(ampelPin); i > 0 ; i--)
  {
    ampelTemp[i] = digitalRead(ampelPin[i]); // liest den Zustand der Pin ein
  }
  // hier vergleiche ist das gelesene mit dem bekannten
  // nur wenn ungleich, dann senden
  if (memcmp(ampelTemp, ampel, sizeof(ampelTemp)) != 0)
  {
    memcpy(ampel, ampelTemp, sizeof(ampel));
    sprintf(sendAmpel, "A %i", (uint16_t)ampel);   // Datenpaket zusammenstellen
    //    sendData(&ampel);
    Serial.print(F("Sende Ampel: ")); Serial.println(ampel[3 ]);
  }
}
void leseVoltage()                                                                                        // Lese Eingangspannung der Spuren ein
{
  //Aufbau nach https://mint-unt.de/mediapool/78/781152/data/Voltmeter.pdf
  // max. Wert der Eingangsspannung die der Spannungsteiler aufnimmt
  // um am AnalogPin ca. 5V abzubilden 100/10K->55.0V 13/2,5K->30.0V
  // Die Spannungsteiler geben keine 5V an der Obergrenze zurück!
  // mit map wird später die Obergrenze auf 10xx festgelegt
  const uint16_t maxVoltageDividier = 387; // 2VK 1NK -stelle

  //
  const uint8_t maxUmlauf = 2;  // Anzahl der Werte, die in den Puffer gehen
  static uint8_t umlauf = 0;     // Startwert Umlauf
  //  uint8_t VoltVK[spuren + 1] = {0};
  //  uint8_t VoltNK[spuren + 1] = {0};
  static uint8_t localVoltage[maxSpuren + 1][maxUmlauf] = {0}; // Lokaler Ringspeicher
  bool tik = false;                                            // nur wenn tik == true, wird die Funktion insgesamt ausgeführt
  const uint16_t tikTime = 250;                                // Zeit bis tik in ms
  if (millis() % tikTime == 0) tik = true;                     //Festlegen von tik
  //tik = true;                                                // ERSATZWEISE ohne Pause
  if (tik)
  {
    for (uint8_t i = 0; i < spuren; i++)
    {
      // Hier wird ein Wert zurück gegeben der als OberGrenze maxVoltageDividier hat!
      // Wenn der nicht stimmt, stimmt das Ergebnis nicht!!!
      uint16_t mapVoltage = map(analogRead(voltagePin[i]), 0, 1024, 0, maxVoltageDividier);
      if (mapVoltage > 250) mapVoltage = 255;   // Obergrenze 25V - Wenn darüber, mache 25.5 (255) draus
      localVoltage[i][umlauf] = mapVoltage;     //Übernehme den Wert in den localen Ringspeicher
      // Erste Ausgabe
      // Serial.print(F("Index: ")); Serial.print(i); Serial.print(F(" Inhalt: ")); Serial.println(localVoltage[i][umlauf]);
    }
    // Erklärt sich von selbst
    umlauf++; if (umlauf >= maxUmlauf) umlauf = 0;
    //
    // Ab hier berechnen
    if (umlauf == 0)
    {
      uint16_t zwischenSpeicher = 0;                    // Zwischenspeicher muss zur Aufnahme von 10 * byte fähig sein


      for (uint8_t i = 1; i <= spuren; i++)             // zählt Spuren durch - ACHTUNG! Beginnt mit Spur 1!
      {
        zwischenSpeicher = 0;                           // Setzen
        for (uint8_t b = 0; b < maxUmlauf; b++)         // Auslesen aller Werte anhand des Index
        {
          //          Serial.print(F("localVoltageIndex "));
          //          Serial.print(b);
          //          Serial.print(": ");
          //          Serial.println(localVoltage[i - 1][b]);
          zwischenSpeicher += localVoltage[i - 1][b];     // und aufaddieren
        }
        voltage[i] = (zwischenSpeicher / maxUmlauf);      // VoltageWert der Bahn abspeichern
        //        Serial.print(F("VoltagePin: "));                // Und ab hier ausgeben
        //        Serial.print(i);
        //        Serial.print(" ");
        //        Serial.print(zwischenSpeicher);
        //        Serial.print(" ");
        //        Serial.print(voltage[i]);
        //        Serial.print(" ");
        //        if (voltage[i] > 99)
        //          Serial.print(voltage[i] % 1000 / 100);
        //        else
        //          Serial.print(" ");
        //        if (voltage[i] > 9)
        //          Serial.print(voltage[i] % 100 / 10);
        //        else
        //          Serial.print(" ");
        //        Serial.print(".");
        //        Serial.print(voltage[i] % 10);
        //        Serial.println();
        //        Serial.print("= ");
        //        Serial.println(voltage[i]);
      }
    }
  }
}
bool tasteLinks()                                                                                         // gibt 0 zurueck, wenn Taste Links gedrueckt - sonst 1
{
  static bool tasteMerker = false;
  static uint32_t lastTime = 0;
  if (digitalRead(TsLinks) && tasteMerker)                                          // debounce
  {
    tasteMerker = false;
  }
  if (!digitalRead(TsLinks) && (millis() - lastTime >= bounceTime) && !tasteMerker) // Nur wenn Taste gedrueckt UND bouncezeit abgelaufen ...
  {
    lastTime = millis();                                                            // ... setze Merker ...
    tasteMerker = true;
    return false;                                                                   // ... und gebe 0 zurueck
  }
  else                                                                              // Wenn nicht ...
  {
    return true;                                                                    // ... dann nicht
  }
}
bool tasteRechts()                                                                                        // gibt 0 zurueck, wenn Taste Rechts gedrueckt - sonst 1
{
  static bool tasteMerker = false;
  static uint32_t lastTime = 0;
  if (digitalRead(TsRechts) && tasteMerker)
  {
    tasteMerker = false;
  }
  if (!digitalRead(TsRechts) && (millis() - lastTime >= bounceTime) && !tasteMerker)
  {
    lastTime = millis();
    tasteMerker = true;
    return false;
  }
  else
  {
    return true;
  }
}
void serMon()
{
  const uint32_t intervall = 700;
  serMon(intervall);
}
void serMon(const uint32_t intervall)
{
  static uint32_t lastmillis = 0;
  ausgabe = true;
  if (ausgabe == true && (millis() - lastmillis) > intervall) // Wenn Ausgabe frei
  {
    lastmillis += intervall;
    ausgabe = false;
    for (uint8_t i = 0 ; i <= spuren; i++)
    {
      if (!i)
        Serial.print("BRS");
      else
      {
        Serial.print("AR");
        Serial.print(i);                              //   i=0 ist Beste Runde auf Spur "i" gefahren
      }
      Serial.print(": ");
      Serial.println (AR[i]);                         //   i=1,2,3,4 ist AnzahlRunden auf Spur "i"
      if (!i)
        Serial.print("BRZ");
      else
      {
        Serial.print("RZ");                          //   i=0 ist Beste Rundenzeit von allen Spuren
        Serial.print(i);
      }
      Serial.print(": ");
      Serial.print(RZvk[i]); Serial.print(" - "); Serial.print(RZnk[i]);
      if (!i)
        Serial.print(" BRF");                        //   i=0 ist Bester Fahrer von allen Spuren
      else
      {
        Serial.print(" F");
        Serial.print(i);
      } Serial.print(": "); Serial.println(F[i]);   //   i=1,2,3,4 ist Fahrer von Spur "i"
    }

    //Serial.println();


  }
}
void merkeRunden()                                                                                        // Zählt alle Runden pro Spur zusammen "Tageszahl" setzt Ausgang SPS und merkt sich den Führenden,
{
  for (byte s = 1; s <= spuren; s++)
  {
    if ((AR[s] > maxRunden))                        // hat AR[i] mehr Runden gefahren als maxRunden
    {
      maxRunden = AR[s];                            // dann hat AR[i] die meist gefahrenden Runden
      Poleposition = s;                             // Führende Spur ist Poleposition
    }
    if (AR[s] > lastAR[s])
    {
      TagZahl[s] = TagZahl[s] + AR[s] - lastAR[s];  // Erechne Tageszahl von Spur "s"
      lastAR[s] = AR[s];                            //
      digitalWrite(spsPin[s - 1], HIGH);            // Setze SPS-Ausgang "s" auf High
      spsMillis[s - 1] = millis();                  // Zeitmerker für SPS-Ausgang Verzögerung
    }
    else
    {

      bool neustart = true;
      for (byte b = 1; b <= spuren; b++)
      {
        if (AR[b] >= 1) neustart = false;
      }
      if (neustart)
      {
        for (byte d = 1; d <= spuren; d++)
        {
          lastAR[d] = 0;                      // setze zuletzt
        }
        maxRunden = 0;                        // setze Poleposition zurück
      }
    }
  }
}
void Werkseinstellung()                                                                                   // Setzt alle Werte zurück auf Standard
{
  // Es werden die Voreinstellungen abgespeichert und neu gestartet!!!
  EEPROM.update(1, 3);           // Sendeleistung NRF Sender 4=MAX;3=HIGH;2=LOW;1=MIN
  EEPROM.update(2, 127);         // Speicheradresse ,Funkkanal  (Werte 1 bis 127)Werkseinstellung=127
  EEPROM.update(5, 4);           // Anzahl der Fahrspuren

  //DEBUG_PRINT(F("Werkseinstellungen wurden geladen"));
}
void Datensicherung()                                                                                     // Prüfen ob sich Einstellwerte verändert haben, wenn ja neue Werte ins EEprom schreiben
{
  EEPROM.update(1, watt);
  EEPROM.update(2, kanal);
  EEPROM.update(5, AnzahlSpuren);
  DEBUG_PRINT(F("Ich war bei der Datensicherung"));
}
void SpsSignalzeit()                                                                                      // Signalverlängerung um 20ms für SPS-Ausgang; 
{
  for (byte s = 1; s <= spuren; s++)
  {
    if (millis() - spsMillis[s - 1] >= 20)                                                                // 20ms
    {
      digitalWrite(spsPin[s - 1], LOW);
    }
  }
}
[/code]
1 Like

Ich muss das erstmal einordnen - irgendwie sieht das aus, als würde irgendwo was fehlen, da Du sowohl änderst als auch weiterschaltest.
Aber las mir mal Zeit. Weiter machen kannste, aber bitte nicht am Menu und den Einstellungen. Das nehme ich komplett raus und versuch das runterzubrechen.

Meine Hardware beschränkt sich auf'n Mega nen Display und Encoder...
Man liest sich.

Hier mal ein Bild von meinem Hardware-Chaos :rofl:
Sender-Chaos

Da hat Tommy56 mal geschrieben....blickt Ihr da noch durch mit dem Menü?
Was mag er wohl schreiben wenn er das sieht.... :thinking: :rofl:

Wollte mich dann mal an die Ampel begeben......Hardware gebastelt mit Optokoppler...paar Kabel gezogen. Einlesen funktioniert, das senden leider nicht.....es kommt folgende Fehlermeldung.

cannot convert 'bool ()[5]' to 'char' for argument '1' to 'void sendData(char*)'

void leseAmpel()                                                                                          // Lese Eingangssignal der AMPEL ein
{
  bool ampelTemp[sizeof(ampelPin)] = {0};                   // temporäre Variable; hier stand vorher "bool ampelTemp[sizeof(spuren)] = {0};" aber benötigt werden die Anzahl Eingänge der Ampel( es feste 5 Eingänge)
  char sendAmpel[10] = {0};                                 // Für Übergabe
  for (uint8_t i = 0; i <= 4 ; i++)
  {
    ampelTemp[i] = digitalRead(ampelPin[i]);                // liest den Zustand der Pin ein
  }
  // hier vergleiche ist das gelesene mit dem bekannten
  // nur wenn ungleich, dann senden
  if (memcmp(ampelTemp, ampel, sizeof(ampelTemp)) != 0)    // Die beiden Arrays und die Länge des Speicherbereichs, die verglichen werden soll.
  {
    memcpy(ampel, ampelTemp, sizeof(ampel));                
    sprintf(sendAmpel, "A %i", (uint16_t)ampel);          // Datenpaket zusammenstellen
    sendData(&ampel);
    
    for (uint8_t i = 0; i <= 4 ; i++)
    {      
    Serial.print(F("Sende Ampel Nr.")); Serial.print(i+1);
    Serial.print(F(" Schaltzustand="));
    Serial.println(ampel[i]);
    }
  }
}

Ist das ein größeres Problem, was wir hinten anhängen sollten?
Oder sieht man den Fehler auf Anhieb, nur ich wieder nicht?