Variable fällt auf 0 zurück

Moin,
ich habe das Problem, dass ich in meinem Programm per Tastendruck eine einfache Auswahl treffen will und diese fortan bestehen bleibt.
Dazu hab ich int Auswahl noch vor dem setup() deklariert, innerhalb von setup zur sicherheit auf 0 gesetzt und anschließend in loop () je nach gedrückter Taste mit einer Ganzzahl überschrieben.
Doch sobald ich daraus eine LCD-Ausgabe ableite, geht die Variable anscheinend auf 0 zurück.
hab’s schon mit einem zusätzlichen static probiert, ändert nix. Wo liegt das Problem?!

#include <Wire.h>
#include <LiquidCrystal_I2C.h>


int InputPin;
int ButtonState;
static int Auswahl;


LiquidCrystal_I2C lcd(0x27, 16, 2);
char LCDLine1[16];
char LCDLine2[16];

void LCDupdate (char Line1[16],char Line2[16])
{
    lcd.setCursor(0,0);
    lcd.print(Line1);
    lcd.setCursor(0,1);
    lcd.print(Line2);
}



void setup() {
  // put your setup code here, to run once:
  Auswahl = 0;
  // LCD an I2C starten
  lcd.begin();
  lcd.clear();

  //LCD-Zeile1 mit dauerhaftem Text füllen
  sprintf(LCDLine1,"blablablabla....");


  //joypad-Tasten als Input definieren)
  for(InputPin=2; InputPin<=8; InputPin++)
  {
    pinMode(InputPin, INPUT);
  }


  // serieller Monitor starten
  Serial.begin(9600);
  delay(3000); // wait for console opening
  Serial.println("gestartet");

}



void loop() 
{
  // put your main code here, to run repeatedly:


  //Tasten an Input 2-8 abfragen
  for( InputPin=2; InputPin<=8; InputPin++)
  {
    ButtonState = digitalRead(InputPin);
    Serial.print(InputPin);
    Serial.print(":");
    Serial.print(ButtonState);
    Serial.print(" ");
    if(InputPin==8){Serial.println();}   //Umbruch wenn alle Tasten gelesen


    if (ButtonState == 0)           // Wenn Tastendruck LOW an x, dann Menüpunkt y)
    {
      Serial.println();                     //debug
      Serial.println("knopf gedrückt");     //debug
      if (InputPin == 2){Auswahl=1;}
      if (InputPin == 3){Auswahl=2;}
      if (InputPin == 4){Auswahl=3;}
      if (InputPin == 5){Auswahl=4;}
      Serial.print("Auswahl nach Button="); //debug
      Serial.println(Auswahl);              //debug
    }
  }  



  /* PROBLEM im nachfolgenden Teil:
   * Auswahl geht auf 0 zurück, wenn Taste losgelassen, 
   * soll aber alten Zustand beibehalten.
   */

  //Zweite LCD-Zeile mit Text füllen
  Serial.print("Auswahl vor Text=");  //debug
  Serial.println(Auswahl);            //debug    Hier ist Auswahl noch ok!
  if (Auswahl==0){sprintf(LCDLine2,">Knopf druecken<");} 
  if (Auswahl==1){sprintf(LCDLine2,"abcd           ");}   //zumindest    
  if (Auswahl==2){sprintf(LCDLine2,"blabla          ");}   //während Druck
  if (Auswahl==3){sprintf(LCDLine2,"xyzxyz          ");}   //erscheint Text
  if (Auswahl==4){sprintf(LCDLine2,"blablubb        ");}    
  Serial.print("Auswahl nach Text="); //debug    Hier ist Auswahl immer 0!
  Serial.println(Auswahl);            //debug    Damit in nächster Schleife
                                      //         ungewollt "Knopf drücken"
     

  //LCD-Text aufs Display schreiben
  LCDupdate(LCDLine1,LCDLine2);
 
  delay(500);
}

(deleted)

Hallo,

InputPin scheinen die Pinnummern zu sein die in der for durchgerasselt werden. Es liegt möglicherweise am ButtonState.
Das Problem ist, dass der gesamte Code nur mit großer Mühe lesbar ist. Weil man die Abfrage so eigentlich nicht macht. Ich würde den Indexzähler nutzen. Der ist im Schleifendurchlauf bis zum nächsten Durchlauf eh konstant.
Ich würde das zudem auftrennen. Eine Funktion die alle Taster abfragt und den gedrückten Taster als Wert zurückgibt.
Diesen Wert der nächsten Funktion übergeben.

