Serielle Datenstring vom Computer im Adruino Mega einlesen und aufteilen

Es animiert mich aber auch, mal zu überlegen, wie ich das eventuell realisieren würde, denn derzeit ist das noch Neuland für mich.

Mein Ansatz wäre "weniger Programm", die "Daten steuern die Abläufe". Also mache ich ein Feld mit den Texten und eine Struktur für die Menüpunkte. Als Elemente neben dem Textzeiger noch Min- und Maxwert und die einzustellende Variable, derzeit immer 0 bis 255 für uint8_t, nur bei den Fahrspuren 1 bis 6 mit 4 als Startwert.

Die Anzahl der Fahrspuren könnte man also menue[5].wert entnehmen.

Meine Idee als getestetes Programm:

// Tower-Control auf MEGA 2560
const char ver[] = {"TC-V1.0.1 rotary"};
// LCD
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

// RotaryEncoder
#include <rotary.h>
Rotary r = Rotary(5, 3, 23); // Rotary(Encoder Pin 1, Encoder Pin 2, Button Pin)

const char *text[] = {
  //     0                  1                  2                  3                  4
  "  Hauptmenue  " , "  Verlassen   " , " Turm-Anzeige " , "  Turm-Licht  " , "  Helligkeit  " ,
  //     5                  6                  7                  8                  9                 10
  "  RGB-1 Werte " , "  Fahrspuren  " , " Startanzeige " , "   IDNummer   " , "   Funkkanal  " , "  WerksWerte  "
};

struct Menue
{
  const char *zeile1;
  const char typ;
  const uint8_t min, max;
  const uint8_t abwerk;
  uint8_t wert;
};

Menue menue[] =
{
  {text[ 0], '0', 1,  10,   0,   0},  // Hauptmenue
  {text[ 1], 'V', 0,   0,   0,   0},  // Verlassen
  {text[ 2], '1', 0, 255, 127, 127},  // Turm-Anzeige
  {text[ 3], '2', 0, 255, 127, 127},  // Turm-Licht
  {text[ 4], '3', 0, 255, 127, 127},  // Helligkeit
  {text[ 5], '4', 0, 255, 127, 127},  // RGB-1 Werte
  {text[ 6], '5', 1,   6,   4,   4},  // Fahrspuren
  {text[ 7], '6', 0, 255, 127, 127},  // Startanzeige
  {text[ 8], '7', 0, 255, 127, 127},  // IDNummer
  {text[ 9], '8', 0, 255, 127, 127},  // Funkkanal
  {text[10], 'W', 1,  10,   0,   0}   // WerksWerte
};

void menueanzeige()
{
  static uint8_t aktMenu = 255;
  if (aktMenu > (sizeof(text) / sizeof(text[0])) )
  {
    r.process();
    if (r.buttonPressedReleased(20))
    {
      aktMenu = 0;  // Hauptmenü
      menue[aktMenu].wert = 0;
    }
  } else {
    menueausfuehrung(menue[aktMenu], aktMenu);
  }
}

