Fragen an die Profis (Tastatur Input Verabeitung und SD Log)

Guten Morgen liebe Forenmitglieder,

Mein erstes großes Aduinoprojekt nähert sich nun der Vollendung, auch dank Eurer super Hilfe. 2 Probleme sperren sich im Moment noch gegen eine Lösung.

Das Projekt in wenigen Worten: Ein Versuchsfahrzeug besitzt ein Arudino und einen 4d Systems Touchscreen als Glascockpit zur Anzeige der Werte, sowie eine SD Card. (Geloggt und Angezeigt werden zig Werte, wie GPS, Geschwindigkeit, Werte einer IMU, 10 Radsensoren, Motorwerte usw...) Ebenfals können 5 verschiedene Parameter über Aktuatoren verändertet werden) funktioniert alles wunderbar.

Auf dem Touchscreen ist auf einer Unterseite eine "klassische" Tastatur. Diese Tastatur soll für Komentare genutzt werden, die am Ende eines Logs angehängt werden. Soll heißen, ich Drücke einen Button, der Datenlog beginnt, ich drücke Ihn erneut, der Datenlog wird gestoppt und auf den Screen erscheint die Tastatur. Ich tippe einen Komentar ein, bsp: "Fahrt mit diesen Werten war gut", drücke Enter, Komentar wird gespeichert am Ende des Logs, Datei geschlossen. So weit so gut... Das funktioniert auch teils.... Ich wollte mal anfagen, wie und ob man das schöner machen kann und vor allem, wie ich meine Fehler behebe. Die Tastatur liefert die Eingaben in HEX.

Das Unterpogram dafür:

void handleGenieEvent (void)  //Touch wird berührt, löst Fkt aus. (Unterporgramm des Haupt loops)
{

char keyboard2;


 if (event.reportObject.index == 1)        //  Keyboard 1 (Großes Keyboard für  Logdatei)
      {
       keyboard2 = genieGetEventData(&event);  Daten werden an Keyboard 2 übergeben
      
       int opskey = keyboard2;              // Opskey ist für die Befehls Tasten wie Enter, Del usw.... (Zahl)
       
       Serial.println(keyboard2);           // Zu Visulaisierung
       Serial.println(keyboard2, HEX);
       Serial.println(keyboard2, DEC);
       
       TXTdisp[TXTdisp_index] = keyboard2;  //TXT Disp wird am Anfang im Hauptprogramm initalisiert. (Char)
       TXTdisp_index++;                                    //Index Zählt bei jeder Eingabe eins hoch.
       
       Serial.print(TXTdisp);
       
    
       genieWriteStr(0, TXTdisp);           //Schreiben auf Display
       
      
       switch (opskey)    
         
         {
          case 13:                     // Enter
          myFile.println(TXTdisp);
          myFile.close(); 
         
          TXTdisp[0] = '\0';
          
          genieWriteStr(0, TXTdisp);  //Leeren des Displays
          genieWriteObject (GENIE_OBJ_FORM, 3, 0) ; //Schlißen der Tastatur
          genieWriteObject(GENIE_OBJ_USER_LED, 0, 0); //LOG LED abschalten
          break;
         
          case 0:                      // Del/Back
          TXTdisp[TXTdisp_index-1] = '\0';
          
          
          break;
         }
      } 
    }
}

Die Frage ist, bin ich überhaupt auf dem richtigen weg, oder ist das total Murks?

Nun zu meinen Problemen:

  • Einmal geht es, aber wenn ich den Befehl TXTdisp[0] = '\0'; verwende bleibt das char beim 2 Log immer leer danach, soll heißen, es lässt sich nicht mehr befüllen. Andere Möglichkeiten es zu leeren hatten auch keinen Erfolg bisher.

  • Zahleneingaben checkt er gar nicht. Da macht er einen dicken Punkt in der Zeilenmitte

Es verhält sich algemein etwas "buggy" bei der Eingabe.

