Frage zu Serial.find()

Hallo,

ich habe meinen Mega auf der Serial1 mit meinem neuen Spielzeug (ESP8266 WiFi-Modul) gekoppelt. Wenn ich einen AT-Befehl raus sende, dann bekomme ich auch wieder eine Antwort zurück.

Ich benutze jetzt die Serial1.find Funktion, um abhängig von der Antwort etwas zu steuern Also in etwa so:

   if(!Serial1.find("OK")) {
      Serial.print ("Error");
      }
   else
      {
       Serial.print ("OK");
      }

Kann ich jetzt aber noch die empfangenen Daten auf der debug-Schnittstelle ausgeben? Ein hintergeschobenes

  while (Serial1.available()) {
    Serial.write(Serial1.read());
}

gibt nichts mehr aus, da vermutlich der Puffer schon wieder leer ist.

Als einzige Lösung sehe ich, dass ich nicht mit der find-Funktion arbeite, sondern nur das Serial-read verwende, die Bytes in einem String sammle, und dann den String nach meinem Suchstring parse.

Gehts irgendwie besser? ich meine: Den Serial.find Befehl zu verwenden finde ich schön, aber wie kann ich dann noch einmal den Empfangspuffer auslesen, um an die Daten zu kommen?

gruß/hk007

Als einzige Lösung sehe ich, dass ich nicht mit der find-Funktion arbeite, sondern nur das Serial-read verwende, die Bytes in einem String sammle, und dann den String nach meinem Suchstring parse.

Ich würde es machen, in ein Chararray einlesen am Ende ein '/0' setzen. Dann kann z.b mit strstr gesucht werden.

hk007: Den Serial.find Befehl zu verwenden finde ich schön, aber wie kann ich dann noch einmal den Empfangspuffer auslesen, um an die Daten zu kommen?

Wie du sagtest: in einen eigenen Puffer einlesen und dann Parsen. Suchen nach einem Sub-String an einer beliebigen Stelle geht mit strstr()

Das hört sich erst mal nicht so schön an, da man natürlich mehr Speicher braucht, aber so schlimm ist das meistens auch wieder nicht.

OK,
hilft wohl nicht.

Aber ihr wisst ja aus der Vergangenheit: Ich und die Strings… :confused:

da man natürlich mehr Speicher braucht

Wenn wirklich am RAM für eine Zeile gespart werden soll, mach das find() doch in der Umkopier-Funktion mit:

static boolean found;

while (Serial1.available()) {
    char c= Serial1.read();
    Serial.write(c);
    if (findOK(c) == true) found = true;
}


boolean findOK(char c)
{
   //  liefert true, wenn ein 'K' direkt nach einem 'O' erkannt wurde
   static char prev;
   if ((prev == 'O') && (c == 'K') ) { prev = 0; return true; }
   prev = c;
   return false;
}

Strings braucht man wirklich nicht :wink:

Hallo nochmal,

in der Zwischenzeit hat sich noch eine weitere Frage aufgeworfen: (Ich mach mal jetzt keinen neuen Thread auf. Oder soll ich?)

Der serielle Puffer ist ja nur 64 Byte groß. Ich habe jetzt den Fall, dass ich eine Antwort bekomme, die mehr Zeichen hat. Ok dachte ich mir, dann ändere ich halt die Puffergröße in der HardwareSerial.cpp auf 256 Byte Die 192 Byte sind bei einem Mega ja noch drin.

Pustekuchen.

Kostet mich 1536 (8x192) Byte!!! Wieso??

Meine Vermutung: Der Mega hat 4 serielle Schnittstellen. Jeweils 1x RX und 1xTX-Puffer mal 4 --> 1536 Byte mehr???

Wäre schön, wenn man das für jede serielle einzeln angeben könnte. Noch besser, wenn man es aus dem AnwenderProgramm heraus steuern könnte...

Aber ihr wisst ja aus der Vergangenheit: Ich und die Strings...

Den Code um von Serial in einen Puffer einzulesen habe ich hier schon zig mal gepostet :)