void menueausfuehrung(Menue &m, uint8_t &aktMenu)
{
  uint8_t aktRotary = getRotary(m.wert, m.min, m.max);  // Abfrage RotaryEncoder
  static uint8_t altMenu = 255;
  switch (m.typ)
  {
    case 'V':
      lcd.clear();
      lcd.print("Ende Menue Verl");  // Text nur jetzt zum Testen
      aktMenu = 255;
      altMenu = 255;
      break;
    case 'W':
      for (uint8_t j = m.min; j < m.max; j++)
      {
        menue[j].wert = menue[j].abwerk;
      }
      lcd.clear();
      lcd.print("Ende Menue Werk");  // Text nur jetzt zum Testen
      aktMenu = 255;
      altMenu = 255;
      break;
    default:
      if ( (altMenu != aktMenu) || (m.wert != aktRotary) )
      {
        altMenu = aktMenu;
        m.wert = aktRotary;
        lcd.setCursor(0, 0);
        lcd.print('*'); lcd.print(m.zeile1); lcd.print('*');
        lcd.setCursor(0, 1);
        if (aktRotary > m.min)
        {
          lcd.print("<              ");
        } else {
          lcd.print("               ");
        }
        if (aktRotary < m.max)
        {
          lcd.print(">");
        } else {
          lcd.print(" ");
        }
        lcd.setCursor(1, 1);
        if (aktMenu)
        {
          lcd.print(aktRotary);
        } else {
          lcd.print(text[aktRotary]);
        }
      }
      if (r.buttonPressedReleased(20))
      {
        if (aktMenu)
        {
          aktMenu = 0;

        } else {
          aktMenu = aktRotary;
        }
      }
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  //lcd.begin(); // Für XY Projekt
  lcd.init(); // Für Benziner und agmue
  lcd.backlight();
  // ************ Anfang LCD Start-Anzeige ***********
  lcd.setCursor(0, 0);
  lcd.print(ver);
  // *************Ende LCD Start-Anzeige *************
}// Ende Setup

void loop()
{
  //Serial.println("tak"); /debug non-Blocking  anzeige();
  menueanzeige();
}

uint8_t getRotary(uint8_t rotaryWert, const uint8_t rotaryMin, const uint8_t rotaryMax)
{
  uint8_t lastRotary = rotaryWert;  // fuer debugausgabe
  uint8_t val = r.process();     // Check the encoder
  if (val == r.clockwise())
  {
    rotaryWert ++;
  }
  else if (val == r.counterClockwise())
  {
    rotaryWert --;
  }
  if (rotaryWert > rotaryMax)
  {
    rotaryWert = rotaryMax;
  }
  else if (rotaryWert < rotaryMin)
  {
    rotaryWert = rotaryMin;
  }
  if (lastRotary != rotaryWert) Serial.println(rotaryWert); // debugausgabe
  return rotaryWert;
}

Ich finde es kürzer und übersichtlicher :thinking:

Nicht alle Ideen der letzten Beiträge sind berücksichtigt, ich hatte schon vorher angefangen :wink:

EDIT: Ich habe das Verlassen des Menüs überarbeitet. Ergänzt habe ich die Werkswerte und den Menütyp.

Das wohl.
Wenn ich meine ganzen Seriellen Ausgaben rausnehme, wird das auch kürzer :slight_smile:

@Benziner dann hier meine Vorgabe.
Ich habe nur das Turmanzeige und Turmlichtmenu gebaut.
Alles was geht, kommt auf dem SerMon raus.
Ich will wissen, ob Du das verstehst was da passiert. Es ist sicher nicht ganz einfach, ist aber so gebaut und kommentiert, das ich es für verständlich halte.
Wenn es passt und keine weiteren Fragen sind, baue ich noch den tab Helligkeit aus der funktion im grundausstattungstab als Vorlage und mach noch mit den Zeichenketten fürs Display rum. Der Rest ist dann Fleissarbeit.
Turmlicht.ino (1,7 KB)
Turmanzeige.ino (1,8 KB)
Submenu_Grundausstattung.ino (2,0 KB)
Hauptmenue.ino (4,2 KB)
Turm_Empfaenger_v50.0B.ino (2,9 KB)

Und dann überleg Dir mal, ob Du nicht doch lieber die Encoderwerte überlaufen lassen willst - also wenn max erreicht wird der nächste min - bzw. anders rum.
Gesamter Code zum lesen:

// Tower-Control auf MEGA 2560
const char ver[] = {"TC-V1.0.3 rotary"};

// LCD
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
byte minutealt = 61;                  // Start-LCD Uhrzeit wird nur alle Minute aktualisiert

// Uhr
#include <DS1307RTCB.h>                  // auf die TimeLibB angepasste 1307 von Paul Stoffregen
#include <TimeLibB.h>                    // auf deutsch umgeschriebene TimeLib von Paul Stoffregen

// RotaryEncoder
#include <rotary.h>
Rotary r = Rotary(5, 3, 23); // Rotary(Encoder Pin 1, Encoder Pin 2, Button Pin)
const uint8_t bounce = 20;   // zeit in ms die zwischen zwei Impulsen vergehen muss

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  lcd.begin(); // Für XY Projekt
  //lcd.init(); // Für Benziner
  lcd.backlight();
  //  RTC.begin();
  // ************ Anfang LCD Start-Anzeige ***********
  lcd.setCursor(0, 0);
  lcd.print(ver);
  // *************Ende LCD Start-Anzeige *************
}// Ende Setup

void loop()
{
  menue();
 if (!menue()) displayKalender();
}

