OLED und String-Ausgabe

Hallo zusammen,

ich habe an meinen Arduino Uno ein OLED Display angeschlossen. Zusätzlich möchte in über den Seriellen-Monitor mit dem Ardunio kommunizieren. Mein Problem ist, dass die Strings manchmal nicht korrekt verarbeitet werden.

Wenn ich das Programm starte und im Seriellen-Monitor “Zeit” eingebe sollte die Ausgabe:

Es wurde ‘zeit’ eingegeben. Laenge 4
sHilfe: 20:36:11 Länge: 8
Aktuelle Zeit: 20:36:11 Uhr, 1549053371

Es wird aber nur :

Es wurde ‘zeit’ eingegeben. Laenge 4
sHilfe: 20:35:56 Länge: 8
Aktuelle Zeit:

Das heißt das zusammensetzen des Strings sAusgabe funktioniert nicht.

Die Fehlerhafte Ausgabe kann ich korrigieren, wenn ich andere Teile des Programms auskommentiere, z.B. wenn ich in setup() den Teil für das OLED display (Zeile 20-22). Es funktioniert ebefalls wenn ich den Inhalt der if-Abfrage in der Funktion SerialPortLesen() Zeile (80-83) auskommentieren.

Ich habe schon viel rumprobiert. Ein umstellen von Strings auf C-String hat das Problem auch nicht gelöst. Wenn ich irgendwo etwas ändere geht an einer anderen Stelle eine String-Operation oder Ausgabe nicht.

Es ist auch schon passiert. Das Ausgaben in setup () nicht mehr funktioniert hat, wenn ich etwas in der Funktion SerialPortLesen() etwas geändert habe. Obwohl die Funktion noch gar nicht aufgerufen wurde.

#include <Time.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Deklaration des SSD1306 Display angeschlossen an I2C (SDA, SCL pins)
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);  //Anzeige für OLED-Display intialisieruen

time_t iZeitEingabe; // Lezter Zeitstempel, der Eingeben wurde


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
    Serial.println("SSD1306 allocation failed");
  }   

  //Serial.println(UhrZeit(10, 5, 1) + " Uhr");
   setTime(1549053354);
}

void loop() {

  SerialPortLesen();
}


String UhrZeit (int Stunde, int Minute, int Sekunde) {
  //Gibt einen String in der Form: hh:mm:ss zurück.
 
  String sZeit;

  if (Stunde < 10) {
    sZeit = '0';
    sZeit.concat(Stunde);
    sZeit.concat(':');
  } else {
    sZeit = Stunde;
    sZeit.concat(':');
  }
  if (Minute < 10) {
    sZeit.concat('0');
    sZeit.concat(Minute);
    sZeit.concat(':');
  } else {
    sZeit.concat(Minute);
    sZeit.concat(':');
  }
    if (Sekunde < 10) {
    sZeit.concat('0');
    sZeit.concat(Sekunde);
  } else {
    sZeit.concat(Sekunde);
  }
  return sZeit;
}

void SerialPortLesen() {
// Ließt Eingaben vom Serielen Port aus
  String sEingabe;
  String sAusgabe;
  String sHilfe;

  if (Serial.available() == 0) return; // Wenn keine Zeichen vorhanden sind, Funktion verlassen

  sEingabe = Serial.readString();
  sEingabe.trim();  //Entfernt den Zeilenumbruch und eventuelle Leerzeichen
  Serial.println ("Es wurde '" + sEingabe + "' eingegeben. Laenge " + sEingabe.length());
  sEingabe.toLowerCase(); 

  if (sEingabe.startsWith("t") && sEingabe.length() == 11) {
  // Eine neuer Zeitstempel wird eingegeben, wenn erste Buchstabe 't' und 11 Zeichen enhalten sind
    sEingabe.remove(0,1);  // Entfernt das nullte Zeichen also das 't'
    iZeitEingabe = sEingabe.toInt();
    Serial.print("Zeit wird gesetzt "+ sEingabe+" umgewandelt ist das: ");Serial.println(iZeitEingabe);
    setTime(iZeitEingabe);   //Neue Ardunio-Zeit zuordenen
    
  } else if (sEingabe == "zeit") {
  // Aktuelle Zeit wird über den Seriellen Port ausgegeben
    sAusgabe = "Aktuelle Zeit: ";
    sHilfe = UhrZeit(hour(),minute(),second());
    Serial.print("sHilfe: ");Serial.print (sHilfe);Serial.print ("  Länge: "),Serial.println(sHilfe.length());
    sAusgabe += sHilfe + " Uhr, ";
    sAusgabe.concat(String(now()));
    Serial.println (sAusgabe); 
  }
}