Ich habe jetzt den Fall, dass ich eine Antwort bekomme, die mehr Zeichen hat.

Die serielle Übertragung ist sehr langsam. Bei 9600 Baud dauert ein Zeichen etwa 1ms. Selbst bei 115200 Baud sind es ca. 90µs. Wenn du den Puffer nicht schnell genug auslesen kannst ist dein Programm schlecht geschrieben

Kostet mich 1536 (8x192) Byte!!! Wieso?? Wäre schön, wenn man das für jede serielle einzeln angeben könnte.

Hast du mindestens 1.5.6. installiert?

Da hat man das glaube ich endlich ausgebessert:

Put each HardwareSerial instance in its own .cpp file in order to save memory for unused UARTS

Hi Serenifly,

ich arbeite mit 1.05

Wenn du den Puffer nicht schnell genug auslesen kannst ist dein Programm schlecht geschrieben

Das mag schon sein... Aus deinen Ausführungen höre ich raus, dass der Arduino schneller als die serielle mit 115200 Baud ist.

Ich zeig dir hier mal kurz den entsprechenden Auszug:

#define Debug Serial
#define ESP8266 Serial1

void setup() {
  Debug.begin(115200);
  ESP8266.begin(115200);
  Debug.println ("Programmstart");
  delay(500);
  }

