String trennen

Hallo Zusammen

Ich habe folgendes Problem: Ich betreibe eine 3G Wetterstation, welche ihre Daten an eine Cloud sendet, welche dann diese Daten wiederum dem Arduino schickt. Dies funktioniert auch super. Jedoch weiss ich nicht, wie ich den Datenstring aufteilen soll, um die Daten (z.B. Temperatur) darzustellen.

Immoment sieht der string, welcher ankommt so aus:

POST /?t=65.5&h=60.7&da=929.50&dr=1020.75&ta=51.5 HTTP/1.1
User-Agent: ParticleBot/1.1 (https://docs.particle.io/webhooks)
host: xx.xxxxx.ch:5045
content-type: application/x-www-form-urlencoded
content-length: 0
Connection: keep-alive

Nun soll das aber so aussehen (im Serial Monitor)

Temperatur: 65.5 
Luftfeuchte: 60.7 
Druck absolut: 929.50
Druck relativ: 1020.75
Taupunkt: 51.5

Der Rest des Strings ist unnötig.

Könnt ihr helfen?

Bis jetzt sieht mein Code so aus:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>

// Hardcode WiFi parameters as this isn't going to be moving around.
const char* ssid     = "xxxxxxxx";
const char* password = "xxxxxx";

IPAddress ip2(192,168,1,100);  //Node static IP
IPAddress gateway2(192,168,1,1);
IPAddress subnet2(255,255,255,0);



// Start a TCP Server on port 5045
WiFiServer server(5045);
   WiFiClient client;

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid,password);
  WiFi.config(ip2, gateway2, subnet2);
  Serial.println("");
  //Wait for connection
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print("Connected to "); Serial.println(ssid);
  Serial.print("IP Address: "); Serial.println(WiFi.localIP());
  
  // Start the TCP server
  server.begin();
}

void loop()   {


WiFiClient client = server.available();
  if (client) {
    if (client.connected()) {   
       client.print("HTTP/1.1 200 OK\n\nConnection: close");
    }
    if (client.available() > 0) {
      // Read incoming message
     String inChar = client.readString();
      Serial.print(inChar);
      Serial.println("");
      client.stop();
  }
}
}

Oder statt der String Klasse C Strings zu verwenden. Dann ist es trivial mit strstr() nach Teil-Strings zu suchen und den Anfang der Zahl danach mit Zeiger-Arithemtik zu bestimmen und dann an eine Konvertierungsfunktion wie atof() zu übergeben

Die String Klasse machst solche Sachen extrem umstandlich

char input[] = "POST /?t=65.5&h=60.7&da=929.50&dr=1020.75&ta=51.5 HTTP/1.1";

void setup() {
  Serial.begin(250000);
  printTagged(F("Temperatur"), F("t"), 1);
  printTagged(F("Luftfeuchte"), F("h"), 1);
  printTagged(F("Druck absolut"), F("da"), 2);
  printTagged(F("Druck relativ"), F("dr"), 2);
  printTagged(F("Taupunkt"), F("ta"), 1);
}

void loop() {}

void printTagged(const __FlashStringHelper* name, const __FlashStringHelper* tag, byte nachkomma) {
  Serial.print(name);
  Serial.print(F(": "));
  Serial.println(getTagged(input, tag), nachkomma);
}

float getTagged(char* buff, const __FlashStringHelper* tag) {
  char* p = strstr_P(buff, (const char*)tag);
  byte len = strlen_P((const char*)tag);
  float val = NAN;
  while (p) {
    char davor = *(p - 1);
    if ((davor == '?' || davor == '&') && *(p + len) == '=') {
      val = atof(p + len + 1);
      break;
    }
    p = strstr_P(p + 1, (const char*)tag);
  }
  return val;
}
Temperatur: 65.5
Luftfeuchte: 60.7
Druck absolut: 929.50
Druck relativ: 1020.75
Taupunkt: 51.5

Ich denke es ist einfacher nach den einzelnen Feldern zu suchen, statt den String zu zerlegen.

Der obige Kode sucht den Namen des Parameters und schaut ob ein & oder ? davor und ein = direkt dahinter steht.
Wenn das der Fall ist, greift es sich die float Zahl, die hinter dem = anfängt.
Wenn nicht, sucht es ein Zeichen hinter der letzten Fundstelle weiter.

Ist es nicht einfacher, mit strtok und atof drüber zu gehen?
Das Datentelegramm ist doch bestimmt immer gleich zusammengesetzt