bool menue()           // gibt true zurück, wenn menu aufgerufen
{
  static uint8_t myMenu = 0;
  if (myMenu == 0 && r.buttonPressedReleased(bounce))      // wenn bisher kein Menu
  {
    lcdMenuLineOne(1);                                     // setze LCD Zeile 1 "Hauptmenu"
    myMenu = Hauptmenue(myMenu);                           // rufe das Hauptmenu -> Rückgabewert ist das Auswahlmenu
    Serial.println(F("Button pressed Aufruf Hauptmenue"));
  }
  else  if (myMenu != 0)
  {
    myMenu = Hauptmenue(myMenu);                           // aufruf bei jedem Umlauf wenn menu aktiv
  }                                                        // rueckgabewert aus Hauptmenu 
  return myMenu;    // wenn im Menumodus, wird true sonst false zurück gegeben
}

// Funktion gibt aboluten add-/subtrahierten encoderWert zurück
uint8_t getRotary(uint8_t rotaryWert, const uint8_t rotaryMin, const uint8_t rotaryMax)
{
  // *INDENT-OFF*
  uint8_t lastRotary = rotaryWert;                             // fuer debugausgabe
  uint8_t val = r.process();                                   // Check encoder
  //Serial.println(val);
       if (val == r.clockwise())        {rotaryWert ++;}       // Wert berechnen
  else if (val == r.counterClockwise()) {rotaryWert --;}
       if (rotaryWert > rotaryMax) {rotaryWert = rotaryMax;}   // Begrenzung berechnen
  else if (rotaryWert < rotaryMin) {rotaryWert = rotaryMin;}
  if (lastRotary != rotaryWert) Serial.println(rotaryWert);    // debugausgabe
  return (rotaryWert);
  // *INDENT-ON*
}

void displayKalender() //
{
  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);
    Serial.println(zeileAnzeige);
  }
}

// Ab hier Tab Hauptmenue
uint8_t Hauptmenue(uint8_t menuRotary) // Auswahl Hauptmenue...
{
  const uint8_t rotaryMin = 1;                                      // erster Eintrag
  const uint8_t rotaryMax = 11;                                     // letzer Menueintrag -> Menu verlassen
  static uint8_t subMenu = 0;                                       // Merker auf naechstes Untermenu
  static bool lcdInitMerker = false;                                // fuer erstmalige Anzeige des UnterMenu
  if (subMenu == 0)                                                 // bisher kein SubMenu ausgewählt
  {
    uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);// Abfrage RotaryEncoder
    if (menuRotary != tikRotary)        // neuer Wert? dann zeige an
    {
      menuRotary = tikRotary;           // merke den aktuellen RotaryWert
      lcdMenuLineTwo(menuRotary);       // untere Zeile im lcd
    }
    if (r.buttonPressedReleased(bounce))// EncoderButton ausgelöst?
    {
      lcdMenuLineOne(menuRotary + 1);
      subMenu = menuRotary;
      Serial.print(F("switch Menu: "));
      Serial.println(subMenu);
    }
  }
  if (subMenu)
    switch (menuRotary)
    {
      case rotaryMin:                    // Gehe zu Turmanzeige-Menue
        if (!lcdInitMerker)lcdSubTurmLineTwo(1);
        subMenu = turmAnzeige(subMenu);
        break;
      case 2:                           // Gehe zu Turmlicht-Menue
        if (!lcdInitMerker)lcdSubLichtLineTwo(1);
        subMenu = turmLicht(subMenu);
        break;
      case 3:                           // Gehe zu Helligkeit-Menue
        lcdMenuLineOne(4);
        subMenu = Helligkeit(subMenu);
        break;
      case 4:                           // Gehe zu LEDFarben-Menue
        lcdMenuLineOne(5);
        subMenu = LEDFarben(subMenu);
        break;
      case 5:                           // Gehe zu Fahrspuren-Menue
        lcdMenuLineOne(6);
        subMenu = Fahrspuren(subMenu);
        break;
      case 6:                           // Gehe zu  Startanzeige-Menue
        lcdMenuLineOne(7);
        subMenu = Startanzeige(subMenu);
        break;
      case 7:                           // Gehe zu  IDNummer-Menue
        lcdMenuLineOne(8);
        subMenu = IDNummer(subMenu);
        break;
      case 8:                           // Gehe zu  Funkkanal-Menue
        lcdMenuLineOne(9);
        subMenu = Funkkanal(subMenu);
        break;
      case 9:                           // Gehe zu  WerksWerte-Menue
        lcdMenuLineOne(10);
        subMenu = WerksWerte(subMenu);
        break;
      case 10:                          // Gehe zu  Speichern-Menue
        lcdMenuLineOne(11);
        subMenu = Speichern(subMenu);
        break;
      case rotaryMax:                   // Hauptmenue verlassen
        Serial.println(F("Button pressed Hauptmenue verlassen"));
        lcd.setCursor(0, 0);
        lcd.print(ver);
        minutealt = 61;
        subMenu = false;
        menuRotary = 0;
        break;
    }
  if (subMenu) lcdInitMerker = true; else lcdInitMerker = false; //
  return (menuRotary);
}