void loop() {
....

  ESP8266.println("AT+CWLAP"); 
  Debug.println(">>>Liste der vorhandenen APs"); 
  delay(3000);
   while (ESP8266.available()) {
    Debug.write(ESP8266.read());
   } 
...

Wenn ich das "delay(3000)" weglasse, dann kommt gar nichts :-( Liegt wohl daran, dass das Modul nach meiner Anweisung gut 2 Sekunden zum Scannen der APs braucht, und dann erst die Antwort schreibt. Und wenn ich 3 Sekunden warte, bis ich auslese, dann ist er natürlich übergelaufen. Und ohne das delay läuft das Programm wohl gar nicht in die while-Schleife. Die Frage ist jetzt, wie kann ich das Programm anhalten, bis das erste Zeichen kommt, und dann schnell genug auslesen, dass der Puffer nicht überläuft.

Ja, das ist Murks. Bei while nimmst du einfach mal so an dass alle Daten sofort da sind. Da ist nicht der Fall und die Schleife bricht ab bevor überhaupt alles komplett gesendet wurde.

Serenifly: Ja, das ist Murks. Bei while nimmst du einfach mal so an dass alle Daten sofort da sind. Da ist nicht der Fall und die Schleife bricht ab bevor überhaupt alles komplett gesendet wurde.

Was wäre dann besser?

Mit serialEvent()? In der Subroutine ein Bit setzen, wenn alles gelesen ist, und dann im Hauptprogramm warten bis das Bit kommt?

Hast du am Ende des Strings ein bestimmtes Zeichen? Vielleicht sogar ein Linefeed und/oder Carriage Return?

Dann ist das trivial und du musst nur solange Einlesen bis das da ist. Dazu lässt man einfach loop() durchlaufen. Und dann halt solange nicht der ganze String da ist, erst mal keine Anforderung absenden.

So einfach ist es nicht.
Manchmal kommen auch mehrere Zeilen, durch \r \n getrennt. Im Anhang ein Beispiel als Screenshot von Hterm.

Und immer durch loop laufen gefällt mir auch nicht so besonders.

Ich baue da so eine Initialisierungsroutine auf, bei der mehrere Befehle hintereinander an das Modul gesendet werden. Das ist dann eher wie eine Schrittkette.
Ich bräuchte da was wie:
→ Befehl an Modul absenden
→ Warten auf erstes Zeichen
→ Alle Zeichen einlesen
→ Wenn eine gewisse Zeit nichts mehr kommt, dann im Programmablauf weitermachen.

ESP8266.jpg

hk007:
Manchmal kommen auch mehrere Zeilen, durch \r \n getrennt. Im Anhang ein Beispiel als Screenshot von Hterm.

Hängt das nicht vom Befehl ab den du schickst wie viele Zeilen als Antwort kommen? Man kann auch die Linefeeds zählen wenn man immer weiß wie viele man erwartet.

Und immer durch loop laufen gefällt mir auch nicht so besonders.

Das ist das beste was man machen kann, da man dann zwischendurch auch andere Dinge erledigen kann. Man kann ja ständig eine Funktion aufrufen um es etwas zu kapseln

→ Alle Zeichen einlesen

Man muss immer irgendwie wissen wann es fertig ist.

Du kannst es auch beim Einlesen in einen Puffer über die Zeit machen, wenn es gar nicht anders geht. Mit millis() messen wie lange man schon einliest und irgendwann aufhören.

Wie wäre es so, das modul wird nur einmal initalisiert.

Die loop läuft ja, und irgendwann wird ESP8266.available wahr. Dann die Daten in ein Char Array schreiben. Wenn du im Programm das Modul nochmals initalieren must einfach ESP_init wieder auf true setzen

boolean ESP_init=true;

void loop() {
....

 if (ESP_init) {
   ESP8266.println("AT+CWLAP"); 
  Debug.println(">>>Liste der vorhandenen APs"); 
      ESP_init=false;
                }
                
   while (ESP8266.available()) {
    Debug.write(ESP8266.read());
   } 
...

Man müsste dann noch einen Timeout definieren, der anzeigt wann das Paket fertig ist. Wenn kein Ende Zeichen oder ähnliches vorkommt.

Ich hab mir das noch mal genau angesehen, was vom Modul zurückkommt. Gleich nach der Anforderung kommt der Befehl noch mal als Echo zurück, und dann dauert es ca. 2 Sek, bis er die APs gescannt hat, und dann kommt das Ergebnis.

Init nur einmal. Da habt ihr recht. Drum kommt das ganze auch in die Setup. Serialevent kann man nicht brauchen, da die nur am Ende von loop() aufgerufen wird.

Ich werd das jetzt so machen:

Befehl absetzen -> Ne Schleife von x-Sekunden aufmachen, und in der Schleife alles von der seriellen lesen und ausgeben -> Nach der Zeit geht es weiter zum nächsten Schritt.

Allgemein gefallen mir Zeiten auch nicht, aber wenn ich kein sauber erkennbares Ende habe, dann gehts wohl nicht anders. Es kommt zwar immer ein OK, wenn der Befehl erfolgreich ausgeführt wurde, aber bei Problemen kommen auch immer andere Antworten. (Error, "no this fun"...)

Mein Ansatz hätte den Vorteil das du das Modul nicht nur beim Start neu initialsieren kannst.

Dein Gedanke dies mit einer festen Schleife zu machen kann funktionieren, finde ich aber nicht ideal.

Es kommt zwar immer ein OK, wenn der Befehl erfolgreich ausgeführt wurde, aber bei Problemen kommen auch immer andere Antworten. (Error

Um das geht es doch. Du sendest einem Befehl, und es kommt entweder "OK" oder "Error" zurück. Das Problem ist, das nur ein Zeichen (Char) zum Prüfen auf ein Ende Zeichen hast.

Du sendest einem Befehl, und es kommt entweder “OK” oder “Error” zurück.

Nein, das hast du falsch verstanden. Ich habe auch schon “fail” oder “no this fun” bekommen.
Wer weiss, was das Ding noch alles auf Lager hat :slight_smile:

Dein Gedanke dies mit einer festen Schleife zu machen kann funktionieren, finde ich aber nicht ideal.

Ich finde es eigentlich praktikabel.

So siehts jetzt aus, und läuft auch wie erwartet.

  ESP8266.println("AT+CWLAP"); 
  Debug.println(">>>Liste der vorhandenen APs"); 
  target = millis() + 3000;    // 3 Sekunden warten
  while (millis() < target) {
   while (ESP8266.available()) {
    Debug.write(ESP8266.read());
   }  
  }