siehe auch hier

Dann schreib es doch mal einfacher mit strtok hin. :wink:

Bin leider erst heute abend dazu gekommen, aber so müsste es gehen:

float t,h,da,dr,ta;

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

Serial.println("strtok");


char telegram[]="POST /?t=65.5&h=60.7&da=929.50&dr=1020.75&ta=51.5 HTTP/1.1";
Serial.println(telegram);

char* ptr = telegram;

t=atof(strtok(ptr+9, "&"));
h=atof(strtok(NULL, "&")+2);
da=atof(strtok(NULL, "&")+3);
dr=atof(strtok(NULL, "&")+3);
ta=atof(strtok(NULL, "&")+3);

Serial.println(t);
Serial.println(h);
Serial.println(da);
Serial.println(dr);
Serial.println(ta);

}

void loop() {
  // put your main code here, to run repeatedly:

}

Getestete, endgültige, vereinfachte Fassung:

float t,h,da,dr,ta;

void setup() {
Serial.begin(9600);
char delimiter[] = "&=";
char* ptr;
Serial.println("strtok");


char telegram[]="POST /?t=65.5&h=60.7&da=929.50&dr=1020.75&ta=51.5 HTTP/1.1";
Serial.println(telegram);
Serial.println();
ptr = strtok(telegram, delimiter);  // Anfang verwerfen
t = atof(strtok(NULL, delimiter));  // t extrahieren
ptr = strtok(NULL, delimiter);      // Bezeichnung verwerfen
h = atof(strtok(NULL, delimiter));  // h extrahieren
ptr = strtok(NULL, delimiter);      // etc.
da = atof(strtok(NULL, delimiter));
ptr = strtok(NULL, delimiter);
dr = atof(strtok(NULL, delimiter));
ptr = strtok(NULL, delimiter);
ta = atof(strtok(NULL, delimiter));

Serial.print("t\t");   Serial.println(t);
Serial.print("h\t");   Serial.println(h);
Serial.print("da\t");     Serial.println(da);
Serial.print("dr\t");    Serial.println(dr);
Serial.print("ta\t");    Serial.println(ta);
}

void loop() {
  // put your main code here, to run repeatedly:

}

Schöner finde ich deine Version nicht, ich empfinde das als unübersichtlicher und schwerer zu lesen.

Auch wird nicht der vorgegebene Output erzeugt (Feldnamen und Nachkommastellen),
ein Vertauschen der Parameter ändert das Ergebnis, das Fehlen eines Parameters wird ignoriert.

Welche Version findest du besser und aus welchen Gründen?

Die zweite ist besser, da die Länge der Feldnamen nicht berücksichtigt werden müssen. Die Nachkommastellen sind, wie sie sind. Ist ja float, die standardmäßig bei Serial.print halt mit 2 Stellen nach dem Komma ausgegeben.
Die Annahme in Post 4 war ja von mir, das das Format so bleibt. Daher mein Vorschlag mit strtok drüber zu gehen.
Ob nun schön oder schwerer lesbar ist Ansichtsache. Du wolltest es ja sehen wie.

ElEspanol:
Die zweite ist besser, da die Länge der Feldnamen nicht berücksichtigt werden müssen.

Bei dir werden die Feldnamen bei der Eingabe ignoriert und die Labels bei der Ausgabe nicht wie gewünscht ausgegeben.

ElEspanol:
Die Nachkommastellen sind, wie sie sind. Ist ja float, die standardmäßig bei Serial.print halt mit 2 Stellen nach dem Komma ausgegeben.

Was hat das mit der Vorgabe / Aufgabenstellung zu tun?

arduino_weather:
Nun soll das aber so aussehen (im Serial Monitor)

Temperatur: 65.5 

Luftfeuchte: 60.7
Druck absolut: 929.50
Druck relativ: 1020.75
Taupunkt: 51.5

ElEspanol:
Die Annahme in Post 4 war ja von mir, das das Format so bleibt.

Na hoffentlich wird der Umstand dann gut dokumentiert und von allen Clients eingehalten.
Ich würde möglichst wenig Annahmen über die hereinkommenden Daten machen,
da sich deren Formattierung nur selten völlig kontrollieren lässt.

Ja, die Labels. Das hab ich einfach nicht hingekriegt, dass die im Serial Monitor richtig ausgegeben werden.