readStringUntil('\n')

Kenn jemand eine Seite wo "readStringUntil('\x') erklärt wird.
Welche Möglichkeiten gibt es:

readStringUntil('\r') Zeile einlesen
readStringUntil('\n') Hmmm?
readStringUntil('\t') ???
.......
.......

Möchte mit dem ESP8266 bestimmte Stellen aus einer HTML Seite rausfiltern.

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <WiFiServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <SPI.h>


char ssid[] = "SSID"; //WLAN Netzwerk
char pass[] = "Passw";    //WLAN Passwort

int status = WL_IDLE_STATUS;
char server[] = "webseite.de"; //Webserver

WiFiClient client;

void setup() {
  Serial.begin(115200);

  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    //mit Wlan Netzwerk verbinden
    status = WiFi.begin(ssid, pass);

    //kurzes warten fuer Verbindungsversuch
    delay(10000);
  }
  Serial.println("Connected to wifi");
  printWifiStatus();

  Serial.println("\nStarting connection to server...");
  //Ausgabe ueber serielle Verbindung
  if (client.connect(server, 80)) {
    Serial.println("connected to server");
    client.println("GET /ESP.html HTTP/1.1");  // /index.html durch gewuenschte Unterseite ersetzen (index.html = Startseite)
    client.println("Host: webseite.de"); //Adresse des Webservers
    client.println("Connection: close");
    client.println();  //Verbindungs mit Server aufbauen und HTTP Anfrage senden
  }
}

void loop() {
  while (client.available()) {
    Serial.print("Gelesen:");
    String line = client.readStringUntil('\r');
    Serial.print(line);                            
    }    //Ausgabe des empfangenen HTML Codes



  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting from server.");
    client.stop();
    //Beenden der Verbindung
    
    while (true);
  }
}


void printWifiStatus() {
  //Ausgabe des WLan Netzwerks
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  //Ausgabe IP Adresse
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
}

Schlechter Befehl. Blockierungsfreie Programmierung ist damit nicht möglich.

Von der Verwendung ist abzuraten, lies lieber blockierungsfrei ein, jeweils bis zum Zeilenende und mach dann damit, was du brauchst.

Oder eben, bis zu dem Zeichen, mit dem das Ende der Nutzdaten gekennzeichnet ist, oder, oder, ....

Mit welchem Befehl kann ich besser ab einer bestimmten Stelle lesen?

z.B.: "das Datum" & "Test-Text"

Attempting to connect to SSID: Routername
Connected to wifi
SSID: Routername
IP Address: 192.168.xx0.x0

Starting connection to server...
connected to server
HTTP/1.1 200 OK
Date: Tue, 10 Nov 2015 18:52:14 GMT
Server: Apache
Last-Modified: Sun, 04 Sep 2011 07:55:09 GMT
ETag: "xxx725d1-855-4axxxxxxxxx"
Accept-Ranges: bytes
Content-Length: 2133
Connection: close
Content-Type: text/html

<html>
<head>
<title>Testseite</title>
</head>
Test-Text
</html>

ElEspanol:
Von der Verwendung ist abzuraten, lies lieber blockierungsfrei ein, jeweils bis zum Zeilenende und mach dann damit, was du brauchst.

Genau so. Lies Zeile für Zeile ein, und suche darin mit strcmp oder strncmp.
Das blockierungsfreie serielle Einlesen ist ein Kapitel für sich, wenn du eine komplette Zeile hast, setzt du ein Flag und aktivierst dann die Auswertungsroutine (EVA-Prinzip) oder machst das in einer if

if (serialAbfrage)
{
// hier auswerten
}

Das blockierungsfreie serielle Einlesen ist ein Kapitel für sich

Prinzipiell sehr wahr.

Wenn irgendetwas zu tun ist, solange die zu lesende Seite noch nicht fertig angekommen ist, sollte man natürlich nicht darauf warten, dass endlich das kommt auf das man wartet, (oder eventuell etwas fehlt).
Wenn nichts sonst zu tun ist, ist es egal, ob man selbst wartet oder warten lässt.

Zwar ist ein Zeilenende bei eher Nebensache, aber du willst ja nicht einen generellen html oder xml Parser schreiben, sondern auf einer bekannten Seite eine erwartete Struktur und ein paar Zeichen wiedererkennen.