Hat jemand eine Idee wo mein Fehler liegt?
Vielen Dank für eurer Hilfe.

Auf dem UNO sollte man tunlichst die Finger von der Klasse String lassen, da diese in kurzer Zeit den Speicher fragmentiert und damit zu Fehlfunktionen führt.

Besser ist es, mit char-Arrays zu arbeiten. Hier ein paar Gedanken dazu.

Gruß Tommy

Mit Char-Strings hatte ich es auch probiert. Dabei lässt sich das OLED Display nicht initialisieren, wenn meine Funktion SerialPortLesen() zu umfangreich wird.
Mein Eindruck ist, dass ich es nicht funktioniert, wenn ich zu viel sprintf-Anweisungen oder viele Serial.print -Anweisungen verwende.

Chrishpot:

Als ich mit einem SSD1306-basierten OLED-Display gespielt habe, habe ich mir meine eigene Bibliothek geschrieben. Die umfasst zwar nicht so irre viele Funktionen und ist vermutlich deutlich unkomfortabler als fertige Bibliotheken, aber sie kann alles, was ich brauch(te) (und enthält Bilder, die nur in meiner damaligen Anwendung Verwendung fanden).

Einen nicht ganz frischen Stand habe ich angehängt. Foto einer Demo-Anwendung:

Gruß

Gregor

PS: Ich hatte damals (ist >>2 Jahre her) auch mit einer Funktion begonnen (zumindest hatte ich sie schon geplant), die Zeichenketten ausgeben sollte. Naja … manches plant man, manches macht man dann auch … manches auch nicht.

gSSD1306.h (50.5 KB)

gSSD1306.cpp (9.15 KB)

keywords.txt (272 Bytes)

Hallo

ausgaben für das Display hast Du ja keine drin. Also ich hab den Sketch von Dir mal auf meinen UNO geladen und so gelassen wie er ist. Unten bei der Zeitausgabe hast du was drin was eigendlich so nicht klappen kann denke ich.

sAusgabe.concat(String(now()));

Wenn du das weglässt klappt es doch und du bekomst die aktuelle Zeit deines Strings sAusgabe ja schon angezeigt. Lass die Zeile weg. Wenn Du den wert von now() noch mit ausgeben willst mach das in einer neuen Zeile.

Heinz

 else if (sEingabe == "zeit") {
    // Aktuelle Zeit wird über den Seriellen Port ausgegeben
    sAusgabe = "Aktuelle Zeit: ";
    sHilfe = UhrZeit(hour(), minute(), second());
    Serial.print("sHilfe: "); Serial.print (sHilfe); Serial.print ("  Länge: "), Serial.println(sHilfe.length());
    sAusgabe = sHilfe + " Uhr, ";
    // sAusgabe.concat(String(now()));  //geht so nicht
    Serial.print (sAusgabe);
    Serial.println (String(now()));
  }

gregorss: Als ich mit einem SSD1306-basierten OLED-Display gespielt habe, habe ich mir meine eigene Bibliothek geschrieben. ... Einen nicht ganz frischen Stand habe ich angehängt. ,,,,