void lcdMenuLineOne(const uint8_t auswahl)  // Inhalt der ersten Zeile des LCD-Menu
{
  lcd.setCursor(0, 0);
  const char *line1[] = {" "                , "*  Hauptmenue  *" , "* Turm-Anzeige *" , "*  Turm-Licht  *" ,
                         "*  Helligkeit  *" , "*  RGB-1 Werte *" , "*  Fahrspuren  *" , "* Startanzeige *" ,
                         "*   IDNummer   *" , "*   Funkkanal  *" , "*  WerksWerte  *" , "*   Speichern  *"
                        };
  lcd.print(line1[auswahl]);
  //Serial.print(F("Hauptmenu1 "));
  //Serial.println(line1[auswahl]);
}
void lcdMenuLineTwo(const uint8_t auswahl) // Inhalt der zweiten Zeile des Lcd-Menu im Hauptmenu
{
  lcd.setCursor(0, 1);
  const char *line2[] = {" "                , "  Turm-Anzeige >" , "<  Turm-Licht  >" , "<  Helligkeit  >",
                         "<  LED-Farben  >" , "< Spurenanzahl >" , "< Startanzeige >" , "<   ID-Nummer  >",
                         "<   Funkkanal  >" , "<  Werks-Werte >" , "<  Speichern   >" , "<   Verlassen   "
                        };
  lcd.print(line2[auswahl]);
  Serial.print(F("Hauptmenu2 "));
  Serial.println(line2[auswahl]);
  //  printLcdLine(1,line2[auswahl]);
}

// Ab hier Tab Turmanzeige
uint8_t turmAnzeige(uint8_t menuRotary)         // Turmanzeige-menue "Was soll Turm anzeigen werden: Rundenzeit / Rundenanzahl / Uhr_Datum / Extern "
{
  const uint8_t rotaryMin = 1;
  const uint8_t rotaryMax = 5;
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);  // Abfrage RotaryEncoder
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    lcdSubTurmLineTwo(tikRotary);                                   // untere Zeile im lcd
  }
  if (r.buttonPressedReleased(bounce))
  {
    Serial.print(F("Button pressed Turmanzeige -> Wert: "));
    Serial.println(tikRotary);
    switch (menuRotary)
    {
      case rotaryMin:   // Turmanzeige  "Rundenzeit"
      Serial.print(F("Rundenzeit"));
        break;
      case 2:   // Turmanzeige  "Rundenanzahl"
      Serial.print(F("Rundenanzahl"));
        break;
      case 3:   // Turmanzeige "Datum / Uhrzeit"
      Serial.print(F("Datum / Uhrzeit"));
        break;
      case 4:   // Turmanzeige "Extern-Auswahl"
      Serial.print(F("Extern-Auswahl"));
        break;
      case rotaryMax:   // Turmanzeige gehe zurück zu Hauptmenue
      Serial.print(F("-> ins Hauptmenu"));
        break;
    }
    Serial.println(F(" ausgewaehlt"));
    Serial.println();
    lcdMenuLineOne(1);
    lcdMenuLineTwo(1);
    return 0;
  }
  return menuRotary;
}

void lcdSubTurmLineTwo(const uint8_t auswahl) // Inhalt der zweiten Zeile des Lcd-Menu im Turmmenu
{
  static uint8_t oldAuswahl = 0;
  lcd.setCursor(0, 1);
  const char *line2[] = {"", " Zeiten-Anzeige>" , "<Runden-Anzeige>" , "<  Uhr/Datum   >" , "< Ex-Steuerung >" , "<    Zurueck    "};
  lcd.print(line2[auswahl]);
  if (oldAuswahl != auswahl)
  {
    oldAuswahl = auswahl;
    Serial.print(F("SubMenu Tumranzeige: "));
    Serial.println(line2[auswahl]);
  }
}

