Box instabil wenn Messwert < 1, dann HTTP/1.1 422 Unprocessable Entity

ich kämpfe schon eine ganze Zeit damit, dass meine Box gelegentlich keine Daten sendet, bzw. diese Daten nicht von opensensemap angenommen werden.
Meine Box ist ausgerüstet mit Temp und Feuchtigkeit, Beleuchtungs- und UV Intensitäts-, sowie Feinstaubsensor. Nur in dieser Kombi ist sie instabil, stecke ich den Feinstaubsensor oder den Beleuchtungssensor ab ist alles gut.
Meine Beobachtung ist nun diese, sobalb einer der Messwerte für Beleuchtung oder UV oder Feinstaub kleiner 1 wird, treten die Probleme auf. Bei Beleuchtung habe ich bereits 1 hinzuaddiert, bei UV Intersität 0.01, das hat die Stuation verbessert aber nicht geheilt.

Jetzt habe ich den ähnlichen Effekt beim PM2.5 beobachtet. Anbei das interessante Log für 2 aufeinderfolgende Messungen, einmal nicht erfolgeich, danach doch. Beachtet Sensor 5e1e144261fbc3001bb5e120, das ist PM2.5. Der Wert nach dem Komma der Messwert, einmal unter 1, danach darüber.

Loop
submit values
connecting...
Connection successful, transferring...
POST /boxes/5e1e144261fbc3001bb5e11f/data HTTP/1.1
Host: ingress.opensensemap.org
Content-Type: text/csv
Connection: close
Content-Length: 177

5e1e144261fbc3001bb5e125,16.18
5e1e144261fbc3001bb5e124,29.32
5e1e144261fbc3001bb5e123,5.00
5e1e144261fbc3001bb5e122,0.10
5e1e144261fbc3001bb5e121,1.50
5e1e144261fbc3001bb5e120,0.40

HTTP/1.1 422 Unprocessable Entity
Content-Length: 131
Content-Type: application/json; charset=utf-8
Date: Wed, 22 Apr 2020 19:12:58 GMT
Deprecation-Warning: If your client supports TLS, please use https://ingress.opensensemap.org
Connection: close

{"code":"UnprocessableEntity","message":"Missing value in measurement {\"sensor_id\":\"5e1e144261fbc3001bb5e120\",\"value\":\"\"}"}
disconnecting from server.
done!

Loop
submit values
connecting...
Connection successful, transferring...
POST /boxes/5e1e144261fbc3001bb5e11f/data HTTP/1.1
Host: ingress.opensensemap.org
Content-Type: text/csv
Connection: close
Content-Length: 179

5e1e144261fbc3001bb5e125,16.17
5e1e144261fbc3001bb5e124,30.43
5e1e144261fbc3001bb5e123,1.00
5e1e144261fbc3001bb5e122,0.10
5e1e144261fbc3001bb5e121,3.59
5e1e144261fbc3001bb5e120,1.10

HTTP/1.1 201 Created
Content-Length: 27
Content-Type: application/json; charset=utf-8
Date: Wed, 22 Apr 2020 19:13:56 GMT
Deprecation-Warning: If your client supports TLS, please use https://ingress.opensensemap.org
Connection: close

"Measurements saved in box"
disconnecting from server.
done!

Liegt der Fehler in den Libraries?

Markus, danke dass du uns die Logs mit geschickt hast!

Die API können wir glaube ich ausschliessen, habe gerade deine Messungen als Test definiert und als Unit-Test funktionieren sie.

Dann hab ich nochmal in deine Logs geschaut und mir ist folgendes aufgefallen: Die Content-Length wird im ersten Fall falsch berechnet: 177 und 179 im zweiten Fall.

Deine Vermutung sollte also richtig sein: Der Fehler liegt im Sketch. Das müssen wir jetzt aber erstmal herausfinden wo genau.

Viele Grüße,
Gerald

Hallo Markus,

hat sich da etwas ergeben? Ich denke ich habe das gleiche Problem, mein Sketch ist unverändert von der Webseite, allerdings vermute ich den exotischen Bodenfeuchtesensor als Auslöser…

Viele Grüße

TK

Hallo,
nein, kein Fortschritt seit dem letzten Post.
Hast du mal versucht 1 an der Stelle im Code hinzu zu addieren, an der der Messwert gelesen wird, analog meinem anderen Post in

Gruß
Markus

Hi all,

ich glaub’ ich hab’s! Die Sensebox rechnet vor der Übertragung zum Server aus, wie viele Bytes die zu übertragenden Sensorwerte als Textstring lang sind. Im Originalsketch von der Webseite macht sie das wie folgt:

dataLength += String((int)value * 100).length() + 1; //length of measurement value + decimal digit

Das passt aber nicht immer. Liefert beispielsweise der Feinstaubsensor einen Wert zwischen 0 und 1 (was er hoffentlich oft tut), also sagen 0.8, dann passiert folgendes:

a) 0.8 wird als Integer interpretiert und das Ergebnis ist 0 (der cast zu int wird vor der Multiplikation ausgeführt)

b) 0 wird mit 100 multipliziert, es kommt wieder 0 raus

c) als String umgewandelt hat 0 die Länge eins

d) es wird noch eine Stelle für das Komma draufaddiert und die resultierende Länge ist 2

Der richtige Wert wäre aber sogar 4 für “0,80”. Somit sind durch diesen einen Sensorwert schon zwei Stellen verloren, die dann beim letzten übertragenen Sensorwert abgeschnitten werden.

Eine schöne Lösung wäre vermutlich über sprintf() oder ein Umstellen im Code, so dass zuerst die zu übertragenen Strings erzeugt werden, dann deren Länge bestimmt wird und die Strings im Anschluss übertragen werden.

Für mich zum Blumen gießen tut’s jetzt hoffentlich erstmal mit zwei fixen Nachkommastellen:

dataLength += String((int)value).length() + 1 + 2; //length of measurement value int part + decimal digit + 2x frac part

tk

Nach einer Woche: Kein Problem mehr, Box läuft wie ein Uhrwerk. Endlich!

TK

Hallo Tonio,
ähm, ich bin jetzt nicht so der Programmier-Profi. Wie bau ich den String von Dir jetzt in das Programm ein?
Habe nämlich das gleiche Problem :frowning:
Rolf

Hallo Rolf,

suche die Zeile amEnde der Funktion
void addMeasurement(const char *sensorId, float value)
in der die dataLenght errechnet wird:
dataLength += String((int)value * 100).length() + 1;
und ersetzte diese durch die Zeile von Tonio.

Bei mir sieht das dann so aus, das mag aber bei deiner Sensorkonfiguration anders sein:

   void addMeasurement(const char *sensorId, float value) {
  measurements[num_measurements].sensorId = sensorId;
  measurements[num_measurements].value = value;
  num_measurements++;
  dataLength += String(sensorId).length() + 1; //length of ID + ','
//  dataLength += String((int)value * 100).length() + 1; //length of measurement value + decimal digit
  dataLength += String((int)value).length() + 1 + 2; //length of measurement value int part + decimal digit + 2x frac part  //Aenderung von Tonio im Senseboxforum
}

Ich habe die alte Zeile auskommentiert mit den 2 Schrägstrichen davor und die neue Zeile darunter geschrieben.

Bei mir funktioniert die Box seitdem tadellos.

Gruß
Markus

Das war im Mai. Jetzt wurde es Winter und ich hatte wieder Löcher in meinen Daten. Die Temperatur ist bei mir der einzige Sensorwert, der negativ werden kann. Und da war der Hase begraben. Das Vorzeichen hing an den Nachkommastellen. Sehr seltsam. Ich kann mir nicht vorstellen, dass der Bug noch im Code ist, der aktuell verteilt wird, Winter ist ja an vielen Orten und nicht das erste Mal, aber für alle, die den Sketch von Ende 2019 in Betrieb haben:

In der Funktion writeMeasurementsToClient() habe ich den originalen sprintf() ausgetauscht gegen:

sprintf_P(buffer, PSTR("%s,%.2f\n"), measurements[i].sensorId, measurements[i].value);

Damit ist das ganze Rumgerechne mit dem temp unnötig und es bleibt mir ein Rätsel, warum so kompliziert über den Split in intPart und fracPart gegangen wurde. Es kann gut sein, da ist noch etwas vergraben, was ich gerade nicht sehe.

Anyway, bei mir tut jetzt wieder seit gestern! Auch wenn Kühlakkus zum Test herhalten mussten. :wink:

Hier die komplette ergänzte Funktion:

void writeMeasurementsToClient() {
  // iterate throug the measurements array
  for (uint8_t i = 0; i < num_measurements; i++)
  {
    //convert float to char[]
    float temp = measurements[i].value;
    int intPart = (int)measurements[i].value;
    temp -= intPart;
    temp *= 100; //2 decimal places
    int fracPart = (int)temp; 
    sprintf_P(buffer, PSTR("%s,%.2f\n"), measurements[i].sensorId, measurements[i].value);
//    sprintf_P(buffer, PSTR("%s,%i.%02i\n"), measurements[i].sensorId, intPart, fracPart);
    // transmit buffer to client
    client.print(buffer);
    DEBUG2(buffer);
  }

Viel Spaß

TK

P.S.: Diese Probleme dürften im aktuellen Code nicht mehr enthalten sein. Wäre es also sinnvoll nun nach längerer Zeit alles noch mal neu runterzuladen? Ich habe halt Sorge, dass ich meine Sensebox-ID verliere und dann meine Daten neu anfangen. Das fände ich sehr schade.

Als ich im Mai auf den Bug aufmerksam gemacht habe, war ich auch mit den Entwicklern in Kontakt, aber ich habe dann nie wieder etwas gehört.