mehrstellige Zahlen eingeben mit Keypad (Probleme)

Hallo,

ich muss zu morgen ein Schulprojekt fertig bekommen und habe dort noch einige Probleme.

Ziel des Projektes ist, dass auf einem lcd Display 10 verschiedene Matheaufgaben angezeigt werden und die Lösungen anschließend über ein 4x4 Keypad eingegeben werden können. Anschließend soll je nach dem ob die Antwort richtig oder falsch war eine rote bzw. grüne LED leuchten.

Um die Eingabe der mehrstelligen Zahl zu beenden, sollte die Taste A gedrückt werden können.

//Variabeln:
int i, u, z[5], antwort, eingegeben, richtig = 12, falsch = 13; 

//keypad:
#include <Keypad.h>           // Bibliothek für Keypad wird hinzugefügt
const byte COLS = 4;          // 4 Spalten (Größe)
const byte ROWS = 4;          // 4 Zeilen (Größe)
char hexaKeys[ROWS][COLS]={   // Die Ziffern und Zeichen des Keypads werden eingegeben:
{'D','#','0','*'},
{'C','9','8','7'},
{'B','6','5','4'},
{'A','3','2','1'}
};
byte colPins[COLS] = {2,3,4,5};   //Definition der Pins für die 4 Spalten
byte rowPins[ROWS] = {6,7,8,9};   //Definition der Pins für die 4 Zeilen
char Taste;                       //Taste ist die Variable für die jeweils gedrückte Taste.
Keypad Tastenfeld = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); //Das Keypad kann absofort mit "Tastenfeld" angesprochen werden

//lcd Display:
#include <Wire.h>                   // Wird Bibliothek einbinden
#include <LiquidCrystal_I2C.h>      // Vorher hinzugefügte LiquidCrystal_I2C Bibliothek einbinden
LiquidCrystal_I2C lcd(0x27, 16, 2); // Hier wird festgelegt um was für einen Display es sich handelt. In diesem Fall eines mit 16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27. Für ein vierzeiliges I2C-LCD verwendet man den Code "LiquidCrystal_I2C lcd(0x27, 20, 4)" 



void setup() 
{
Serial.begin(9600); // Nur zur Testphase  !!! <- löschen

//lcd Display:
lcd.init();         // Im Setup wird der LCD gestartet 
lcd.backlight();    // Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus). 

//LEDs und so:
pinMode(richtig, OUTPUT); // LED grün
pinMode(falsch, OUTPUT);  // LED rot und Piezo piepst

//random
randomSeed(analogRead(A0));
} 



void loop() 
{ 
Serial.print("Start");

for (i=0; i<10; i=i+1){
  
  // Die 10 Aufgaben:
  int x[10] = {10, 67, 53, 98, 5, 7, 52, 46, 100, 32};
  int y[10] = {47, 45, 5, 50, 3, 7, 3, 754, 871, 6};
  String rechenart[10] = {" plus ", " minus ", " mal  ", " minus ", " mal  ", " plus ", " plus ", " mal  ", " minus ", " mal  "};
  lcd.setCursor(0,  0);            // Erste Zeile: 
  lcd.print(x[1]);                   // Aufgabe
  lcd.print(rechenart[1]);
  lcd.print(y[1]);
  
  //Eingabe über Keypad
  char Taste = Tastenfeld.getKey();
  for (u=0; Taste != 'A' ;u=u+1) {  
    if (Taste) {
      z[u] = Taste;
      Serial.print("eingegebene Ziffer: "); //Test
      Serial.print(z[u]); //Test
    }
  }
      
  switch (u) {    //Wie viele Ziffern hat die Zahl?
    case '1' : 
      eingegeben = z[0];
    break;
    case '2' :
      eingegeben = z[0]*10 + z[1];
    break;
    case '3' : 
      eingegeben = z[0]*100 + z[1]*10 + z[2];      
    break;
    case '4' :
      eingegeben = z[0]*1000 + z[1]*100 + z[2]*10 + z[3];
    break;
    case '5' :  
      eingegeben = z[0]*10000 + z[1]*1000 + z[2]*100 + z[3]*10 + z[4]*1;
    break;
      }
      
  lcd.setCursor(0, 1);            // Zweite Zeile:
  lcd.print(eingegeben);          // Eingegebene Zahl

  //eingegebene Antwort richtig oder falsch?
  if (eingegeben == antwort) {
    digitalWrite(richtig, HIGH);
    delay(1000);
    digitalWrite(richtig, LOW);
  } else {
    digitalWrite(falsch, HIGH);
    delay(500);
    digitalWrite(falsch, LOW);
  }
  } 
  }

Ich freue mich sehr über eure Hilfe

LG Leonie

Und wo ist Deine Frage bzw. was funktioniert und was nicht?

Gruß Tommy

Hallo Tommy,

die Ausgabe auf dem lcd Display funktioniert soweit.

