Ausgabe auf LCD hat Sonderzeichen am Ende

Hallo Ihr Lieben,

ich habe hier ein seltsames Phänomen und brauche Eure Hilfe - ich bin leider eher Anfänger:

Ich lese über die serielle Schnittstelle SERIAL1 Daten von einem externen Gerät ein (exakt nach dem Beispiel hier im Forum (Serial Input Basics - updated - Projects / Tutorials - Arduino Forum), welches z.B. "VOL:60;" oder "VOL:50;" ausgibt, das funktioniert im seriellen Monitor auch einwandfrei. Gebe ich diesen in einem Array eingelesenen String jedoch auf dem angeschlossenen 20x4-LCD per lcd.print() aus, dann ist hinter dem ";" immer ein undefinierbares Zeichen.

Wenn ich einen festen String auf dem LCD ausgebe, passt alles, z.B. lcd.print("Test")...dort ist dieses Zeichen nicht am Ende zu sehen.

Kann das jemand erklären? Es hängt wohl irgendwie mit dem letzten (Steuer-)Zeichen im Array zusammen, was mit auf dem LCD ausgegeben wird.

#include <LiquidCrystal.h>

// LCD20x4 PINs
#define LCD_D7 37
#define LCD_D6 36
#define LCD_D5 35
#define LCD_D4 48
#define LCD_RS 21
#define LCD_E 47
#define LCD_BL_PWM 2
#define U2S_RX 18
#define U2S_TX 17
#define MAX_LCD_COL 20
#define MAX_LCD_LIN 4

LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);

const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data
boolean newData = false;

void setup() {
  pinMode(LCD_BL_PWM, OUTPUT);
  analogWrite(LCD_BL_PWM, 150);
  Serial.begin(115200);
  delay(500);
  Serial.println("Serial Monitor startet...");
  Serial1.begin(115200, SERIAL_8N1, U2S_RX, U2S_TX);
  delay(500);
  lcd.begin(MAX_LCD_COL, MAX_LCD_LIN);
  lcd.clear();
}

void loop() {
    recvWithEndMarker();
    showNewData();
}

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
    while (Serial1.available() > 0 && newData == false) {
        rc = Serial1.read();
        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        Serial.println(receivedChars);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print(receivedChars);
        newData = false;
    }
} 

Danke Euch schonmal!

Wahrscheinlich ist von der Eingabe noch ein Zeilenvorschub / Wagenrücklauf hinten dran, denn musst Du entfernen. Mit welcher Einstellung sendest Du aus dem seriellen Monitor?
Ansonsten lass Dir doch mal receivedChars zeichenweise als HEX ausgeben.

Bitte schreibe nicht nach jeder Codezeile eine Leerzeile. Das stört den Lesefluss.

Gruß Tommy

Hallo Tommy,

vielen Dank für die ultraschnelle Antwort!

So etwas in der Art habe ich mir auch gedacht, ich habe auch schon versucht einfach ein Zeichen weniger auszugeben:

    `lcd.print(receivedChars - 1);`

Das hat aber nichts gebracht, auch nicht mit zwei Zeichen. Dann steht das Sonderzeichen auf einmal am Anfang der Ausgabe auf dem LCD. Auch der Test mit verschiedenen Sonderzeichen als Endmarker hat bisher nicht geholfen.

Du meinst die Ausgabe in der IDE? Die steht auf "neue Zeile", aber mit der Ausgabe auf dem LCD hat das nichts zu tun, oder?

Hat sich erledigt, ich habe die trim()-Funktion gefunden und eingebaut und es funktioniert nun wie gewünscht.

Wenn Du die Eingabe vom seriellen Monitor auf dem LCD ausgibst, dann hat diese natürlich etwas damit zu tun.

Gruß Tommy

Hmm, hatte ich doch oben geschrieben: die Daten kommen von extern an Serial und werden in ein Array eingelesen. Diese werden danach einmal auf dem seriellen Monitor ausgegeben und parallel dazu auf das LCD geschrieben.

Dann schreibe die empfangenen Bytes vor der Verarbeitung doch einmal (HEX-) auf den seriellen Monitor.

