Messwerte erfassen und auswerten

Hallo,

ich habe ein Problem wo ich mir schon eine ganze Weile den Kopf zerbreche und irgendwie nicht weiter komme.
Ich habe hier einen Niederschlagssensor der mir die aktuelle Niederschlagsmenge in mm/min als mA ausgibt. Das erfassen des Wertes ist nicht das Problem.
Jetzt habe ich zwar den aktuellen Wert der Niederschlagsmenge, nur würde ich es toll finden das ich dann am Ende des Tages/Regens eine Statistik habe, wo ich sehen kann wie viel Niederschlag im laufe des Tages runtergekommen ist.

Wie würdet ihr da weiter vorgehen? Ich denke mal nicht das diese Berechnung auf dem Controller, in meinem Fall ein ESP32, stattfinden kann. Deshalb übertrage ich den aktuellen Messwert auch schon per mqtt an meinen Broker im lokalen Netzwerk.
Laut meinem Verständniss müsste ich ja irgendwie, um so genau wie möglich zu werden, einem sekündlichen Mittelwert bilden aus dem dann einen Minütlichen und das dann irgendwie mit timestamp in eine Datenbank schreiben, damit ich daraus dann später meine Statistik erstellen kann.

Das hört sich für mich aber nach einer verdammt grossen Menge an Daten an. Gibt es da vielleicht einen eleganteren Weg den ich übersehe?

Gruß Michael

Der ESP32 ist schon sehr leistungsfähig und kann diese kleine Berechnung sicher erledigen. Wenn du die Daten schon im ESP32 zur Verfügung hast, dann einfach damit rechnen, das schafft der schon.

Nö.
Die Frage ist, was Du als "aktuellen" Niederschlagwert definierst.

Mach mal nen Kurzsketch, wo in irgendeiner Var zu sehen ist, was Du tatsächlich hast.

wenn du jede Minute einen Wert abfragen kannst
kannst du den ganzen Tag (also 60 x 24 = 1440 mal) den erhaltenen Wert einfach zu deinem Tageszähler/heute dazuzählen - dann hast du bis um Mitternacht deine Niederschlagsmenge.
Um Mitternacht kopierst du dir den heute Wert in eine variable Gestern und
setzt deinen heute Zähler auf null und fängst somit wieder von vorne an.
Dann weist du wie viel Niederschlag heute war - wie viel Gestern den ganzen Tag war.

Der "aktuelle" Niederschlagswert, ist die Niederschlagsmenge in mm/min die der Sensor gerade in nahezu Echtzeit ausgibt. Es ist ein Analoger Wert zwischen 4 und 20mA den ich mit Hilfe einer kleinen Schaltung in eine Spannung umwandle und diese dann am ADC messe und auswerte.

Hier ist mein Code, habe die ganze Netzwerk Geschichte eingekürzt.

#include <Arduino.h>
#include <config.h>
#include <RunningMedian.h>
#include <Smoothed.h>
#include <PubSubClient.h>
#include <WiFi.h>

#define ANALOG_GPIO 35

#define outTopicPrecipitation "nssensor/precipitation/mmmin"

#define DEBUG

#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTDEC(x, DEC) Serial.print(x, DEC)
#define DEBUG_PRINTLN(x) Serial.println(x)
#define DEBUG_PRINTLNDEC(x, DEC) Serial.println(x, DEC)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTDEC(x, DEC)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINTLNDEC(x, DEC)
#endif

WiFiClient espClient;
PubSubClient client(espClient);

Smoothed <float> mySensor;
Smoothed <float> mySensor_smoothed;

RunningMedian sensorValue = RunningMedian(50);
RunningMedian mA = RunningMedian(30);
RunningMedian prec = RunningMedian(10);

String clientId = "NSS-";

int raw = 0;
float niederschlag = 0.0;

void setup_wifi()
{
 ....
}

void setup() {
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  //mySensor.begin(SMOOTHED_AVERAGE, 10);
  mySensor.begin(SMOOTHED_EXPONENTIAL, 1);
  mySensor_smoothed.begin(SMOOTHED_EXPONENTIAL, 1);
  mySensor.clear();
  mySensor_smoothed.clear();
}


void reconnect()
{
  ...
}


void sendMqtt()
{
  if (client.connected())
  {
    client.publish(outTopicPrecipitation, String(niederschlag, 3).c_str(), true);
  }
}