Die Eingabe auf dem keypad funktioniert überhauptnicht, vermutlich geht die Idee mit der for-Schleife nicht auf. Außerdem leuchten sowohl die grüne als auch die rote LED, was durch die if... else... anweisung eigentlich unmöglich seien sollte.

LG Leonie

Hi Leonie,

was wird denn im Seriellen Monitor beim Tastendruck angezeigt?

Die Variablen antwort und eingegeben als int festzulegen ist riskant, 5-Stellige Zahlen dürften so nicht höher als ca. 32.700 sein. Hier besser als long definieren, größtmögliche Zahl > 2.000.000.000.

Die Variable antwort vergleichst du in der letzten if-Abfrage, du lässt den Wert von Antwort aber nicht setzen, daher ist antwort=0. Zumindest entdecke ich da nichts.

Die Zähler der for-Schleifen kannst du auch mit i++ (u++) hochzählen, aber dürfte so dennoch gehen.

Mit den LEDs komme ich gerade nicht drauf.

Gruß
Jörg

Hallo Jörg,

das ist es ja, beim Tastendruck wird im Seriellen Monitor nichts angezeigt.
Vielen dank für all die kleinen Hinweise.

Liebe Grüße Leonie

Die letzte for-Schleife gefällt mir nicht so sehr.

 //Eingabe über Keypad
  char Taste = Tastenfeld.getKey();
  for (u=0; Taste != 'A' ;u=u+1) {  
    if (Taste) {
      z[u] = Taste;
      Serial.print("eingegebene Ziffer: "); //Test
      Serial.print(z[u]); //Test
    }
  }

Ich bin noch lange kein Experte, aber ich meine dass der Zähler u sich nach wenigen Sekunden wieder nullt, spätestens nach dem ~32.700 Durchlauf, siehe Variablen antwort und eingegeben. Außerdem kann switch dadurch nur äußerst bedingt funktionieren, da u zwischen 0 und ~32.700 groß sein kann und das Array z kann dadurch auch nicht funktionieren oder nur mit Glück.

Ich könnte dir allerdings grad keine Lösung dafür anbieten, dafür reichen meine Kenntnisse noch nicht.

Hallo,

deine Lese for Schleife klappt so nicht. Du musst die Zahlen vom Keypad erstmal in einen Buffer einlesen. Dieses einlesen wird beim erkennen von 'A' beendet und die eingelesene Zahl danach ausgewertet, angezeigt oder sonstwas.

Deine erste Zeile der mehrfachen Variablendefinitionen könnte auch nicht das machen was es sollte.
Schreib das mal bitte alles einzeln.

Hallo,

welche Keypad Lib hast du? Link?

Ich benutze die Bibliothek namens keypad: Arduino Playground - Keypad Library

LG Leonie

Hallo,

ich komme nicht auf die Seite drauf, stellt sich wie tot. Mal sehen ob sich das noch gibt ...

Jetzt gings, sollte die vom Alexander Brevig sein.

 for (u=0; Taste != 'A' ;u=u+1) { 
    if (Taste) {
      z[u] = Taste;
      Serial.print("eingegebene Ziffer: "); //Test
      Serial.print(z[u]); //Test
    }

Hat 2 große Probleme:

  1. Taste wird innehalb fer FOR Schleife nie neu eingelesen und darum wird die Abbruchbedingung nie wahr.
  2. z führt schnell zu einem Überlauf des Indexes da dieser nur von 0 bis 4 geht. Das Resultat sind komische, unverständliche, unvorhersagbare und unmögliche Reaktionen und Fehler des Sketches.
    Grüße Uwe

Hallo,

ich wollte nachsehen was die Funktion zurückgibt wenn nichts gedrückt wurde. Es wird ein Null-Terminator zurückgeliefert.

Ich kann das nicht testen. Was passiert wenn du maximal 18 Zeichen eintippst und dann als letztes 19. Zeichen 'A'?

//keypad:
#include <Key.h>
#include <Keypad.h>           // Bibliothek für Keypad wird hinzugefügt

const byte COLS = 4;          // 4 Spalten (Größe)
const byte ROWS = 4;          // 4 Zeilen (Größe)
char hexaKeys[ROWS][COLS] = { // Die Ziffern und Zeichen des Keypads werden eingegeben:
  {'D', '#', '0', '*'},
  {'C', '9', '8', '7'},
  {'B', '6', '5', '4'},
  {'A', '3', '2', '1'}
};
byte colPins[COLS] = {2, 3, 4, 5}; //Definition der Pins für die 4 Spalten
byte rowPins[ROWS] = {6, 7, 8, 9}; //Definition der Pins für die 4 Zeilen
char Taste;                       //Taste ist die Variable für die jeweils gedrückte Taste.
Keypad Tastenfeld = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); //Das Keypad kann absofort mit "Tastenfeld" angesprochen werden

// globale Variablen:
/*
int i;            // antwort
int u;            // eingegeben
int z[5];
int richtig = 12;
int falsch = 13;
*/

const int SERIAL_BUFFER_SIZE = 20;
char serialBuffer[SERIAL_BUFFER_SIZE];