...
        rc = Serial1.read();
        Serial.print(rc, HEX);   // <--- Testausgabe
        Serial.print(' ');       // Optik, erleichtert das Lesen
        if (rc != endMarker) {
...

Dann wirst Du schon sehen, was da am Ende drin steht (Vermutung: 13 10 bzw. 0x0D 0x0A).

Ok, dann habe ich die Herkunft verwechselt. Wahrscheinlich sendet das externe Gerät 0xD und 0xA. Du filterst nur eins raus.

Gruß Tommy

Danke für die Hilfe, es kommt folgendes an (wenn z.B. "VOL:62;" an Serial1 reinkommt):

56 4F 4C 3A 36 32 3B D A

0D müsste CR (carriage return) sein, 0A müsste LF (line feed) sein, auch wenn in der Ausgabe die vorgestellte 0 fehlt.

Komischerweise hat es oben nichts gebracht, die beiden letzten Zeichen mit lcd.print(receivedChars - 2); "rauszufiltern".

Hallo,

schau dir einmal die ASCII Tabelle an. Man sollte, macht man eigentlich immer, alle nicht benötigten Steuerzeichen, nicht verwechseln mit Sonderzeichen, beim einlesen vorm in den Buffer schreiben filtern. Das heißt alles bis dezimal 32 kann weg.

Mach das so bitte nicht. Ändere die Einlesefunktion, also dort am dem Ort wo es passiert.

Danke für den Tipp, aber das übersteigt leider meine Fähigkeiten :slight_smile:

Kannst Du mir das ggf. im Code oben ergänzen?

Danke vorab!

Na siehste - wie @Tommy56 schon in #2 und wieder in #9 schrub: Da sind sie ja, die CR LF Bytes.
Versuch mal (habe ich nur so "trocken" ohne Test runtergetippt):

    char endMarker = 0x0A;  // '\n';
    char rc;
    while (Serial1.available() > 0 && newData == false) {
        rc = Serial1.read();
        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            // es ist jetzt ein 0x0D als letztes Byte im Puffer receivedChars
            // Ganeu das kann mit der \0 überschrieben werden.
            ndx--;  // also ein Byte zurück und weiter gehts.
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
...

Ist "minimal-invasiv", denn...

Das leistet meine kleine Änderung natürlich nicht...

Allgemein String, Text kürzen

 String req = "0987654321\n\r";
  int Laenge = req.length();
  int index = Laenge - 9;
  req.remove(index);
  String gekuerzt = req;
  Serial.print(F("gekuerzt = '"));
  Serial.print(gekuerzt);
  Serial.println(F("'"));

ergibt 098, habe das mal irgend wo gebraucht.

Du hast aber schon gesehen, dass der TO gotteidank nicht mit String sondern mit einem Character-Array arbeitet, oder?

char receivedChars[numChars]; 

Danke Euch nochmals, ich hatte das so gelöst mit der trim()-Funktion:

void showNewData() {
    if (newData == true) {
        Serial.println(receivedChars);
        mystring = (receivedChars);
        mystring.trim(); //entfernt die Sonderzeichen am Ende vor der LCD Ausgabe
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print(mystring);
        newData = false; 
        } 

Spricht etwas dagegen? Funktioniert jedenfalls :-)

Ja: mystring ist ein String - was auf kleinen Maschinchen wie dem Arduino über kurz oder lang immer zu Speicherproblemen führt.

Das macht etwas ganz anderes, als Du denkst.
Nehmen wir an, in Deinem Speicher steht:

0x80x99Hallo0xd0xa0x0 

Dein receivedChars ist ein Zeiger, der auf den Anfang von Hallo zeigt. Mit Zeigern kann man auch rechnen. Also zeigt receivedChars-2 auf 2 Zeichen davor, also auf 0x8 und das ist nicht das, was Du Dir vorgestellt hast.

Gruß Tommy

Na ja, kann man auch machen.
Unserer Jurs hat was einfaches gemacht, das kann man auch mit Serial machen.

void setup() 
{

  Serial.begin(9600);
  String request="GET /rgb/set/7d52ff HTTP/1.1\r";
  char* substr=strstr(request.c_str(),"HTTP/1.1\r")-7; // Substring lokalisieren
  int red, green,blue;
 sscanf(substr,"%02x%02x%02x%02x",&red,&green,&blue); // Substring in Farbwerte zerlegen
 Serial.println("Scanned RGB values:");
 Serial.print("red:\t"); Serial.println(red);
Serial.print("green:\t"); Serial.println(green);
Serial.print("blue:\t"); Serial.println(blue);


}

void loop() {
  
}

Danke, an den ganzen Beispielen hatte ich mich orientiert :slight_smile: und mit verschiedenen Endmarkern (CR oder LF) hat es leider nicht funktioniert.