float mapf(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

float precipitation(float sensorValue)
{
  if (sensorValue >= 4.5 && sensorValue <= 8.0){
    //Serial.println("4-8 leichter Nieselregen");
    return (0.0025 * sensorValue - 0.01);
  }
  else if (sensorValue > 8.0 && sensorValue <= 12.0){
    //Serial.println("8-12 Regen");
    return (0.0225 * sensorValue - 0.17); 
  }
  else if (sensorValue > 12.0 && sensorValue <= 16.0){
    //Serial.println("12-16 starker Regen");
    return (0.225 * sensorValue - 2.6); 
  }
  else if (sensorValue > 16.0){
    //Serial.println(">16 sehr starker Regen");
    return (2.25 * sensorValue - 35);
  }
  else {
    //Serial.println("< 4.5 Kein Niederschlag");
    return 0.0;
  }
  //Serial.println("< 4.5 Kein Niederschlag");
  return 0.0;
}

void loop() {

  if (!client.connected())
  {
    reconnect();
  }
  client.loop();

  raw = analogRead(ANALOG_GPIO); 

  //#### Analogwert mehrmals glätten, da er stark verrauscht ist
  mySensor.add(raw);
  float smoothedSensorValueExp = mySensor.get();
  mySensor_smoothed.add(smoothedSensorValueExp);
  float mySensor_smoothedValueExp = mySensor_smoothed.get();
  
 
  sensorValue.add(mySensor_smoothedValueExp);
  mA.add(mapf(sensorValue.getMedian(), 580, 3770, 4, 20)); //mA errechnen und dem Buffer hinzufügen
  prec.add(precipitation(mA.getMedian())); //niederschlagsmenge dem Buffer hinzufügen
  
  niederschlag = prec.getMedian(); //Niederschlagsmenge aus Buffer errechnen in mm/min Wert zwischen 0.0 - 10.00 mm/min
  sendMqtt(); //wert per mqtt senden
 
  // Serielle Ausgabe für SerialPlot
  Serial.print(raw);
  Serial.print(",");
  Serial.print(sensorValue.getMedian(), 2);
  Serial.print(",");
  Serial.print(mA.getMedian(), 2);
  Serial.print(",");
  Serial.println(niederschlag, 3);
}

Ich frage den Wert kontinuierlich ab, habe dadurch mehrere Messwerte in der Sekunde die ich dann noch filtere, da der analoge Wert doch sehr am Rauschen ist.
Ich müsste mir die Daten einer ganzen Minute speichern und daraus den Mittelwert bilden, und da weiß ich nicht ob der ESP das lange mitmachen wird, da da ja doch einiges zusammenkommen dürfte.

bau dir einen ein Minuten timer und speichere es dir einmal in der minute weg

pseudo code

static uint32_t previousMillis = 0;
uint32_t currentMillis=millis();
if (currentMillis - previousMillis>60000UL)
{
  heute += niederschlag;
  previousMillis=currentMillis();
  Serial.println("... und merken...");
}

wie du zum "niederschlag" kommst ist doch egal bzw. das kannst machen wie du willst.
Entweder nimmst einen aktuellen Wert (der soll ja mm/min sein) oder deinen schon vorhanden Mittelwert.

Ich muss mal versuchen ob es der ESP schafft mir einen Mittelwert aus einer Minute zu errechnen.

Ich kann nicht einfach jede Minuten den Wert abspeichern. Dieser schwankt natürlich innerhalb einer Minute sehr stark da es nie so konstant regnet. der Wert von dem Sensor gibt den Wert an, wie es wäre wenn es exakt eine Minute so weiter regnen würde. (Das is aber natürlich nie der Fall) Deshalb muss ich mir wohl einen Mittelwert aus der letzten Minute erstellen.

Kenne mich mit C leider nicht so besonders gut aus, gibt es "flexible" Arrays in C?
Oder muss man die Grösse des Arrays zwingend immer vorher wissen? Ich weiß natürlich nicht genau wie viele Messwerte in einer Minute zusammenkommen.

du machst ja schon allerlei smooth, median , map ... ich mein auf einen gültigen Wert wirst doch hoffentlich kommen... sonst hats ja sowieso was mit deinem Sensor (oder deiner Programmlogik)

Ich bin mir sicher, der ESP32 kann das, daher ist eher die Frage, ob Du das kannst.

Programmiert wird in C++ und ich kann mir nicht vorstellen, wozu flexible Felder notwendig wären.

Du scheinst eine Art Durchflußsensor zu haben. Den fragst Du beispielsweise jede Sekunde ab. Den Wert addierst Du zu einer Minuten-Summe, die Du nach 60 Messungen durch 60 teilst, dann hast Du den Niederschlag der letzten Minute. Diesen Wert kannst Du dann 1440 mal zur Tages-Summe addieren, die Du dann an das Netzwerk überträgst.

Das benötigt eine Handvoll Variablen, kein flexibles Feld.

Natürlich kannst Du auch häufiger messen und mehr Informationen an das Netzwerk übertragen, das benötigt dann eben ein paar mehr Variablen, aber immer noch kein flexibles Feld.

Ich komme ja auf einen gültigen Wert, aber nicht auf einen Wert der für eine komplette Minute gültig ist. Mit dem ganzen Sketch komme ich auf ca. 420 werte die Sekunde. Da jetzt einfach jede Minuten einen Wert von zu nehmen wäre nicht repräsentativ für die letzte Minute.
Vielleicht sollte ich den Sketch erstmal umbauen und das ganze erstmal auf 2-4 Messungen die Sekunde reduzieren. Dann hätte ich nur ca. 250 Werte aus den ich ein Mittelwert bilden muss.

Ja, auf die Idee, dass ich den Sensor eventuell einfach weniger abfrage bin ich auch gerade gekommen. Das macht die Sache auf jeden Fall um einiges einfacher.
Ich werde einfach nochmal von ganz vorne anfangen.

Vielen Dank für eure Antworten.

Egal wie häufig Du abfragst, Du solltest es in einem festen Zeitraster tun. Wenn Du dann n Messungen machst, benötigst Du kein Feld mit n Elementen, sondern nur eine Summe, die Du nach den n Messungen durch n teilst.

summe += aktuellerMesswert;
...
statistischerWert = summe / anzahlMessungen;
summe = 0;

Danke, es auf dies Weise zu lösen bin ich gar nicht gekommen. Ich habe da wohl wieder zu kompliziert gedacht.

Vielen Dank! Jetzt habe ich wieder was zu tun heute Abend. :smiley:

Da möchte ich nicht widersprechen :slightly_smiling_face:

Wird schon!

absolut?
Ich gehe mal von relativ aus.

Also mache jede Sekunde einen Wert in einen Ringpuffer und bilde am Ende der Minute einen Durchschnitt. (60 Werte)
Addiere jede Minute in einen Stundenwert. Macht 24 Stundenwerte. Die in einem Array am Ende des Tages zusammenaddiert, dann hast Du:
letzte Minute, letzte Stunde, letze 24 Stunden, letzten Tag.
Wenn Du willst auch noch 31 Tage.
Das sind dann insgesamt irgendwo um die 125 Werte.
Für jeden Wert 4 Byte.
Da lacht sich der ESP drüber eher tot, als Du den vollschreibst. :wink:

Es gibt den Variablentyp unsigned long. Der kann Zahlen bis 2 hoch 32 = 4.294.967.296

Wenn du in einer Variable vom Typ unsigned long die 12-bit-AD-Wandler.Meßwerte aufsummierst
dann kannst du da 12 bit = 2 hoch 12 = 4096

4.294.967.296 / 4096 = 1.048.576 eine Million Messwerte addieren bevor die Zahl zu groß wird.
Das heißt du könntest die Messwerte bei hoher Geschwindigkeit aufaddieren und dann durch die Anzahl Meßwerte teilen.

Dabei ist wichtig, dass die Messungen über einen präzise konstant gehaltenen Zeitraum aufsummiert werden oder du musst die Mikrosekunden zum Meßstart und Meßende mit erfassen. Dann ist eine präzise Berechnung mm/min möglich.

Die Mikrosekunden werden auch in Variablen vom Typ unsigend long gespeichert

Die Berechnung wird mit hochskalierten Integerwerten präziser als wenn man variablen vom Typ float verwendet.

Beispiel:
Originalwert 0,04593

mit der Zahl 1 Million hochskalierter Wert 45930,000000
Das Komma wurde durch die Multiplikation mit 1.000.000 um 6 Stellen nach links verschoben.
vgs