Ob blockierend oder nicht, das eigentliche Problem ist wohl, zu erkennen, ob wohl was schief gegangen ist :wink:

Im Prinzip steht ja das Gesuchte immer in der gleichen Zeile und an der selben Stelle in der Zeile.

Wie sage ich per Code: nur 2. Zeile, Zeichen ab 24-32? line.substring(24, 32)?

so ähnlich.
du suchst nach "Date:" am Zeilenanfang und auf
zeileninhalt+10 steht das was du suchst

Meinst du sowas?
Ist bloß recht viel Code - wollte zwecks Speicherplatz etwas "sparen".

#include <ESP8266WiFi.h>

const char* ssid = "SSID";
const char* password = "Passw";

void setup() {
  Serial.begin(115200); 
  Serial.println(); 
  initWifi();
}

void loop() {
  //Serial.println(getTime());
  Serial.println(getTime().substring(17, 25));
  delay(5000);
}
  
String getTime() {
  WiFiClient client;
  while (!!!client.connect("Serversadresse.de", 80)) {
    Serial.println("connection failed, retrying...");
  }

  client.print("HEAD / HTTP/1.1\r\n\r\n");
 
  while(!!!client.available()) {
     yield();
  }

  while(client.available()){
    if (client.read() == '\n') {    
      if (client.read() == 'D') {    
        if (client.read() == 'a') {    
          if (client.read() == 't') {    
            if (client.read() == 'e') {    
              if (client.read() == ':') {    
                client.read();
                String theDate = client.readStringUntil('\r');
                client.stop();
                return theDate;
              }
            }
          }
        }
      }
    }
  }
}

void initWifi() {
   Serial.print("Connecting to ");
   Serial.print(ssid);
   if (strcmp (WiFi.SSID(),ssid) != 0) {
      WiFi.begin(ssid, password);
   }

   while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
   }
  Serial.print("\nWiFi connected, IP address: ");
  Serial.println(WiFi.localIP());
}

nein, das ist Vollschrott. Solche if Gräber macht man nicht. und 3 x ! (!!!) kann Probleme beim kompilieren geben und ist beim Arduino im Prinzip nie nötig.

Mach folgende Schritte, bzw. suche passenden Code und versuche ihn zu verstehen, oder besser, verstehe ihn.

  1. Nicht blockierende Routine, die zeilenweise seriell in einen char array einliest und eine eins (1) zurückgibt, wenn einen Zeilenende erkannt wurde, 0 wenn nicht.
  2. Teste das exzessiv mit dem seriellen Monitor der IDE
  3. Bau einen Timeout ein und gib bei Timeout eine zwei (2) zurück.
    Das war Lektion 1

Dann beschäftige dich mit char array und Zeigern darauf, dann damit, wie man innerhalb des Arrays Text sucht und das Ergebnis über einen Zeiger ansprechen kann.

Dann hast du eine gesunde Basis zum weiter machen.

Und das ganze ist nicht in 2 Stunden erledigt.

Wenn man nicht alles einlesen will, kann man auch erst mal Zeichen überlesen bis ein bestimmtes Zeichen kommt. Aber danach musst du in ein char Array einlesen. Dabei immer abfragen ob du noch in den Array Grenzen bist! Und wenn das Endzeichen erkannt wurde NULL/0/'\0' in den letzten Index schreiben.
Dann kann man strstr() nach einem Teil-String suchen. Der Rest kommt dann darauf an was man machen will.

Aber danach musst du in ein char Array einlesen