Bspw. so, ungetestet, nur kompiliert

Dann schreibste dir noch eine Funktion die nur reagiert wenn Tasternummer verschieden vom Rückgabewert 255.
Oder meinetwegen mit in die updateLCD Funktion integrieren.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

byte const ERSTER_PIN = 2;
byte const LETZTE_PIN = 8;

LiquidCrystal_I2C lcd(0x27, 16, 2);
char LCDLine1[16];
char LCDLine2[16];

void setup()
{
  lcd.begin();
  lcd.clear();

  //LCD-Zeile1 mit dauerhaftem Text füllen
  sprintf(LCDLine1, "blablablabla....");

  // Joypad-Tasten als Input definieren
  for (byte i = ERSTER_PIN; i <= LETZTE_PIN; ++i)
  {
    pinMode(i, INPUT);
  }

  // serieller Monitor starten
  Serial.begin(9600);
  delay(3000); // wait for console opening
  Serial.println(F("gestartet"));
}


void loop()
{
  byte taster = updateTaster(ERSTER_PIN, LETZTE_PIN, 30);             // Tasten 2...8 aller 30ms abfragen

  byte auswahl = auswertungTaster(ERSTER_PIN, LETZTE_PIN, taster);

  //Zweite LCD-Zeile mit Text füllen
  Serial.print("Auswahl vor Text=");  //debug
  Serial.println(auswahl);            //debug
  if (auswahl == 255) {
    sprintf(LCDLine2, ">Knopf druecken<");
  }
  if (auswahl == 1) {
    sprintf(LCDLine2, "abcd            ");
  }
  if (auswahl == 2) {
    sprintf(LCDLine2, "blabla          ");
  }
  if (auswahl == 3) {
    sprintf(LCDLine2, "xyzxyz          ");
  }
  if (auswahl == 4) {
    sprintf(LCDLine2, "blablubb        ");
  }
  Serial.print("Auswahl nach Text="); //debug
  Serial.println(auswahl);            //debug

  // LCD-Text aufs Display schreiben
  updateLCD(LCDLine1, LCDLine2, 500);

}


// ****** Funktionen ******

void updateLCD (char Line1[16], char Line2[16], unsigned int const INTERVAL)
{
  static unsigned long lastMillis = 0;
  unsigned long ms = millis();

  if (ms - lastMillis >= INTERVAL)
  {
    lastMillis = ms;

    lcd.setCursor(0, 0);
    lcd.print(Line1);
    lcd.setCursor(0, 1);
    lcd.print(Line2);
  }
}


byte updateTaster(byte const ERSTER, byte const LETZTER, unsigned int const INTERVAL)
{
  static unsigned long lastMillis = 0;
  byte lastButton = 255;

  unsigned long ms = millis();

  if (ms - lastMillis >= INTERVAL)
  {
    lastMillis = ms;

    for (byte i = ERSTER; i <= LETZTER; ++i)
    {
      bool state = digitalRead(i);
      if (state)
      {
        lastButton = i;
        Serial.print(i);
        Serial.print(":");
        Serial.println(lastButton);
      }
    }
  }
  return lastButton;
}

byte auswertungTaster(byte const ERSTER, byte const LETZTER, byte const lastTaster)
{
  byte taster = 255;

  for (byte i = ERSTER; i < LETZTER; ++i)
  {
    if (i == lastTaster)
    {
      taster = i - 1;
      Serial.print(i);
      Serial.print(" : ");
      Serial.println(lastTaster);
    }
  }

  // Sonderfall, wofür auch immer
  if (lastTaster == 8) {
    Serial.println();
  }

  return taster;
}

Edit:
Nochwas. Statt sprintf würde ich memcpy verwenden, weil du formatierst nichts, es wird nur kopiert. Die Zeilenlänge von ‘16’ konstant machen und überall als Parameter nutzen.

Was mir mal so auf den ersten Blick auffällt:

char LCDLine1[16];
char LCDLine2[16];

Diese Arrays sind zu klein dimensioniert. Es fehlt der Platz für das abschließende \0 .
Jedesmal, wenn Du die mit einem 16 Zeichen langen c-String füllst, wird die Speicherzelle die dahinter liegt mit 0 überschrieben.

