Lcd Menü Name in variable

Hallo zusammen,

Ich brauche mal eure Hilfe, ich versuche gerade mir ein lcd Menue (16x2) mit zwei Einträgen (Name und Ort) zu erstellen. Das Menü soll dazu dienen zwei variablen zu befüllen die später im Programm verwendet werden (es soll ein Ausdruck über einen Thermodrucker erfolgen) sollen. Idealerweise mit einem rotary encoder mit klick Funktionalität... Zur Not reichen mir aber auch die Buttons des shields und die analog Auswertung. :stuck_out_tongue:

Ich lerne zur Zeit Schritt für Schritt mit dem Arduino umzugehen habe auch schon versucht mit Arrays zu arbeiten doch so bekomme ich immer nur einzelne Buchstaben und das ganze nicht in einen string. Jetzt hab ich schon gelesen das ich mit Zeigern und Funktionen arbeiten könnte, leider fehlen mir hier jedoch die Kenntnisse...

Kann mir jemand ein paar Tipps geben wie ich am besten vorgehen kann?

Ich habe schon so viele unterschiedliche Versionen versucht das hier scheint mir zumindest vom Ansatz die vielversprechendste zu sein... Doch wie gesagt... Ich komme nicht mehr weiter....

#define WITH_LCD 1

#include <ClickEncoder.h>
#include <TimerOne.h>

#ifdef WITH_LCD
#include <LiquidCrystal.h>

int i = 0;
int menue = 0;
/*
 The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
*/
#define LCD_CHARS   20
#define LCD_LINES    4

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

#endif

ClickEncoder *encoder;
int16_t last, value;

void timerIsr() {
  encoder->service();
}

void setup() {
  Serial.begin(9600);
  encoder = new ClickEncoder(A9, A8, A10);

#ifdef WITH_LCD
  lcd.begin(LCD_CHARS, LCD_LINES);
  lcd.clear();
  
#endif

  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr); 
  
  last = -1;
}

void loop() {  
  
  char Zeichen [108] = { 'A','A','A','A','B','B','B','B','C','C','C','C','D','D','D','D','E','E','E','E','F','F','F','F','G','G','G','G','H','H','H','H','I','I','I','I','J','J','J','J','K','K','K','K','L','L','L','L','M','M','M','M','N','N','N','N','O','O','O','O','P','P','P','P','Q','Q','Q','Q','R','R','R','R','S','S','S','S','T','T','T','T','U','U','U','U','V','V','V','V','W','W','W','W','X','X','X','X','Y','Y','Y','Y','Z','Z','Z','Z','_','_','_','_'};
  char Name[16] = {'""','""','""','""','""','""','""','""','""','""','""','""','""','""','""','""'};
  
  
  value += encoder->getValue();
 
 /* 
  if (value != last) {
    last = value;
    Serial.print("Encoder Value: ");
    Serial.println(value);
#ifdef WITH_LCD
    lcd.setCursor(0, 0);
    lcd.print("         ");
    lcd.setCursor(0, 0);
    lcd.print(value);
#endif
  }
  */
  
   if (value >= 0 && value != last && value <= 107) // noch testen
  {
    
    /*
    if (menue <= 10)
    {
      
      if (value != last)
          {   
                       
          last = value; 
          //#ifdef WITH_LCD
          
          if (menue <=5)
          {
            lcd.setCursor(1, 0);
          lcd.print("Ort");
          
          lcd.setCursor(10, 0);
          lcd.print(value);
          }
          if (menue <= 10 && menue >=5)
          {
            lcd.setCursor(1, 0);
          lcd.print("Name");
          lcd.setCursor(10, 0);
          lcd.print(value);
          }
          
          }
          menue++;
    }
    
    */
/// Name eingeben    
    if (i<=16)
    {
          if (value != last)
          {   
          Serial.print("Wert ");
          Serial.println(Zeichen[int ((value))]);             
          last = value; 
          //#ifdef WITH_LCD
          
          lcd.setCursor(i, 0);
          lcd.print("         ");
          lcd.setCursor(i, 0);
          lcd.print(Zeichen[int ((value))]);      
          Name[i] = Zeichen[int ((value))];
         // String Name =  String(Name[1]+ String(Name[2]) );
         // lcd.setCursor(i, 1);
          //lcd.print (Name);
          }
     }
        
  }  
  
        ClickEncoder::Button b = encoder->getButton();
        if (b != ClickEncoder::Open) 
        {
          i++;                                                                            //Bei klick stelle weiter
          lcd.setCursor(9, 0);                                                            
          lcd.print(i);                                                                    
          Serial.print(i);
           
          Serial.print("Button: ");
          #define VERBOSECASE(label) case label: Serial.println(#label); break;
            switch (b) 
            {
            //VERBOSECASE(ClickEncoder::Pressed);
            case ClickEncoder::Pressed:
            //VERBOSECASE(ClickEncoder::Held)
            //VERBOSECASE(ClickEncoder::Released)
            //VERBOSECASE(ClickEncoder::Clicked) 
            //case ClickEncoder::Clicked:     
            case ClickEncoder::DoubleClicked:                                                  //Doppelklick stelle zurück
                i=i-2;
                Serial.println("i-1");
              break;
            }
        
                if(i > 20)
                {  
              //  lcd.setCursor(0, 1);
               // lcd.print("Enter");
                
                } 
      }  
  
}