Danke für das Angebot deiner Bibliotheken. Aber ich denke der Fehler liegt eher in meinem eignen Funktionen und nicht in der Adafruit Bibliothek.

Rentner: .... Unten bei der Zeitausgabe hast du was drin was eigendlich so nicht klappen kann denke ich.

sAusgabe.concat(String(now()));

Mein Beispiel lässt sich durch deine Änderung hin bekommen. Aber das Programm war nur ein Beispiel für mein Problem, dass mit der Stringbehandlung etwas nicht stimmt und durch Auskommentierung andere Programmteile es wieder funktioniert. Wo genau liegt der Fehler an der Zeile? Die Funktion now() gibt ein unsigned long zurück. Dies sollte sich durch die String()-Funktion konvertieren lassen. Das verketten sollte doch eigentlich auch zulässig sein.

Hallo,

Na ja , es wurde ja schon angesprochen, der UNO hat nicht viel Ramspeicher. Wenn es da eng wird können die seltsamsten Sachen passieren. Ich bin da auch schon verzeifelt an dem Thema.

Speicher kannst Du noch einsparen wenn Du bei konstanten Texten anstelle von

Serial.print("das ist ein Text");

Serial.print(F("das ist ein text"));

schreibst , dann wird kein Ram benutzt

Warum das sAusgabe.concat(String(now()) nicht geht keine Ahnung es geht halt nicht.

Heinz

Auf einem AVR (UNO/MEGA) sollte man tunlichst die Finger von der Klasse String lassen. Diese fragmentiert den RAM (wenn man nicht extrem genau weiß, was man tut) und bald geht nichts mehr.

Eine Einführung in Zeichenketten in C/C++ habe ich hier beschrieben.

Gruß Tommy

Rentner: Ich bin da auch schon verzeifelt an dem Thema.

Bitte schaue mal, ob Dir das Thema Nachlaufzeit spinnt ab ca 30 Sekunden weiterhilft.

So langsam glaube ich auch, dass der zu kleine RAM das Problem verursacht.

Ein weiteres Indiz dafür ist, dass ich mein Progrämmchen auch mal auf C-Strings umgestellt hatte. Dort ist das Problem, dass die Zeile

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64

Die Fehlermeldung ausspuckt, wenn das Programm zu groß wird. Wenn ich in der Quelltest der SSD1306-Bibliothek gehe, wird auf den ersten Blick nur in der Zeile wo der Speicher reserviert wird:

  if((!buffer) && !(buffer = (uint8_t *)malloc(WIDTH * ((HEIGHT + 7) / 8))))
    return false;

ein "false" zurückgebeben.

Ich werden dort mal noch ein bisschen rumexperimentieren.

wird auf den ersten Blick nur in der Zeile wo der Speicher reserviert wird ein "false" zurückgebeben.

Besser geht es nicht, als auszuwerten, dass malloc() NULL zurückliefert.

Dynamisch RAM zu reservieren ist generell eine kritische Sache bei kleinen Controllern. Weil die im Prinzip vor sich hinwursteln, ohne dass jemand zuschaut. Und wenn der Speicherplatz nicht reicht, reicht er eben nicht, da ist die Philosophie, dass das möglichst früh (beim Kompilieren) auffällt, oft besser.

Ich habe nun in meinem obigen Programm-Code eine FreeRam-Funktion eingebaut.

Der Freie RAM sinkt nach der Initialisierung des OLED-Displays 1209 auf 183 Byte und dann durch die Strings noch weiter auf 163 Byte. Mit dem wenigen Speicher kann ich mir vorstellen, dass es Probleme gibt.

Jetzt heißt es wohl Speicher sparen oder mehr speicher zulegen.

Auf jeden Fall vielen Dank an alle, die mich auf den richtigen Weg geführt haben.

Was würdest Du zu einer Speicher sparenden Bibliothek sagen?

Was soll auf dem Display angezeigt werden? Nur Text oder auch Grafik?