Edit: ~~ich verstehe auch nicht, wie das mit den Tastern funktionieren soll. Du liest alle ein, überschreibst aber immer 'ButtonState'. Da steht dann letztendlich nur der Status des letzten Tasters drin.~~Hab's gesehen, das ist ja noch alles in der for-Schleife drin.

Für die Abfrage von so vielen Tastern könntest Du auch die Klasse MoToButtons aus meinen MobaTools benutzen. Da tust Du dich wesentlich leichter, die auszuwerten. Da ist dann auch schon eine Flankenauswertung drin.

thx, die Array-Größe war's. Mit Länge 17 klappt es wie gewünscht.

static int Auswahl;

wieso static?

Grüße Uwe

War wohl ein Ergebnis seiner Versuche…
Aber eigentlich könnte-sollte-müsste :wink: man alle globale Variable, die nur im .ino File genutzt werden ‘static’ deklarieren.

Doc_Arduino:
Nochwas. Statt sprintf würde ich memcpy verwenden, weil du formatierst nichts, es wird nur kopiert. Die Zeilenlänge von ‘16’ konstant machen und überall als Parameter nutzen.

Sauberer ist es eine Funktion aus der strcpy() Familie zu verwenden und das Array 17 lang machen. z.B. strncpy() mit sizeof(LCDLine1) - 1 als Größe. Wenn man sich die Beschreibung genau durchliest sieht man dass das nicht terminiert wenn man die Grenze erreicht hat. Deshalb - 1

Es gibt mit snprintf() auch eine “n” Version von sprintf()! Hier ist das -1 nicht nötig, da die Funktion das korrekt macht

Wobei dieses Kopieren gar nicht unbedingt nötig sind. Wieso nicht sowas?

void printStr(const char* str, byte line)
{
    lcd.setCursor(0, line);
    lcd.print(str);
}

Dann kann man einfach das machen:

printStr("Blah blah", 0);
printStr("Mehr blah blah", 1);

Hallo,

ja stimmt, hab nicht aufgepasst. Entschuldigung.
Meine Ausrede ist, weil ich aktuell nur mit Arrays aus Integern zu tun habe. :slight_smile:
Ohne kopieren ist natürlich noch besser.
Gut das ich nicht alleine hier bin.

Moin,

jetzt mal als Noob-Frage - ist das so denn dann Überlauf-sicher? Oder muss man penibel beim "..."-Freitext mitzählen?
Bzw. was genau bewirkt hier das const? (hab es noch nicht so mit den Variablen-Attributen)

Wird hier ein Zeiger auf ein Char-Array namens str kreiert, das durch const-Attribut fest am gleichen Speicherort startet?!
Nun ist "MehrBlablah" aber länger als "blabla". Wie bestimmt sich die reservierte Größe?

Oder wird ein fixer Speicherort definiert für einen Zeiger auf verschiedene Strings?! Wäre das dann nicht eher ein static-Verhalten?

Serenifly:
Wobei dieses Kopieren gar nicht unbedingt nötig sind. Wieso nicht sowas?

void printStr(const char* str, byte line)

{
    lcd.setCursor(0, line);
    lcd.print(str);
}



Dann kann man einfach das machen:


printStr("Blah blah", 0);
printStr("Mehr blah blah", 1);

const* bedeutet dass der Zeiger auf konstante Daten zeigt. String Literale sind const. Wenn du das weglässt geht es, aber du bekommst eine Warnung.
Generell verwendet man const immer für Dinge die sich nicht ändern sollen. Dann kann man sie auch nicht ändern

Nun ist "MehrBlablah" aber länger als "blabla". Wie bestimmt sich die reservierte Größe?

Der erledigt bei String Literalen alles der Compiler für dich. Der sieht wie lang der Text ist und macht das entsprechend. Du hast hier keinen Speicher wie bei einem Array auf den du beliebig zugreifen kannst

Wird hier ein Zeiger auf ein Char-Array namens str kreiert

Das ist kein Zeiger auf ein Array, sondern einfach nur ein Zeiger. Array-Variablen sind in C schon nicht viel mehr als Zeiger auf das erste Element. Auch wenn du bei der Paramter-Übergabe schreibst. Arrays sind in C/C++ keine eigenen Datentypen oder Objekte wie in machen anderen Sprachen!

Der Funktion könntest du genauso ein char Array übergeben:

char str[] = "Blah blah";
printStr(str, 0);