Vollschrott. Solche if Gräber macht man nicht

    if (client.read() == '\n') {   
      if (client.read() == 'D') {   
        if (client.read() == 'a') {   
          if (client.read() == 't') {   
            if (client.read() == 'e') {   
              if (client.read() == ':') {

Ist zwar drumherum viel Mist drin, aber wenn "\nDate:" das einzige ist, was er sucht und es nur einmal genau da vorkommt, warum nicht...

Falls der interessierende Text eventuell auf zwei Blöcke aufgeteilt ist, passt das mit dem client.available() nicht sicher...
Problematisch ist das Verhalten auch, wenn -- warum auch immer -- der Suchstring gar nicht vorkommt.
Diese zwei Probleme sind auch beim angedeuteten Änderungsvorschlag (ganze Zeile erstmal in ein char Array) zu beachten.

michael_x:
Falls der interessierende Text eventuell auf zwei Blöcke aufgeteilt ist, passt das mit dem client.available() nicht sicher...
Problematisch ist das Verhalten auch, wenn -- warum auch immer -- der Suchstring gar nicht vorkommt.
Diese zwei Probleme sind auch beim angedeuteten Änderungsvorschlag (ganze Zeile erstmal in ein char Array) zu beachten.

Klar, man sollte schon in etwa wissen, wie die Daten strukturiert sind. Wenn der Suchstring in der Zeile nicht vorkommt, muss eben der Programmierer entscheiden, was passiert. I.a.R. geht man zur nächsten Zeile. Und wenn er da auch nicht ist, zur nächsten, etc.
Das ganze sollte man dann ggf. noch mit einem Timeout versehen, falls notwendig. Und mit einer Fehlerbehandlungsroutine für Pufferüberlauf.

Natürlich ist dies nicht die Universallösung für alles, wie gesagt, es kommt auf die Datenstruktur an. Bei einem Projekt bei mir kommen die Messdaten in Frames von verschiedener Länge ohne CD oder LF, da sieht es dann wieder anders aus.
Aber hier sind es ja html Daten.

Äh - ja -ok!

Ihr seid euch sicher dass ich dies verstanden habe? :grin:

Array - da muß ich mich erst mal schlau machen.
Ich schau mich nochmal nach anderen Beispielen um.

Genau: Was passiert wenn "Date" nicht gefunden wird... daran hatte ich auch noch nicht gedacht...

Deswegen einen Timeout. Der muss aber je nach Anwendungszweck individuell gemacht werden. Bei html Seiten beispielsweise 2-5 Sek. nach dem ersten erhaltenen Zeichen. Wenn dann das gewünschte nicht gefunden wurde, gehst du in die Fehlerbehandlung.
Paralell kannst du noch nach suchen. Wenn das gefunden wird, gehst du sofort in die Fehlerbehandlung.

Dass du das jetzt noch nicht alles verstehst, ist fast klar. Aber irgendwann fällt es dir wie Schuppen von den Augen und dir wird klar, was gemeint war und vor allem warum.

Je nach Anwendung ist Fehlerbehandlung ein grosser Teil der Programmierarbeit.

Hier ist mal eine grundlegende Funktion um eine Zeile bis zu einem LF einzulesen (ohne Timeout!):

const int STRING_BUFFER_SIZE = 50;
char stringBuffer[STRING_BUFFER_SIZE];

bool readLine(Stream& stream)
{
  static int index;

  while(stream.available())
  {   
    char c = stream.read();
    
    if(c >= 32 && index < STRING_BUFFER_SIZE - 1)
    {
      stringBuffer[index++] = c;
    }
    else if(c == '\n' && index > 0)
    {
      stringBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}

Das geht so egal was die Quelle ist. Serial, SD, Ethernet. Jede Klasse die von Stream abgeleitet ist. Man muss es nur übergeben:

void loop()
{
  if (readLine(Serial) == true)
  {
  }
}

Wenn man nur Serial als Quelle hat reicht das. Hier muss man es erweitern. Wenn man "nichts da", "fertig" und "timeout" unterscheiden will, dann eben statt einem bool einen int zurückgegen

ElEspanol:
Mach folgende Schritte, bzw. suche passenden Code und versuche ihn zu verstehen, oder besser, verstehe ihn.

  1. Nicht blockierende Routine, die zeilenweise seriell in einen char array einliest und eine eins (1) zurückgibt, wenn einen Zeilenende erkannt wurde, 0 wenn nicht.
  2. Teste das exzessiv mit dem seriellen Monitor der IDE
  3. Bau einen Timeout ein und gib bei Timeout eine zwei (2) zurück.
    Das war Lektion 1

@stoni99 Also Punkt 1 aus meinem Post 8 wäre nun mit Sereniflys Code schon mal halb erledigt. Jetzt musst du ran