void setup()  {
  Serial.begin(9600);
}


void loop(void) {

  if ( read_Serial() )  {
    ShowSerialData();
  }

}


/* ------------------------------------------------------------------------------------------------ */

bool read_Serial()
{
  static unsigned int index;

  if (Tastenfeld.getKey() != NO_KEY)  {
    char c = Tastenfeld.getKey();
    if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if (c == 'A')
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}



void ShowSerialData ()
{
  Serial.println(serialBuffer);
}

Das Einlesen in einer for-Schleife kann nicht gehen. So schnell kannst du gar nicht drücken. Du musst jede Taste einzeln in loop() einlesen. Wenn gerade nichts gedrückt wird dreht sich loop() halt im Kreis

Mit int als Datentyp musst du auch vorsichtig sein. Der geht nicht weit genug für hohe 5-stellige Zahlen. Besser long verwenden

Hallo,

kann sein das funktioniert nicht, weil getKey 2x aufgerufen wird und damit der Eingabewert beim 2. Aufruf schon wieder weg ist. Dann bitte das ersetzen in

bool read_Serial()
{
  static unsigned int index;

  char c = Tastenfeld.getKey();
  if ( c != NO_KEY)  {
    if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if (c == 'A')
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}

Man muss nicht über ein Array gehen. Als Denkansatz, daher unvollständig und ungetestet:

uint32_t zahl;

bool zahlenEingabe(&zahl) {
static bool fertig = false;
  char c = lesenKeypad();  // hier Pseudofunktion auslesen des Keypad
  if (c == 'A') {
    fertig = true;
  }  
  else {
    if (fertig) { // erster Durchlauf nach fertig = neue Zahl = aufräumen
      zahl = 0;
      fertig = false;
    }
    if (c >= '0' && c <= '9') { // keine Sondertasten
      zahl = zahl * 10 + c -'0'; // * 10 + Ziffer
    }      
  }
  return fertig;
}

void loop() {
bool isZahl = zahlenEingabe(zahl);
  if (isZahl) {
    Serial.print("Eingegebene Zahl: ");
    Serial.println(zahl);    
}

Gruß Tommy

Hallo,

in diesen Intervallen wird das bis morgens wohl nichts - fürchte ich.
Meine angedachte Vorgehensweise war gewesen wie folgt, wenn obiger Sketch funktioniert wie erwartet.
Einlesefunktion auf Ziffern beschränken und in long Datentyp wandeln. Damit kann dann weitergerechnet oder verglichen werden. Das war mein Ansatz gewesen.

#include <Key.h>
#include <Keypad.h>           // Bibliothek für Keypad wird hinzugefügt

const byte COLS = 4;          // 4 Spalten (Größe)
const byte ROWS = 4;          // 4 Zeilen (Größe)
char hexaKeys[ROWS][COLS] = { // Die Ziffern und Zeichen des Keypads werden eingegeben:
  {'D', '#', '0', '*'},
  {'C', '9', '8', '7'},
  {'B', '6', '5', '4'},
  {'A', '3', '2', '1'}
};
byte colPins[COLS] = {2, 3, 4, 5}; //Definition der Pins für die 4 Spalten
byte rowPins[ROWS] = {6, 7, 8, 9}; //Definition der Pins für die 4 Zeilen
char Taste;                       //Taste ist die Variable für die jeweils gedrückte Taste.
Keypad Tastenfeld = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); //Das Keypad kann absofort mit "Tastenfeld" angesprochen werden

// globale Variablen:
/*
int i;            // antwort
int u;            // eingegeben
int z[5];
int richtig = 12;
int falsch = 13;
*/

const int SERIAL_BUFFER_SIZE = 20;
char serialBuffer[SERIAL_BUFFER_SIZE];

void setup()  {
  Serial.begin(9600);
}


void loop(void) {

  if ( read_Serial() )  {
    ShowSerialData();
  }

}


/* ------------------------------------------------------------------------------------------------ */

bool read_Serial()
{
  static unsigned int index;

  char c = Tastenfeld.getKey();
  if ( c != NO_KEY)  {
    //if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1)          // alles außer Steuerzeichen
    if ( (c >= 48) && (c <= 57) && (index < SERIAL_BUFFER_SIZE - 1) ) // nur Ziffern 0 ... 9
    {
      serialBuffer[index++] = c;
    }
    else if (c == 'A')                                        // Eingabe Endezeichen
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}



void ShowSerialData ()
{
  Serial.println(serialBuffer);
  long value = atol(serialBuffer);
  Serial.println(value);
}

Wenn es bis morgen früh fertig sein muss, dann ist der TO auch ziemlich spät hier aufgeschlagewn. Er hat die Aufgabe nicht erst heute bekommen. Das ist dann sein Risiko/Problem.

Ich sehe uns nicht als Hausaufgabenlöser, besonders auf den letzten Drücker.

Irgendwie scheinen jetzt wieder mal Abgabetermine anzustehen. Die Anfragen schlagen ringsum ein.

Gruß Tommy