Mir sind 2 Sachen allgmein nicht so ganz klar: In welcher Form (Hex, ASCII usw) verabreitet man das am besten weiter. Wie handelt man Sondertasten am besten ab. Ich springe von DEC, HEX, ASCII, hin und her... Alles etwas konfus. Verwende ich Chars richtig? Array, pointers .... Fragen über Fragen.

Vielen Dank schon mal für Eure Hilfe. Gruß Michl

Wenn du bei "Enter" von der unbekannten Funktion genieGetEventData(&event); eine 13 zurückkriegst, ist die "Del/Back" Taste vermutlich nicht die 0. Aber das kriegst du ja über deine drei Serial.println(keyboard2) - Ausgaben raus. Im Fall "Del/Back" solltest du nicht nur die '\0' auf index-1 schreiben, sondern auch den Index selbst verringern.

Ausserdem solltest du vor Serial.print(TXTdisp); die Endekennung gesetzt haben:

       TXTdisp[TXTdisp_index++] = keyboard2; 
       TXTdisp[TXTdisp_index] = '\0';

Dass du mit jedem Zeichen auf Serial und dem Display den kompletten Text (zur Zeit noch mit undefiniertem Schrott am Ende) ausgibst, weisst du ? Das ist bei längeren Texten nicht so schön, aber einfach.

  • Zahleneingaben checkt er gar nicht

Stimmt, Zahleneingaben checkst du gar nicht. Das sind Buchstaben wie alle anderen auch in deinem Kommentar-Text. Was anderes sehe ich nicht.

Mir sind 2 Sachen allgmein nicht so ganz klar: In welcher Form (Hex, ASCII usw) verabreitet man das am besten weiter. Wie handelt man Sondertasten am besten ab. Ich springe von DEC, HEX, ASCII, hin und her... Alles etwas konfus.

Ich rate mal aus deinen Zeilen ohne Variablen-Definition und Funktionsdeklaration, dass

genieGetEventData(&event);  Daten werden an Keyboard 2 übergeben