// ab hier tab Turmlicht
uint8_t turmLicht(uint8_t menuRotary)         // Was soll Turm leuchten..... EG-Licht an / OG-Licht an / EG/OG-Licht an / Licht Aus
{
  const uint8_t rotaryMin = 1;
  const uint8_t rotaryMax = 5;
  uint8_t tikRotary = getRotary(menuRotary, rotaryMin, rotaryMax);  // Abfrage RotaryEncoder
  if (menuRotary != tikRotary)
  {
    menuRotary = tikRotary;
    lcdSubLichtLineTwo(tikRotary);                                   // untere Zeile im lcd
  }
  if (r.buttonPressedReleased(bounce))
  {
    Serial.print(F("Button pressed Turmlicht -> Wert: "));
    Serial.println(tikRotary);
    switch (tikRotary)
    {
      case 1:   // Turmlicht "Licht EG" an
        Serial.print(F("Licht EG an"));
        break;
      case 2:   // Turmlicht "Licht OG" an
        Serial.print(F("Licht OG an"));
        break;
      case 3:   // Turmlicht "Licht EG / OG" an
        Serial.print(F("Licht EG / OG an"));
        break;
      case 4:   // Turmlicht "Licht aus"
        Serial.print(F("Licht aus"));
        break;
      case 5:   // zurück zum Hauptmenue
        Serial.print(F("-> ins Hauptmenu"));
        break;
    }
    Serial.println(F(" ausgewaehlt"));
    Serial.println();
    lcdMenuLineOne(1);
    lcdMenuLineTwo(2);
    return 0;
  }
  return (menuRotary);
}

void lcdSubLichtLineTwo(const uint8_t auswahl) // Inhalt der zweiten Zeile des Lcd-Menu im Lichtmenu
{
  static uint8_t oldAuswahl = 0;
  lcd.setCursor(0, 1);
  const char *line2[] = {"", "   Licht EG    >", "<  Licht OG    >", "<  Licht EG/OG >", "<  Licht-Aus   >","<    zurueck    "};
  lcd.print(line2[auswahl]);
  if (oldAuswahl != auswahl)
  {
    oldAuswahl = auswahl;
    Serial.print(F("SubMenu Lichtanzeige: "));
    Serial.println(line2[auswahl]);
  }
}

// Submenue_Grundausstattung

uint8_t Helligkeit(uint8_t menuRotary)
{
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed Helligkeit -> zurück ins Hauptmenu"));
    lcdMenuLineOne(1);
    lcdMenuLineTwo(3);
    return false;
  }
  return menuRotary;
}

uint8_t LEDFarben(uint8_t menuRotary)
{
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed LED-Farben -> zurück ins Hauptmenu"));
    lcdMenuLineOne(1);
    lcdMenuLineTwo(4);
    return false;
  }
  return menuRotary;
}

uint8_t Fahrspuren(uint8_t menuRotary)
{
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed Fahrspuren -> zurück ins Hauptmenu"));
    lcdMenuLineOne(1);
    lcdMenuLineTwo(5);
    return false;
  }
  return menuRotary;
}

uint8_t Startanzeige(uint8_t menuRotary)
{
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed Startanzeige -> zurück ins Hauptmenu"));
    lcdMenuLineOne(1);
    lcdMenuLineTwo(6);
    return false;
  }
  return menuRotary;
}

uint8_t IDNummer(uint8_t menuRotary)
{
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed ID-Nummer -> zurück ins Hauptmenu"));
    lcdMenuLineOne(1);
    lcdMenuLineTwo(7);
    return false;
  }
  return menuRotary;
}

uint8_t Funkkanal(uint8_t menuRotary)
{
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed Funkkanal -> zurück ins Hauptmenu"));
    lcdMenuLineOne(1);
    lcdMenuLineTwo(8);
    return false;
  }
  return menuRotary;
}

uint8_t WerksWerte(uint8_t menuRotary)
{
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed Werks-Werte -> zurück ins Hauptmenu"));
    lcdMenuLineOne(1);
    lcdMenuLineTwo(9);
    return false;
  }
  return menuRotary;
}