Gruß Jens
Von Arduino gesendet

Wenn Du den Text in einem Array hast warum gibst Du den nicht Element für Element aus? Eine For Schleife macht das am einfachsten.
Grüße Uwe

Die Syntax für das Name Array ist aber totaler Murks.

Willst du mit dem Encoder zwischen verschiedenen Strings wählen? Dann Lege die Menü-Namen in ein Array aus C-Strings, Zähle mit dem Encoder einen Index von 0 bis n durch und greife über den Index auf das Array zu.

Damit ich das ganze hier auch etwas übersichtlicher gestaltet bekomme jetzt nochmal das konkrete Ziel etwas strukturierter. 8)

  1. Arduino einschalten, Menü Aufrufen
  2. Ort eingeben (Einmalig bei jeden Start)
  3. Name eingeben (bei jeder Messung)
  4. Eigentlicher Programmablauf (Es wird eine Messung durchgeführt)
  5. Ausgabe des Messergebnisses + Name und Ort
char* Menue[] = {"Ort","Name"}

Die Menüstruktur hatte ich bereits mal wie folgt versucht einzubinden

if (menue <= 10)
    {
      
      if (value != last)
          {   
                       
          last = value; 
          
          if (menue <=5)
          {
            lcd.setCursor(1, 0);
          lcd.print("Ort");
          
          lcd.setCursor(10, 0);
          lcd.print(value);
          }
          if (menue <= 10 && menue >=5)
          {
            lcd.setCursor(1, 0);
          lcd.print("Name");
          lcd.setCursor(10, 0);
          lcd.print(value);
          }

Anschließend habe ich über den Value des Rotary Encoders die Auswahl vorgenommen (der Bereich von jeweils 5 Pulsen habe ich deshalb vorgenommen weil sonst das Menü nur mit viel Fingerspitzengefühl einstellbar wäre.

Hier kann der Nutzer dann durch die Zeichen des Arrays „Zeichen“ scrollen.

if (i<=16)
    {
          if (value != last)
          {   
          Serial.println(Zeichen[int ((value))]);             
          last = value; 
                    
          lcd.setCursor(i, 0);
          lcd.print("         ");
          lcd.setCursor(i, 0);
          lcd.print(Zeichen[int ((value))]);      
          Name[i] = Zeichen[int ((value))];
         
          }
     }

Durch betätigen des Rotary Encoder Buttons wird i um eins erhöht und die Eingabestelle des Namens um 1 erhöht
Durch einen Doppelklick um eine Stelle zurück gesetzt also i=i-2

Durch gedrückt halten wollte ich dann die Eingabe bestätigen.

ClickEncoder::Button b = encoder->getButton();
        if (b != ClickEncoder::Open) 
        {
          i++;                                                                            //Bei klick stelle weiter
          lcd.setCursor(9, 0);                                                            
          lcd.print(i);                                                                    
          Serial.print(i);
           
          Serial.print("Button: ");
          #define VERBOSECASE(label) case label: Serial.println(#label); break;
                switch (b) 
                {
                //VERBOSECASE(ClickEncoder::Pressed);
                case ClickEncoder::Pressed:
                //VERBOSECASE(ClickEncoder::Held)
                //VERBOSECASE(ClickEncoder::Released)
                //VERBOSECASE(ClickEncoder::Clicked) 
                //case ClickEncoder::Clicked:     
                case ClickEncoder::DoubleClicked:                              //Doppelklick stelle zurück
                i=i-2;
                Serial.println("i-1");
                break;
                }
            
                    if(i > 20)
                    {  
                    //  lcd.setCursor(0, 1);
                     // lcd.print("Enter");
                    
                    } 
          }

So Viel zu meinen Überlegungen… Leider bin ich im Umgang mit Strings noch ziemlich unerfahren im Bezug auf Pointer und Zeiger gänzlich unerfahren…. :blush:

Mein Problem war bei diesem Ansatz der Sprung aus den einzelnen Menüebenen in diesem Fall ....

  1. Ort ,
  2. Name ,

....in die jeweilige Eingabemaske und die Übergabe des eingegebenen Namen an eine Variable oder Array um diesen später abzurufen. :disappointed_relieved:

Gruß Jens

Ich werde im Laufe des Tages mal etwas Test-Code zu dem String Teil schreiben wie man das grob machen könnte.

Serenifly:
Ich werde im Laufe des Tages mal etwas Test-Code zu dem String Teil schreiben wie man das grob machen könnte.

Super vielen Dank schon mal :slight_smile:

Eine Möglichkeit wie man das machen kann:

Der folgende Code läuft rein über Serial. Steuerung erfolgt über ASWD + F

A = Cursor Links
D = Cursor Rechts
W = nächster Buchstabe
S = vorheriger Buchstabe
F = Bestätigen

Die Punkte bei der Ausgabe sind nicht im String! Das ist nur zur Verdeutlichung. Die werden per Hand bei der Ausgabe eingefügt. Tatsächlich steht da NULL drin. Das heißt auch, dass der String mit einem Zeichen beginnen muss und beim ersten Punkt/NULL aufhört, egal was dahinter steht. Wenn du Leerstellen willst, musst du in den Zeichensatz ein Leerzeichen einfügen.

Das charSet Array kannst du wie du möchtest anlegen. Das wird dann einfach von 0 bis Ende durchlaufen wenn man den Buchstaben auswählt.

Das ist etwas rudimentär und deckt vielleicht nicht alle gewünschsten Use Cases ab, und es können auch noch Bugs drin sein was die Abfolge angeht, aber es gibt ja um das Prinzip wie man einen String aus einzelnen Zeichen zusammensetzt. Zusätzliche Logik kann man immer noch einbauen.

const char charSet[] PROGMEM = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";   //Zeichensatz. Kann angepasst werden
const char EMPTY = '.';	  //nur für die Debug-Ausgabe auf Serial
const int CHARSET_LENGTH = sizeof(charSet) - 1;	  //Länge des Zeichensatzes
const int MAX_LENGTH = 10;  //Maximale Länge des Strings (ohne Null-Terminator)

char name[MAX_LENGTH + 1];  //+1 für Null-Terminator

int currentIndex;    //aktueller Index im Array
int currentLetter;   //aktueller Buchstabe
char* currentArray = name;	//aktueller String

void setup()
{
  Serial.begin(9600);
  delay(1000);

  printInputField();
}

void loop()
{
  readSerial();
}

void readSerial()
{
  if (Serial.available())
  {
    char c = Serial.read();

    switch (c)
    {
      case 'a':	  //Index dekrementieren (nach links)
      case 'A':
        decrement(currentIndex, MAX_LENGTH);
        printInputField();
        currentLetter = 0;
        break;
      case 'd':	  //Index inkrementieren (nach rechts)
      case 'D':
        increment(currentIndex, MAX_LENGTH);
        printInputField();
        currentLetter = 0;
        break;
      case 'w':	  //nächster Buchstabe
      case 'W':
        currentArray[currentIndex] = pgm_read_byte(&charSet[currentLetter]);	//Buchstabe aus Flash/PROGMEM auslesen
        increment(currentLetter, CHARSET_LENGTH);
        printInputField();
        break;
      case 's':	  //vorheriger Buchstabe
      case 'S':
      	currentArray[currentIndex] = pgm_read_byte(&charSet[currentLetter]);	//Buchstabe aus Flash/PROGMEM auslesen
        decrement(currentLetter, CHARSET_LENGTH);
        printInputField();
        break;
      case 'f':	  //Bestäigung
      case 'F':
        Serial.print(F("Ausgabe: "));
        Serial.println(currentArray);
        currentIndex = 0;		    //Indices zurücksetzen
        currentLetter = 0;
        memset(currentArray, 0, MAX_LENGTH + 1);	//Array zurücksetzen. Das natürlich nicht hier machen wenn man das Array nach der Eingabe noch benötigt!!
        printInputField();
      default:
        ;
    }
  }
}

void increment(int& index, int max)
{
  index = (index + 1) % max;
}

void decrement(int& index, int max)
{
  index--;
  if (index < 0)
    index = max - 1;
}

void printInputField()
{
  for (int i = 0; i < MAX_LENGTH; i++)
  {
    if (currentArray[i] == 0)	//leere Stellen mit Zeichen darstellen
      Serial.print(EMPTY);
    else
      Serial.print(currentArray[i]);
  }
  Serial.println();

  for (int i = 0; i < MAX_LENGTH; i++)	//Cursor ausgeben
  {
    if (i == currentIndex)
      Serial.print('^');
    else
      Serial.print(' ');
  }
  Serial.println();
}

Der Buchstaben-Index wird beim links/rechts scrollen wieder auf 0 gesetzt. Ob man das möchte ist Geschmackssache. Man könnte ihn auch immer auf dem aktuellen Wert lassen.

Eine andere Stelle wo das vielleicht negativ auffällt ist, dass wenn man zurück geht und den Buchstaben ändern will es nicht bei diesem Buchstaben anfängt, sondern wieder von vorne. Da könnte man aber mit strchr_P() (und einer anschließenden Subtraktion der Zeiger) den Index des Buchstaben im charSet Array suchen und den Buchstaben Index darauf setzen. So:

int getLetterIndex(char letter)
{
  if (letter != '\0')
  {
    const char* ptr = strchr_P(charSet, letter);
    if (ptr != NULL)
      return charSet - ptr;
  }
  return 0;
}

Und dann statt:

currentLetter = 0;

das:

currentLetter = getLetterIndex(currentArray[currentIndex]);

Dann ist aber immer noch ein Schönheitsfehler drin, weil er dann beim ersten Schritt den gleichen Buchstaben nochmal macht erst danach inkrementiert... :s

Der Zugriff auf das Array in dem man das speichert erfolgt über char* currentArray. Das nur ein Zeiger auf einen anderen String. Hier name. Nicht extra Speicher für ein eigenes Array. Du kannst da auch sowas machen:

char name[MAX_LENGTH + 1];
char ort[MAX_LENGTH + 1];
char* currentArray = name;

...

currentArray = ort;

Dann zeigt currentArray auf das andere Array und du kannst das bearbeiten

Ich setzte im Test-Code nach der Ausgabe das Array wieder auf 0:

memset(currentArray, 0, MAX_LENGTH + 1);

Das willst du in deinem richtigen Code nicht, da du den Namen ja nach Ende der Eingabe behalten willst! Ich geben ihn da nur einmal aus.

Das würdest du eher jedes mal machen wenn du mit der Eingabe beginnst. Ich mache das lediglich am Ende weil ich kein extra Kommando für den Beginn der Eingabe implementiert habe.

Dadurch dass man das gesamte Array auf 0 setzt ist übrigens nicht nötig den String explizit zu terminieren.

Ich hab mir den Code jetzt mal etwas genauer anschauen können, und hab versucht den Ablauf mal zu visualisieren...siehe Anhang. Leider ohne ein vernünftiges Tool für zur Erstellung von Abalaufdiagrammen, aber ich hoffe es ist trotzdem verständlich.

Kannst du mir sagen was hier passiert?

void increment(int& index, int max)

das int& index ist mir leider nicht bekannt...

Die Erweiterung um die Funktionalität

int getLetterIndex(char letter)
{
  if (letter != '\0')
  {
    const char* ptr = strchr_P(charSet, letter);
    if (ptr != NULL)
      return charSet - ptr;
  }
  return 0;
}

müsste ich also jeweils beim dekrementieren als auch beim inkrementieren machen oder?
In diesem Fall sollte ich dann doch auch immer das bereits eingegebene Array anpassen können richtig?

die Abfrage der cases ist doch nur so aufgebaut damit ich sowohl ein klein als auch einen Großbuchstaben über den Serialinput versenden kann oder`?

case 'a':	  //Index dekrementieren (nach links)
      case 'A':

Ich habe mir jetzt überlegt im Setup eine Abfrage für die Eingabe des Namen und des Ortes zu erstellen , kann ich jetzt einfach vor Ausgabe der Funktion „printInputField()“

den Wert für „char* currentArray“ mittels Index und Arrayabfrage vorgeben? Etwa so? :wink:

#define ARRAYSIZE 10
  #define MaxMenueEinträge 2
  
  String Menue[ARRAYSIZE] = { "Name","Ort" };

  int i=value
  if (i<MaxMenueEinträge)
  {
  char* currentArray = Menue[i]; 
  printInputField();

Ich geb mir mühe das ganze zu verstehen und zukünftig selbst umzusetzen also verzeiht mir bitte die Menge an rückfragen, auch wenn es sich Teilweise vielleicht um Grundwissen handelt :blush: :roll_eyes:

Tabelle.doc (16.9 KB)

Anscheinend nicht... :angry:

char* currentArray = Menue[i];

sketch_feb24a.ino: In function 'void menue()':
sketch_feb24a:108: error: cannot convert 'String' to 'char*' in initialization

das int& index ist mir leider nicht bekannt.

Das ist eine Referenz

Man will ja die Variable verändern und nicht nur die Kopie. Also muss man die Variable call-by-reference statt call-by-value Übergeben. Daher übergibt man mit int& die Adresse der Variable und nicht den Wert. Das ist etwas ähnlich wie Zeiger, aber man kann mit Referenzen weniger machen als mit Zeigern. Dafür ist die Syntax einfacher

die Abfrage der cases ist doch nur so aufgebaut damit ich sowohl ein klein als auch einen Großbuchstaben über den Serialinput versenden kann oder`?

Ja

müsste ich also jeweils beim dekrementieren als auch beim inkrementieren machen oder?

Das ist etwas komplizierter, da man das ja nur einmal machen darf wenn man auf einen existierenden Buchstaben springt. Danach muss es normal weitergehen. Da ist wahrscheinlich etwas extra Logik nötig.

Versuch erst mal die Grundlagen hinzubekommen, vor allem da du etwas Probleme mit C hast. Danach kann man es immer noch schöner machen.

cannot convert 'String' to 'char*' in initialization

Was machst du da mit String Objekten? Hier werden nur Null-terminierte char Arrays (d.h. C Strings) verwendet!! String Objekte sind nicht mit C Strings kompatibel!

Und auch bei C Strings kannst du einem char* natürlich keinen char Zuweisen.

Arrays aus Strings gehen so: Da C Strings Arrays sind, sind Arrays aus C Strings einfach mehrdimensionale Arrays oder Arrays aus Zeigern.

Mit mehrdimensionalen Arrays geht das so:

char menu[2][MAX_LENGTH+1];

Das legt ein zwei Arrays der Länge MAX_LENGTH+1 hintereinander an. Generell musst du bei C Strings den Speicher vor der Verwendung anlegen. Du kannst nicht ein Array der Größe 4 anlegen und danach mehr reinschreiben. Oder besser du darfst es nicht. Können kann man es schon, aber dann funktioniert das Programm nicht mehr richtig.

Dann kannst du das machen:

currentArray = menu[0];

Da menu[0] zu einem Zeiger auf char (d.h. ein char*) zerfällt

Merke: In C sind sind Arrays Zeiger auf das erste Element. Wenn man also ein Array hat:
char array[5]
ist array als Variable ein char*. Und der zeigt auf den ersten Index, d.h. auf &array[0]. Die Adresse des ersten Elements (& liest man hier ähnlich wie wie bei der Referenz als "Adresse von")

Und passe mit ARRAYSIZE auf! Bei mir bezieht sich MAX_LENGTH auf die Anzahl der Buchstaben. Das Array muss aber eins größer als die Anzahl der Buchstaben sein, da am Ende noch Platz für den Null-Terminator sein muss, wenn es tatsächlich voll ist! Deshalb das +1

das int& index ist mir leider nicht bekannt...

Das ist eine sogenannte Referenz.
An die Funktion wird nicht der Wert übergeben, sondern eine Referenz auf die Variable.
In der Funktion wird die Variable "normal" verwendet (nicht als Zeiger), es ist aber keine Kopie, sondern die Original-Variable. Änderungen an der Variable in der Funktion wirken sich also auch aussen aus.

Ob das in dieser Art hier eine gute Konstruktion ist, oder ob Serenifly zeigen möchte was er kann, lass ich mal unkommentiert :wink:

Verwendet wird es jedenfalls sehr einfach:

increment ( i , 100 ); // erhöhe i um eins bis max 100

Ich muss den Wert ja irgendwie by-reference übergeben, da ich ihn ändern will. Also hat man entweder Referenzen oder Zeiger. Referenzen werden bei sowas gerne verwendet, da Zeiger etwas komplizierter zu handhaben sind. Der Vorteil von Zeigern wäre höchstens dass man bei der Parameter-Übergabe sieht dass man eine Referenz übergibt.

Aber das ist so keinesfalls ungewöhnlich.

Historisch kamen Referenzen erst lange nach Zeigern mit c++, sind also schon "ungewöhnlicher"
Ich stimme dir zu, dass es eine gute Verwendung von Referenzen ist.
Mit Zeigern wäre evtl. klarer, was passiert.
Ob leichter oder schwerer lesbar, ist Geschmacks- (bzw. Gewöhnungs-)sache.

Man hätte aber auch

int increment(int i, int max);  // returns min(i+1, max);

deklarieren können ( sogar, wie min() , als Makro ).

Puhhhh, das muss ich erstmal auf mich wirken lassen :smiley:

Ne quatsch ich glaube im Ansatz verstehe ich das schon ich werde mal etwas üben .....

Da es nur zwei Menüeinträge gibt hatte ich das ganz einfach mal so gemacht...

const int Menue_Lenght = 10;
const int MaxMenueEintraege = 2;

String Menue[Menue_Lenght+1] = { "name","ort" };

Ich habe jetzt für den Null-Terminator die länge entsprechend erhöht, nur wie setzte ich diesen bzw. muss ich das überhaupt wenn ich die Menüstruktur bereits festgelegt habe?

case 'n':	  //vorheriger Buchstabe
      case 'N':
      	value = 1;
        break;

...das wirkt sich allerdings noch nicht auf die Auswahl des Menüpunktes aus, ich bin noch am "basteln" um den Fehler in meiner "Logik" zu finden... :stuck_out_tongue:

void menue()
{
  readSerial();
  
  int i=value;
  if (i==1)
  {
  currentArray = name; 
  printInputField();
  Serial.print("Menue - Name");
}
else
{
  currentArray = ort; 
  printInputField();
  Serial.print("Menue - Ort");
}
}

Zeiger sind also "Verlinkungen" bei denen durch eine Änderung das Original unverändert bleibt und Referenzen "Quellen" bei denen eine Anderung sich auch auf das Original auswirkt. Habe ich das so richtig verstanden? 8) :grinning:

String Menue[Menue_Lenght+1] = { "name","ort" };

Soll das einen Bezeichnung für das sein was du eingibst, oder Speicher für das was du eingibst?

So oder so ist ein Array aus String Objekten! Das ist was ganz anderes und völlig unnötig.

Wenn du ein Array aus Bezeichnern willst die sich nicht mehr ändern sollen:

const char* menu[] = { "name", "ort" };

Das ist ein Array aus Zeigern auf C Strings. menu ist ein char** (Zeiger auf Zeiger)

Es existiert aber auch nur Speicher für "name" (5 Bytes) und "ort" (4 Bytes)! Mehr als das kann man da nicht reinschreiben

Was du hier aber glaube ich eher willst ist Speicherplatz für deine Eingabe. Dazu musst du den Speicher wirklich komplett reservieren und brauchst zwei-dimensionale Arrays:

char menu[2][MAX_LENGTH+1];

Legt ein 2 * MAX_LENGTH+1 Array aus char an. Das Array ist mit 0 initialisiert wenn es global ist.

Zeiger sind also "Verlinkungen" bei denen durch eine Änderung das Original unverändert bleibt und Referenzen "Quellen" bei denen eine Anderung sich auch auf das Original auswirkt. Habe ich das so richtig verstanden?

Nicht ganz. Du kannst dir das beides als Verlinkungen vorstellen. Ein Zeiger ist eine Variable in der die Adresse einer anderen Variablen steht.

Referenzen sind eine Art abgespeckte Zeiger. Man kann damit Variablen ansprechen. Das reicht hier. Aber mit Zeiger kann man mehr machen. Man kann Zeigern anderen Zeigern zuweisen. Oder mit ihnen Rechnen (Addition, Subtraktion, Vergleiche).

String Menue[Menue_Lenght+1] = { "name","ort" };

Das soll eigentlich dazu dienen über einen Index den Zeiger auf das current array und somit das jeweils gültige Menue zu wählen....

Was ich mir dabei gedacht habe ist das ich in der Funktion Menue die jeweilige Variable aus dem Array aufrufen will um den Speicherort zu bestimmen.

void menue()
{
  readSerial();
  
  int i=value;
  if (i==1)
  {
  currentArray = name; 
  printInputField();
  Serial.print("Menue - Name");
}
else
{
  currentArray = ort; 
  printInputField();
  Serial.print("Menue - Ort");
}

hier mal mit direktem Namenszuweisungen....

leider klappt das aber auch nicht so wie ich mir das vorstelle....wenn ich jetzt den Index wert also Value ändere dann kann ich zwar zwischen den Speicherorten wechseln doch wie bekomme ich das mit einer Eingabe hin ? :frowning:

hier mal der angepasste Code damit ich nicht immer nur schnipsel liefere...

#include <LiquidCrystal.h>

const char charSet[] PROGMEM = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";   //Zeichensatz. Kann angepasst werden
const char EMPTY = '.';	  //nur für die Debug-Ausgabe auf Serial
const int CHARSET_LENGTH = sizeof(charSet) - 1;	  //Länge des Zeichensatzes
const int MAX_LENGHT = 10;  //Maximale Länge des Strings (ohne Null-Terminator)

char name[MAX_LENGHT + 1];  //+1 für Null-Terminator
char ort[MAX_LENGHT + 1];  //+1 für Null-Terminator

int currentIndex;    //aktueller Index im Array
int maxIndex;	     //maximaler Index. Nötig wenn man nach einer Eingabe zurück geht um was zu ändern
int currentLetter;   //aktueller Buchstabe
char* currentArray = name;	//aktueller String

const int ARRAYSIZE = 10;
const int MaxMenueEintraege = 2;

String Menue[ARRAYSIZE] = { "name","ort" };

int value = 1;

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);


void setup()
{
  Serial.begin(9600);
  delay(1000);

 // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  
 
menue();

}
void loop()
{
  
  readSerial();
  
}

void readSerial()
{
  if (Serial.available())
  {
    char c = Serial.read();

    switch (c)
    {
      case 'a':	  //Index dekrementieren (nach links)
      case 'A':
        decrement(currentIndex, MAX_LENGHT);
        printInputField();
        currentLetter = 0;
        break;
      case 'd':	  //Index inkrementieren (nach rechts)
      case 'D':
        increment(currentIndex, MAX_LENGHT);
        printInputField();
        currentLetter = 0;
        break;
      case 'w':	  //nächster Buchstabe
      case 'W':
        currentArray[currentIndex] = pgm_read_byte(&charSet[currentLetter]);	//Buchstabe aus Flash/PROGMEM auslesen
        increment(currentLetter, CHARSET_LENGTH);
        if (currentIndex > maxIndex)
          maxIndex = currentIndex;
        printInputField();
        break;
      case 's':	  //vorheriger Buchstabe
      case 'S':
      	currentArray[currentIndex] = pgm_read_byte(&charSet[currentLetter]);	//Buchstabe aus Flash/PROGMEM auslesen
        decrement(currentLetter, CHARSET_LENGTH);
        printInputField();
        break;
      case 'n':	  //vorheriger Buchstabe
      case 'N':
      	value = 1;
        break; 
      case 'f':	  //Bestäigung
      case 'F':
        currentArray[maxIndex + 1] = '\0';  //String terminieren
        Serial.print(F("Ausgabe: "));
        Serial.println(currentArray);
        lcd.setCursor(1, 0);
        lcd.print ("Ausgabe:");
        lcd.setCursor(9, 0);
        lcd.print (currentArray);
        currentIndex = 0;		    //Indices zurücksetzen
        currentLetter = 0;
        memset(currentArray, 0, MAX_LENGHT);	//Array zurücksetzen. Das natürlich nicht machen wenn man das Array nach der Eingabe noch benötigt!!
        printInputField();
      default:
        ;
    }
  }
}

void increment(int& index, int max)
{
  index = (index + 1) % max;
}

void decrement(int& index, int max)
{
  index--;
  if (index < 0)
    index = max - 1;
}

void printInputField()
{
  for (int i = 0; i < MAX_LENGHT; i++)
  {
    if (currentArray[i] == 0)	//leere Stellen mit Zeichen darstellen
      {Serial.print(EMPTY);
      lcd.setCursor(0, i);
      lcd.print (EMPTY);}
    else
      {Serial.print(currentArray[i]);
      //lcd.setCursor(i, 0);
      lcd.print (currentArray[i]);}
  }
  Serial.println();

  for (int i = 0; i < MAX_LENGHT; i++)	//Cursor ausgeben
  {
    if (i == currentIndex)
      Serial.print('^');
    else
      Serial.print(' ');
  }
  Serial.println();
}


void menue()
{
  readSerial();
  
  int i=value;
  if (i==1)
  {
  currentArray = name; 
  printInputField();
  Serial.print("Menue - Name");
}
else
{
  currentArray = ort; 
  printInputField();
  Serial.print("Menue - Ort");
}
}

tweak87:
Was ich mir dabei gedacht habe ist das ich in der Funktion Menue die jeweilige Variable aus dem Array aufrufen will um den Speicherort zu bestimmen.

Wie gesagt, zwei-dimensionales Array. Nur damit hast du wirklich Speicher für den gesamten String.

Man kann natürlich auch sowas machen:

char name[MAX_LENGHT + 1];
char ort[MAX_LENGHT + 1];

void selectMenu(int menu)
{
   if(menu == 0)
     currentArray = name;
   else if(menu == 1)
     currentArray = ort;
}

Aber wenn du ein zwei-dimensionales Array hast kannst du direkt über Indices darauf zugreifen:

char menu[2][MAX_LENGTH+1];

currentArray = menu[0];

...

currentArray = menu[1];

Nochwas:
Das mit maxIndex braucht man nicht. Da war ich auf einem Irrweg. Und auch nur weil ich auf die Idee gekommen will, dass man das Array per Hand terminieren müsste. Was nicht stimmt, da das Array ja schon vorher alles NULL ist (und in der Version nachher zurückgesetzt wird). Ich habe das danach geändert.

Schau dir noch mal die aktuelle Version an. Vor allem den Teil unter 'F'

Also die Zeile weglassen:
currentArray[maxIndex + 1] = '\0'; //String terminieren

Und dann alles mit maxIndex löschen

Die beiden Versionen sind funktionell identisch, aber es ist eben überflüssiger Code :slight_smile:

Und hier:
memset(currentArray, 0, MAX_LENGHT);

Das sollte sicherheitshalber MAX_LENGTH + 1 heißen

Wegen dem LCD noch. Wenn du mal in die Library schaust, sieht du diese Funktionen:

  void noBlink();
  void blink();
  void noCursor();
  void cursor();

Damit kannst du an der aktuellen Position einen Unterstrich anzeigen und diesen blinken lassen. Somit kann man bequem die aktuelle Position darstellen

 char menu[2][MAX_LENGTH+1];

currentArray = menu[0];

...

currentArray = menu[1];

Also hier steht ja dann im Array mit der Adresse menu[0] und danach das das serialread und zum erfassen der Buchstaben sowie die Funktion für die Ausgabe richtig?

Das gleiche dann doch auch nochmal nachdem ich dem current Array den Wert von menu[1] zugewiesen haben...

Habs jetzt nur auf dem Smartphone anschauen können und leider noch keinen Test durchgeführt...

Gruß Jens

currentArray zeigt immer auf das Teil-Array das man beschrieben will (entweder menu[0] oder menu[1]). Irgendwann wechselst du dann und der andere Speicher-Bereich wird beschrieben

Man kann das auch anders machen. Das ist nicht die einzige Lösung. Es ist natürlich auch möglich direkt über menu[index1][index2] auf einen Buchstaben zuzugreifen.

Der Vorteil da einen Zeiger wie currentArray zu verwenden liegt darin, dass Einlesen und Speichern getrennt sind. Man kann die Art wie man es speichert ändern (zwei Arrays oder ein mehr-dimensionales Array), aber man muss im Code nicht so viel ändern, da der Zugriff bei jeder Variante gleich ist.

Das mit dem Array war für mich nicht ganz verständlich, vielleicht bin ich auch einfach zu.....
Ich habe es jetzt erstmal mit der Zuweisung gemacht, das weglassen von Maxindex hat bei mir zu Fehlern geführt deshalb bin ich da jetzt erstmal nicht dran gewesen.

ich kann nur noch nicht nach der Eingabe zurück ins Menue springen. ich denke das versuche ich später mal indem Ich nach der Bestätigung über 'f' irgendwie versuche zurück zu springen.

Die Funktion blink und cursor, wie in der LCD Libary beschrieben hab ich eingebaut bekommen, durch das delay jedoch keine vernünftige Eingabe mehr durchführen können. Ich habs jetzt mal mit einer Verzögerung über die Funktion millis() versucht... doch leider ohne Ergebnis.

Ich lasse hierbei an der aktuellen cursor Position nach 100 millisec void blink() ausgeben und nach weiteren 100 millisec void noBlink()...

Ich bin auf dem richtigen Weg hoffe ich, und durch dieses Programm hab ich jetzt mittlerweile vermutlich schon deutlich mehr gelernt als in dem letzten halben Jahr in der Schule :grinning:

Allein weil ich jetzt das Thema Funktionen und deren Verwendung glaube ich deutlich besser verstehe als vorher... :smiling_imp:

#include <LiquidCrystal.h>

const char charSet[] PROGMEM = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";   //Zeichensatz. Kann angepasst werden
const char EMPTY = '.';	  //nur für die Debug-Ausgabe auf Serial
const int CHARSET_LENGTH = sizeof(charSet) - 1;	  //Länge des Zeichensatzes
const int MAX_LENGHT = 10;  //Maximale Länge des Strings (ohne Null-Terminator)

char name[MAX_LENGHT + 1];  //+1 für Null-Terminator
char ort[MAX_LENGHT + 1];  //+1 für Null-Terminator

int currentIndex;    //aktueller Index im Array
int maxIndex;	     //maximaler Index. Nötig wenn man nach einer Eingabe zurück geht um was zu ändern
int currentLetter;   //aktueller Buchstabe
char* currentArray = name;	//aktueller String

long interval = 100;           // interval für LCD Blink oder Cursor
long previousMillis = 0;        // letzer wert für Millis


int value = 1;
int menu = 1;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);


void setup()
{
  Serial.begin(9600);
  delay(1000);

  
      
 // set up the LCD's number of columns and rows: 
  
selectMenu (value) ;

}
void loop()
{
  
  readSerial();
  
}

void readSerial()
{
  if (Serial.available())
  {
    char c = Serial.read();

    switch (c)
    {
      case 'a':	  //Index dekrementieren (nach links)
      case 'A':
        decrement(currentIndex, MAX_LENGHT);
        printInputField();
        currentLetter = 0;
        break;
      case 'd':	  //Index inkrementieren (nach rechts)
      case 'D':
        increment(currentIndex, MAX_LENGHT);
        printInputField();
        currentLetter = 0;
        break;
      case 'w':	  //nächster Buchstabe
      case 'W':
        currentArray[currentIndex] = pgm_read_byte(&charSet[currentLetter]);	//Buchstabe aus Flash/PROGMEM auslesen
        increment(currentLetter, CHARSET_LENGTH);
        if (currentIndex > maxIndex)
          maxIndex = currentIndex;
        printInputField();
        break;
      case 's':	  //vorheriger Buchstabe
      case 'S':
      	currentArray[currentIndex] = pgm_read_byte(&charSet[currentLetter]);	//Buchstabe aus Flash/PROGMEM auslesen
        decrement(currentLetter, CHARSET_LENGTH);
        printInputField();
        break;
      case 'n':	  //vorheriger Buchstabe
      case 'N':
      	value = 0;
        lcd.clear();
        Serial.print("Menue - Name");
        lcd.setCursor(0, 0);
        lcd.print ("Name: ");
        break; 
      case 'o':	  //vorheriger Buchstabe
      case 'O':
      	value = 1;
         Serial.print("Menue - Ort");
         lcd.clear();
         lcd.setCursor(0, 0);
         lcd.print ("Ort: ");
        break;   
      case 'f':	  //Bestäigung
      case 'F':
        currentArray[maxIndex + 1] = '\0';  //String terminieren
        Serial.print(F("Ausgabe: "));
        Serial.println(currentArray);
        lcd.setCursor(1, 0);
        lcd.print ("Ausgabe:");
        lcd.setCursor(9, 0);
        lcd.print (currentArray);
        currentIndex = 0;		    //Indices zurücksetzen
        currentLetter = 0;
        memset(currentArray, 0, MAX_LENGHT);	//Array zurücksetzen. Das natürlich nicht machen wenn man das Array nach der Eingabe noch benötigt!!
        printInputField();
      default:
             ;
    }
  }
}

void increment(int& index, int max)
{
  index = (index + 1) % max;
}

void decrement(int& index, int max)
{
  index--;
  if (index < 0)
    index = max - 1;
}

void printInputField()
{
  for (int i = 0; i < MAX_LENGHT; i++)
  {
    if (currentArray[i] == 0)	//leere Stellen mit Zeichen darstellen
      {Serial.print(EMPTY);
      lcd.setCursor(0, i);
      lcd.print (EMPTY);}
    else
      {Serial.print(currentArray[i]);  
                              
        lcd.print (currentArray[i]);
 
  }
  Serial.println();

  for (int i = 0; i < MAX_LENGHT; i++)	//Cursor ausgeben
  {
    if (i == currentIndex)
    {   
     Serial.print('^');
     unsigned long currentMillis = millis();
       if(currentMillis - previousMillis > interval) 
         { 
         previousMillis = currentMillis;  
         lcd.cursor();
         }
    }   
         
    else
    {
     unsigned long currentMillis = millis();
        if (currentMillis - previousMillis > interval)
         {
         previousMillis = currentMillis;
         lcd.noCursor();        
         } 
    Serial.print(' ');
    }  
  
  Serial.println();
  }
 }
}

void selectMenu(int menu)
{
  
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print ("Willkommen bitte");
  lcd.setCursor(0, 1);
  lcd.print ("Menue waehlen");
  delay (5000);
  printMenue ("Ort: ","Name: ");
  
   if(menu == 0)
    { currentArray = name;
     }
   else if(menu == 1)
     {currentArray = ort;
     }
}

void printMenue (String Ort, String Name)
  {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print (Ort);
  lcd.setCursor(0, 1);
  lcd.print (Name);
  }