ein Zeichen zurückliefert, was in eine char Variable passt. Das kannst du nach Geschmack mit println ausgeben, die Variable enthält immer den gleichen Wert. Eine '7' ist eine '7' ist eine 0x37 ist eine 55 , eine Enter-Taste liefert (vermutlich) eine '\r' und eine 0x0d und eine 13, was alles dasselbe ist. Wie du das in deinem Code behandelst, ist egal, hauptsache es ist leicht nachzuvollziehen, was der Code soll. Manche definieren Konstanten mit Namen ( oder #define ) um den Code selbsterklärend zu machen

#define ESC   0x1B
#define ENTER  0x0D
...
switch ( key ) {
  case ESC: 
      handleEscapeKey();
      break;
  case '0':
  case '1':
  case '2':  
  case '3':
  case '4':
  case '5':  
  case '6':
  case '7':  
  case '8':
  case '9':
    handleNumericKey(key-'0');  // bearbeitet eine Zifferntaste, die als 0 .. 9  ( "binär" ) übergeben wird   
    break;
  case ENTER:
    processNumeric(value);  // value enthält eine Zahl aus den bisher eingegeben Ziffern
    value = 0;   // vorbereiten für eine neue Zahlen-Eingabe
    break; 
}
unsigned long value;
//////////////// handleNumericKey( byte c)  ////////////    Dummy - Beispielfunktion
// c muss 0 .. 9 sein, das Gesamt-Ergebnis als Dezimalzahl wird in value gespeichert.
// Überlauf wird nicht geprüft, Neueingabe einer neuen Zahl wird nicht hier gesteuert
void handleNumericKey(byte c)
{  
    value = value*10+c;
}

Boa! Hammerlange Antwort. Vielen Dank!! Ich werde mich damit erstmal auführlich beschäftigen müßen, aber so grob verstehe ich nun, wie das laufen muss und vor allem wie es übersichtlicher wird.

Hast Du noch einen Typ für dieses Problem:

Wenn ich das char TXTdisp[200] abgespeichert habe, dann möchte ich es wieder leeren.
In verschieden Foren habe ich gelesen:

TXTdisp[0] = ‘\0’;

Was auch funktioniert. Das Char ist dann leer.

Nur das Problem ist, es bleibt auch leer! Soll heißen, beim nächsten LOG und neuem Kommentar kommen zwar alle Zeichen (jetzt auch Ziffern, Leerzeichen, Groß/klein, Sonderzeichen usw JUHUU :slight_smile: ) an, wie ich im Serial Moitor sehe, aber sie werden nicht in das Char geschieben.

Danke und Gruß Michael

Das Char ist dann leer.

Das ist ein missverständlicher Ausdruck, bei dem sich (vermutlich nicht nur mir) die Nackenhaare sträuben.
ein Array von char wird auch gerne string oder c-string genannt, was allerdings nicht mit einem String Objekt verwechselt werden darf.
Ein char* ( Pointer, mit dem Sternchen !) ist die Adresse eines char,
und damit gegebenfalls auch die Anfangsadresse eines char - Arrays, und kann so meist genauso verwendet werden.

Jedenfalls ist in solch einem Text alles nach dem ersten Ende - ‘\0’ -Zeichen undefiniert,
und wird von Funktionen die mit char* arbeiten, nicht mehr verwendet.
Wenn TXTdisp[0] den Wert 0 ( das gleiche wie ‘\0’ ) behält, bleibt das Ding “leer”, egal was in TXTdisp[1] und weiteren Bytes steht oder eingetragen wird…

Tip: am besten immer die index-Variable mit setzen, und nicht vergessen, vor einer Verwendung des char* an der richtigen Stelle die Ende-Kennung zu setzen.

const byte TXTLEN=20;  
byte index:
char TXTdisp[TXTLEN+1];  // Platz für 20 Zeichen + Ende-Kennung
...
index = 0;   //  TXTdisp löschen 
TXTdisp[index] = 0;
char c = 'X';  
if (index  < TXTLEN)  TXTdisp[index++] = c;  // Zeichen c in TXTdisp am Ende anfügen
TXTdisp[index] = 0;  // und Endekennung neu setzen

'\0' ist lediglich der Terminator des Strings. Sämtliche String Funktionen verarbeiten übergebene Arrays bis dahin.

Wenn du in dem Puffer ein Zeichen auf '\0' setzt, dann ist der String da zu Ende. Der Speicher dahinter existiert aber noch und da steht auch noch Zeug vom vorherigen Durchlauf drin!

da steht auch noch Zeug vom vorherigen Durchlauf drin!

... sonst könnte der heartbleed bug nicht funktionieren ;)

Danke nochmal für die Antworten. Deinen Tip, mit dem Index habe ich umgesetzt. Macht auf jeden Fall so am meisten Sinn, da ich so immer weiß, wo ich mich befinde. Gerade mit den "Sondertasten" wie löschen, vor, zurück, ist das mehr als sinnvoll.

Ich glaube, das nun verstanden zu haben, warum das mit dem /0 nicht funktionieren konnte und vor allem, wie es sich im Speicher verhält.

Nach einigem Probieren und weiterer Suche bin ich auf diesen Ausdruck gestoßen (die Lib, die diese Funktion beinhaltet, habe ich anderweitig schon im Programm benötigt)

memset(TXTdisp,0,sizeof(TXTdisp));

Der macht genau das, was ich will. :-) Im nächsten Durchgang den index auf Null und dann kann ich wieder von vorn das Ding befüllen und was drin war ist weg. So zumindest auf dem Display sowie auf der SD karte. :-)) Wenn ich es richtig verstanden habe, wird das char damit "ausgenullt"?

Wenn ich es richtig verstanden habe, wird das char damit "ausgenullt"?

Ja. Damit sparst du dir, am Ende eine 0 zu schreiben, weil das ganze Array schon vorher mit 0 initialisiert wurde.

mit "das char" meinst du aber "die chars". Ein char enthält max 1 Zeichen im üblichen Sprachgebrauch ;)