uint8_t Speichern(uint8_t menuRotary)
{
  if (r.buttonPressedReleased(bounce))
  {
    Serial.println(F("Button pressed Speichern -> zurück ins Hauptmenu"));
    lcdMenuLineOne(1);
    lcdMenuLineTwo(10);
    return false;
  }
  return menuRotary;
}

Erwischt, ich habe mit einem 20x4-Display probiert und die dritte und vierte Zeile zur Fehlersuche verwendet. Das funktionierte für mich schneller als der serielle Monitor.

Mir geht es auch nicht um die Anzahl der Programmzeilen, sondern um die Übersichtlichkeit und einen möglichen Denkanstoß.

Ja, ist schick. Aber ich gebe mehrere Sachen in der ersten Phase aus, da brauch ich dann schon mal 5, 6 Zeilen um zu sehen wo ich hinspringe.

Meine böseste Falle

  if (subMenu)
    switch (menuRotary)
    {

Wenn da Mist baust, kommt irgendwas bei raus. Beim ersten Eintrag mags noch gehen, aber sobald eines von beiden nicht mehr 1 ist, wirds schwierig mit zwei Zeilen Ausgabe :wink:

Guten Abend Männer´s
ich bin mal wieder überwältigt was Ihr für mich macht......DANKE DANKE DANKE
Ihr seit schneller im programmieren als ich im lesen...Unglaublich :smiley:

Ihr bringt mich aber ganz schön in die Zwickmühle.....Ihr beide haut so in die Tasten, ich finde es immer so schade wenn man sich für das andere entscheidet.Ich habe beide heute ausprobiert und bin der Meinung das von Xy Projekt besser nachzuvollziehen zu können.

@agmue
Es tut mir echt leid, das ich schon wieder das von XY Projekt bevorzuge.Ich weiß Deine Mitarbeit und Dein tun sehr zu schätzen. Ich kenne noch eine Stelle wo Du wieder Dein Können mit aller Macht zeigen kannst wenn Du magst. Aber das kommt viel später......

@my_xy_projekt

Da gibt es ein guten Spruch....Wäre es einfach, hätte ich es selber gemacht :thinking: :smiley:
Nun, ich brauche etwas mehr Zeit um es zu verstehen, aber ich denke so mit dem ersten Blick aus Papier, sollte ich es nachvollziehen können.

Da würde gern beim alten bleiben, Grund hierzu ist das verlassen des Menue.So drehe ich eich nach rechts bis sich im Display nichts mehr tut und weiß genau ich bin beim Menuepunkt "Verlassen" :wink:

Da ich schon beim verlassen bin.....wenn ich das Menue verlasse dabei ist es egal ob Hauptmenue oder Turm-Anzeige oder Turm-Licht..... hab ich mächtig Kirmes auf dem LCD
sieht aus wie ein Zufallsgenerator Tausend von wild durcheinander springende Zeichen.
Nach ca. 8 sec. ohne das ich was mache ist der Spuck vorbei und ich bekomme meine Startanzeige wieder.

Schade das man hier kein Video posten kann, sieht echt wirr aus, aber gleichzeitig irgendwie auch wieder gut. :joy:

Morgen werde ich zunächst versuchen den Code zu verstehen und nochmal berichten.
Bis dahin...ein gut Nächtle

Ich habe vor 30 Jahren in einer Schule für Blinde und Sehgeschädigte doziert. :wink:

Hm.
Hab ich nicht. Betreibe das aber auch blank.
Ich such mal mein Mapping raus.
Es könnte sein, das sich da was im Wege steht.

Na denne...

Kommt der Sache sehr nahe :thinking: :wink:

Das merkt man auch im SerMon wenn Du das Hauptmenue verlässt, dauert es eine Zeit bis die nächsten Zeilen kommen.....
switch Menu: 11
Button pressed Hauptmenue verlassen
und genau in dieser Zeit kommt der Zufallsgenerator.

Besten Gruß

Nee, mache Dir keine Sorgen, mir geht es gut.

Das hatte ich heute auch, weil ein Zeiger in einen Speicherbereich zeigte, wo Text hätte stehen sollen, aber nur zufällige Werte standen. Es stimmt, das sieht spaßig aus :joy:

Du machst mich neugierig, sollte ich mich eventuell schon mal Einlesen?

Da bin ich aber froh drüber das Du so entspannt darüber denkst. Ich mags halt ungern einen vor den Kopf zu stossen. Erst recht wenn sich einer soviel arbeit macht.

Irgendwie hat es was von dem Film Matrix..... :joy:

So, nu is aber wieder mal Bettzeit.... :wave:
Gut Nächtle, und passt auf Euch auf

Hier passiert das nicht, aber ich denke ich hab den Übeltäter.

    if (r.buttonPressedReleased(bounce))// EncoderButton ausgelöst?
    {
      lcdMenuLineOne(menuRotary + 1);
      subMenu = menuRotary;
      Serial.print(F("switch Menu: "));
      Serial.println(subMenu);
    }

Ich hab in dem Array einen Wert zu wenig drin, als das es zur Ausgabe reicht.

Quick and Dirty:

    if (r.buttonPressedReleased(bounce))// EncoderButton ausgelöst?
    {
      if (menuRotary != maxRotary)  // sorgt dafür, das die nächste Zeile ggfls. nicht ausgefuehrt wird
      lcdMenuLineOne(menuRotary + 1);
      subMenu = menuRotary;
      Serial.print(F("switch Menu: "));
      Serial.println(subMenu);
    }

Und da ich sowieso noch mit den Zeichenketten baue hier ausführlicher:
Geändert:
Überall:
alt:

    lcdMenuLineOne(1);

neu:

    lcdMenuLineOne(0);

in uint8_ Hauptmenu(uint8_t menuRotary)
alt:

      lcdMenuLineOne(menuRotary + 1);

neu:

      lcdMenuLineOne(menuRotary);

in void lcdMenuLineOne(const uint8_t auswahl)
alt:

  const char *line1[] = {""                 , "*  Hauptmenue  *" , "* Turm-Anzeige *" , "*  Turm-Licht  *" ,

neu:

  const char *line1[] = {"*  Hauptmenue  *" , "* Turm-Anzeige *" , "*  Turm-Licht  *" ,

Na denne - bis heute Abend.

Spart ein wenig Speicher (prinzipiell getestet in #343)

  lcd.print("* ");
  lcd.print(line1[auswahl]);
  lcd.print(" *");
...
void lcdMenuLineTwo(const uint8_t auswahl, const uint8_t min, const uint8_t max)
...
  if (auswahl > min)
  {
    lcd.print("< ");
  } else {
    lcd.print("  ");
  }
  if (auswahl < max)
  {
    lcd.print(" >");
  } else {
    lcd.print("  ");
  }

Auch PROGMEM wäre möglich.

Nur als Anregung :slightly_smiling_face:

Hihi,
Da war ich gestern schon - bin dann aber über mein strlen() gestolpert:

Da musste ich dann gestern auch mal Schluß machen:

const char *lcdHauptMenu[] = {"Hauptmenue", "Turm-Anzeige", "Turm-Licht", "Helligkeit", "RGB-1 Werte", "Fahrspuren",
                              "Startanzeige", "IDNummer", "Funkkanal", "WerksWerte", "Speichern"
                             };
void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  const uint8_t gesamt = 14;
  uint8_t vor = 0;
  uint8_t nach = 0;
  uint8_t x = 0;
  for (uint8_t i = 0; i <= 10; i++)
  {
    vor = 0; nach = 0;
    if ((gesamt - strlen(lcdHauptMenu[i])) > 1)
    {
      vor = (gesamt - strlen(lcdHauptMenu[i])) / 2;
      nach = gesamt - (vor + strlen(lcdHauptMenu[i]));
    }
    //Serial.print(vor); Serial.print(" "); Serial.println(nach);
    Serial.print("<");
    while (vor >= 1)
    {
      Serial.print(" ");
      vor--;
    }
    Serial.print(lcdHauptMenu[i]);
    while (nach >= 1)
    {
      Serial.print(" ");
      nach--;
    }
    Serial.println(">");
  }
}


void loop()
{
}

:slight_smile:

Dann sind wir uns ja einig :slightly_smiling_face:

Seit längerer Zeit schon stört mich die Darstellung beispielsweise von 'g' und 'p', weil im eingebauten Font die untersten Pixel nicht genutzt werden.

Daher habe ich mit dem Custom Character Generator for HD44780 LCD Modules meine eigenen Buchstaben gemalt. Wie man diese Fontdefinitionen speicherschonend in das Display bekommt, hat @Doc_Arduino im Thema Display - Balken - Spielerei - Demos gezeigt.

So könntest Du Menütexte, hübsche Buchstaben und Pfeile verwenden:

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

const byte custom_p[8] PROGMEM = {0b00000, 0b00000, 0b01110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000};
const byte custom_g[8] PROGMEM = {0b00000, 0b00000, 0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b01110};

const char M0[] PROGMEM = {"Anzei\7e"};
const char M1[] PROGMEM = {"\x7F S\6eichern \x7E"};
const char * const menue_table[] PROGMEM = {M0, M1};

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  //lcd.begin(); // Für XY Projekt
  lcd.init(); // Für Benziner und agmue
  byte custom[8] = { };  // Dummyarray
  memcpy_P( custom, custom_p, sizeof custom ); lcd.createChar(6, custom);
  memcpy_P( custom, custom_g, sizeof custom ); lcd.createChar(7, custom);
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd_print(0);
  lcd.setCursor(0, 1);
  lcd_print(1);
}

void loop() {}

void lcd_print(byte index)
{
  char txt[17];
  strcpy_P(txt, (char*)pgm_read_word(&(menue_table[ index ])));
  lcd.print(txt);
}

Die Pfeile habe ich nur der Einfachheit halber in die Menütexte integriert, deren Existenz soll ja berechnet werden.

Möglicherweise möchtest Du was übernehmen, nur zu :slightly_smiling_face:


@combie: "Der C++-Programmierer" verwendet leider String-Objekte, weshalb man mit den von mir wenig geliebten Zeichenketten char[] nicht so richtig vorankommt. Aber die richtige Anregung konnte ich mir gerade dennoch holen. Danke für Deine Kaufanregung!

1 Like

Du sollst das like bekommen - aber bis dahin sind noch ganz weite Wege.
Mach mal daraus nen PROGMEM:

const char *Menu[]={"Hauptmenue", "Turm-Anzeige", "Turm-Licht", "Helligkeit", "RGB-1 Werte", "Fahrspuren", "Startanzeige", "IDNummer", "Funkkanal", "WerksWerte"};

Und nen Beispiel jedes Element einzeln abzugreifen und auszugeben.
Passend zum Code aus #355.
Nach dem, was ich hier habe muss erstmal jedes Element angelegt werden und dann gehts von da aus weiter. Das würde auf eine menuStrings.cpp rauslaufen - Schau Dir die Anzahl der Menus an.
Da kommt noch richtig Musik rein.

Abend Männer / innen, oder wie muss man das heute schreiben..... :joy:

ich habe mich heute mit Deinem Menue Code beschäftigt und einfach mal ein bisschen mit rum experimentiert. Ich denke das ich zurecht komme wenn es darum geht die Menuepunkte zu erweitern oder das zuordnen von einfachen Werten klappt auch soweit.

Das größte Problem wird für mich sein die RGB Farben, 3 Werte in einer Zeile zu ändern.
Aber da werde ich versuchen mich an den Code der Uhrstellen aus dem Converter anzulehnen.
Na schauen wir mal ob klappt :roll_eyes:

Aber sagt mal, was habt Ihr da vor....? Ihr macht mir Angst :thinking:
Soviel Input in kurzer Zeit vertragen doch meine grauen Zellen nicht :joy:

Gibt es da auch Männer / außen?
Weil in der deutschen Sprache ist das Gegenteil von innen doch außen oder?

Gruß Tommy

:slight_smile:
Den hab ich schon angesetzt.

DAS wollte ich lesen.
Da ist ganz bestimmt noch Optimierungspotential, aber ich hab das absichtlich etwas ausführlicher gemacht. Hat sich gelohnt.

Ich weiss auch noch nicht, was ich vor habe....
Nur fertig werden mit den Vorgaben.
Und dieser wunderbaren Idee treu bleiben.

Ich bestimme Hochzeit auf Mittwoch nächste Woche.
Wenn bis dahin nicht geschafft, wird es eng.

Na, wenn das mal nicht Männer Feindlich klingt....
Innen schön warm und trocken, außen kühl und nass.

Ach, xy Projekt sprachlos ich bin...

Wenn ich eins bei dem Projekt gelernt habe, Dir zu zuhören. Bist der Weltbeste Lehrer für Anfänger, hab so einiges von Dir gelernt. Vielen Dank dafür.

Der Weg ist das Ziel, und das Ziel setzen wir uns selbst.

Du bist die treibende Kraft, Du bestimmst die Vorgehensweise.
........