Niederschlag messen, 2x OLEDs, BH1750, und DS3231 RTC

Hallo Arduino-Kameraden,
bei meinem Projekt möchte ich an mehreren Stellen Änderungen/Verbesserungen vornehmen. Für die "Hartgummilutscher" (Zit. Terrence Hill) aus der Programmier-Szene: ich bin noch nicht lange dabei. xD

Am Arduino Uno ist eine Regenwippe (von einer alten Wetterstation), zwei 1.3" OLEDs, das DS3231 RTC Modul und ein BH1750 Lichtsensor angeschlossen. Mit Ausnahme des Regensensors nutzen die Komponenten I2C, ihre Daten sind abrufbar. Weitere Sensoren sind zwecks Übersicht herausgenommen. Der Umstieg vom LCD auf zwei OLEDs ist nach vielem Herumgefrickel geglückt - ich habe nicht wie eigentlich erworben die SSD1306, sondern die SH1106 Controller. Der Verzicht auf die Funktion "u8g.begin()" im Setup löste das Problem sporadisch auftretender Hieroglyphen. Die Drahtbrücke eines Displays wurde von mir mit einem 470k OHM Widerstand geändert und die Datei "u8g_com_arduino_ssd_i2c.c" wie von olikraus im englischsprachigem Forum beschrieben angepasst:

uint8_t I2C_SLA = 0x3d2; // alternativ 0x3c2;

Beide OLEDs können jetzt im Sketch angesprochen werden. Ich hoffe nicht, dass mir die Änderung der Datei für zukünftige Projekte ein Bein stellt. Immerhin, es funzt. Die Sensorenmessung findet mit millis etwa minütlich statt, auf die Sekunde kommt es da nicht an. Aufgrund regelmäßiger Stromausfälle hatte ich daraus die Laufzeit des Arduino berechnet, was auch nicht sonderlich präzise ist aber als Information reichte.

Fragen
1.) Kann die Schriftart der U8glib zentriert werden? Im Netz stieß ich auf den command "setPrintPosCenter", was der Compiler aber nicht akzeptiert.
2.) Der Regensensor zählt zwar die Wippen-Bewegungen, allerdings nur dann, wenn der Arduino den Pin ständig abfragt. Wegen der Bildschirmanzeigen -hier auch noch verkürzt- kann er das jedoch nicht. Mit der <INTERVAL.h> oder der Nachtwächter-Funktion von "agmue", "combie" & Co. komme ich jedoch nicht zurecht. Daraufhin habe ich alle Anzeigen in die Funktion "LCDactivityStats" ausgegliedert - ist zwar im Endergbnis der gleiche Käse, lässt sich aber zu Testzwecken schnell aus dem Sketch völlig ausklammern.
3.) In der Vergangenheit hatte ich den Niederschlag pro Tag, Woche und Monat akkumuliert und per RTC um Mitternacht bzw. zu Wochenbeginn genullt. Da ein Motorkugelventil die Wasserzufuhr einer Gardena-Bewässerungsanlage unterbinden soll, ist diese Methode ziemlich unprofessionell: Besser wäre die fortlaufende Zählung der Regenmenge aus den letzten 24 Stunden, 3 Tagen und 7 Tagen - nur: wie wird das programmiert? Der Monatswert ist nicht so wichtig und dient eher der allgemeinen Auskunft für die Gesamtmenge des Niederschlages aus dem aktuellen Monat.

#define SCREEN_MILLISECONDS 8000
#define DS3231_I2C_ADDRESS 0x68
#define RainPin 2

#include <Wire.h>              // DS3231 RTC, BH1750, RainGauge
#include <BH1750.h>            // BH1750 Light Sensor
  BH1750 lightMeter(0x23);
  uint16_t lux;

#include "ds3231.h"            // DS3231 RTC with AT24C32 Chip
#include "Wire.h"              // https://www.maximintegrated.com/en/products/digital/real-time-clocks/DS3231.html
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  char recv[128];
  byte decToBcd(byte val) {    // Convert normal decimal numbers to binary coded decimal
    return ((val / 10 * 16) + (val % 10));
  }
  byte bcdToDec(byte val) {    // Convert binary coded decimal to normal decimal numbers
    return ((val / 16 * 10) + (val % 16));
  }
  float temperature_rtc;       // Temperatur RTC-Modul

#include "U8glib.h"
  U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST); // Dev 0, Fast I2C / TWI
  extern uint8_t I2C_SLA;      // Changed I2C entry at > Arduino\libraries\U8glib\src\clib

bool RainHigh=false;           // RainGauge
  const float LowAmt = 0.005;
  const float HiAmt = 0.005;
  float rainLast24h = 0.000;
  float rainLast3Days = 0.000;
  float rainLast7Days = 0.000;
  float rainMonth = 0.000;

unsigned long resetLoops = 1;  // Counting overall loops startig with every reset

unsigned long currentMillisMeasurements = millis(); // Sensor Measurement and Runtime
long previousMillisMeasurements = 0;
long intervalMeasurements = 60000;
unsigned int totalMeasurements = 0;


void setup(void) {
  Serial.begin(9600);
  Wire.begin();                // Initialize the I2C bus for DS3231, BH1750, RainGauge

  // UHRZEIT NEU EINSPIELEN
  //    setDS3231time(15,2,10,4,28,2,18);       // e.g. Mittwoch, 28. Februar 2018, 10:02:15 Uhr

  // BH1750: begin returns a boolean that can be used to detect setup problems
  if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE))
    Serial.println(F("BH1750 Advanced begin"));
  else
    Serial.println(F("Error initialising BH1750"));

  pinMode(RainPin, INPUT);     // RainGauge
   if (digitalRead(RainPin)==HIGH)
 RainHigh=true;
   else
 RainHigh=false;

  u8g.setFont(u8g_font_6x13);  // OLED Font 9px height

} // END SETUP

Die Schleife sieht dann folgendermaßen aus:

void loop(void) {
  currentMillisMeasurements = millis(); // Calculation for sensorMeasurements-function
  
  // SENSOR-DATENERHEBUNG
  if(resetLoops == 1 || currentMillisMeasurements - previousMillisMeasurements > intervalMeasurements) {
    sensorMeasurements();
    previousMillisMeasurements = currentMillisMeasurements;
  }

  // Niederschlag
  if ((RainHigh==false)&&(digitalRead(RainPin)==HIGH)) {
    RainHigh=true;
    rainLast24h+=LowAmt;
    rainLast3Days+=LowAmt;
    rainLast7Days+=LowAmt;
    rainMonth+=LowAmt;
  }
  if ((RainHigh==true)&&(digitalRead(RainPin)==LOW)) {
    RainHigh=false;
    rainLast24h+=HiAmt;
    rainLast3Days+=HiAmt;
    rainLast7Days+=LowAmt;
    rainMonth+=HiAmt;
  }

  LCDactivityStats();                   // OLED-INFORMATIONSSCHLEIFE

  resetLoops++;
} // END LOOP


void sensorMeasurements() {
  lux = lightMeter.readLightLevel();    // BH1750 Light calculations

  temperature_rtc = DS3231_get_treg();  // Read temperature from DS3231 RTC
  totalMeasurements++;
} // END - sensorMeasurements


void LCDactivityStats() {
  I2C_SLA = 0x3d*2;   // left display
  // I2C_SLA = 0x3c*2;   // right display
  u8g.firstPage();
  do {
    u8g.drawStr(24, 9, "EINSTELLUNGEN");
    u8g.drawLine(0, 11, 128, 11);
    u8g.setPrintPos(3, 25);
    if (dayOfMonth < 10)
      u8g.print("0");
    u8g.print(dayOfMonth, DEC);
    u8g.print(".");
    if (month < 10)
      u8g.print("0");
    u8g.print(month, DEC);
    u8g.print(".");
    u8g.print("20");
    u8g.print(year, DEC);
    u8g.print(" ");
    if (hour < 10)
      u8g.print("0");
    u8g.print(hour, DEC);
    u8g.print(":");
    if (minute < 10)
      u8g.print("0");
    u8g.print(minute, DEC);
    u8g.print(" UCT");
    u8g.setPrintPos(0, 38);
    u8g.print("RTC Temp. ");
    u8g.print(temperature_rtc, 2);
    u8g.print("*C");
    u8g.setPrintPos(0, 51);
    u8g.print("Helligkeit ");
    u8g.print(lux);
    u8g.print(" lux");
    u8g.setPrintPos(0, 64);
    u8g.print("Laufzeit ");
    if (totalMeasurements < 288) {
      u8g.print("<24 Stunden");
    } else if (totalMeasurements >= 288 && totalMeasurements < 576) {
      u8g.print("ein Tag");
    } else if (totalMeasurements >= 576 && totalMeasurements < 846) {
      u8g.print("2 Tage");
    } else if (totalMeasurements >= 846 && totalMeasurements < 1152) {
      u8g.print("3 Tage");
    } else if (totalMeasurements >= 1152 && totalMeasurements < 1440) {
      u8g.print("4 Tage");
    } else if (totalMeasurements >= 1440 && totalMeasurements < 1728) {
      u8g.print("5 Tage");
    } else if (totalMeasurements >= 1728 && totalMeasurements < 2016) {
      u8g.print("6 Tage");
    } else if (totalMeasurements >= 2016) {
      u8g.print(">1 Woche");
    }
  } while (u8g.nextPage());

  // I2C_SLA = 0x3d*2;   // left display
  I2C_SLA = 0x3c*2;   // right display
  u8g.firstPage();
  do {
    u8g.drawStr(27, 9, "NIEDERSCHLAG");
    u8g.drawLine(0, 11, 128, 11);
    u8g.setPrintPos(0, 25);
    u8g.print("     24h ");
    u8g.print(rainLast24h, 2);
    u8g.print(" Liter");
    u8g.setPrintPos(0, 38);
    u8g.print("  3 Tage ");
    u8g.print(rainLast3Days, 2);
    u8g.print(" Liter");
    u8g.setPrintPos(0, 51);
    u8g.print("  7 Tage ");
    u8g.print(rainLast3Days, 2);
    u8g.print(" Liter");
    u8g.setPrintPos(0, 64);
    u8g.print("   Monat ");
    u8g.print(rainMonth);
    u8g.print(" Liter");
  } while (u8g.nextPage());
  delay(SCREEN_MILLISECONDS);
} // END - LCDactivityStats

Die Funktionen der RTC (void setDS3231time, void readDS3231time und void displayTime) sind ebenso wie Serial.prints wg. der Zeichenbegrenzung herausgenommen.

Für leicht verständliche Hilfe und Verbesserungen bin ich sehr dankbar.

Besser wäre die fortlaufende Zählung der Regenmenge aus den letzten 24 Stunden, 3 Tagen und 7 Tagen - nur: wie wird das programmiert?

Wenn ich dich richtig verstehe, willst du u.a. jede Stunde die älteste Stunden-Regenmenge von vor 7 Tagen subtrahieren und dafür die aktuelle Stundenmenge dazuaddieren?

Das geht so nicht schlecht, wenn man arduinomäßig mit RAM sparsam umgehen will, da man da ja alte Werte aufheben muss, bzw. wissen muss, welcher Wert zu welcher Zeit gehört. Ein Monat hat ja 24*31 = 744 Stunden, soviele Bytes müsstest du schon opfern. ( In float würde ich erst zur Anzeige umrechnen, zumal ja beide Wippenseiten die gleiche Menge messen. )
Wenn ich das richtig verstehe, zählst du die Anzahl Wechsel der Regenwippe. Was kommt denn da pro Stunde max. zusammen? Sind evtl. Stundenwerte im Monat etwas übertrieben?

Interessant ist doch auch die aktuelle Niederschlagsmenge, also die Zeit zwischen den letzten zwei Pulsen der Regenwippe, bzw. dessen Kehrwert.

Was auch eher ginge, wären die letzten 24 Stunden-Werte und die letzten 7 Tageswerte.
Bei deinem 3- und 7-Tageszähler würde dann täglich einmal um einen Tag weitergeschaltet.

Das ist ein Mittelding zwischen "alle 7 Tage bei 0 anfangen" und "jeweils genau die letzten 7*24 = 168 Werte" anzeigen.

Der Regensensor zählt zwar die Wippen-Bewegungen, allerdings nur dann, wenn der Arduino den Pin ständig abfragt. Wegen der Bildschirmanzeigen -hier auch noch verkürzt- kann er das jedoch nicht.

Das hört sich sehr schräg an. Von was für Impulsdauern reden wir hier?

Je nach Anwendungszweck könnte man die Regenmenge pro Zeiteinheit auch in eine externe Datenbank übertragen oder in einem FRAM speichern.

Gruß Tommy

Überzeugt, Michael: Die Datenmenge wird wahrlich viel zu gross - und die Programmierung werde ich auch nimmer hinbekommen!

michael_x:
Was auch eher ginge, wären die letzten 24 Stunden-Werte und die letzten 7 Tageswerte.
Bei deinem 3- und 7-Tageszähler würde dann täglich einmal um einen Tag weitergeschaltet.

Wie wird dann die tägliche Weiterschaltung mit den gesammelten Werten programmiert? Ich denke der Niederschlag aus den letzten drei Tagen und die letzten Stunden sind für die Pflanzenbewässerung von Relevanz, um ggf. die Wasserzufuhr der Bewässerungsanlage zu unterbinden. Das ist aber eine Unterstellung von mir - ich selber habe keine Gartenerfahrungen.

Was die Zeitverzögerungen durch die OLEDs angeht: wie können diese so aus der Loop entfernt werden, dass sie die einzelnen Tasks in der Loop nicht "aufhalten"?

Tommy56:
Je nach Anwendungszweck könnte man die Regenmenge pro Zeiteinheit auch in eine externe Datenbank übertragen oder in einem FRAM speichern.

Das übersteigt meine Kenntnisse bei weitem! Obwohl... einen 512K EEPROM hätte ich noch übrig. Allerdings würde er recht häufig mit den Daten gespeist und ginge wohl recht zügig kaputt.

Felipe2017:
ch habe nicht wie eigentlich erworben die SSD1306, sondern die SH1106 Controller. Der Verzicht auf die Funktion "u8g.begin()" im Setup löste das Problem sporadisch auftretender Hieroglyphen. Die Drahtbrücke eines Displays wurde von mir mit einem 470k OHM Widerstand geändert und die Datei "u8g_com_arduino_ssd_i2c.c" wie von olikraus im englischsprachigem Forum beschrieben angepasst:

uint8_t I2C_SLA = 0x3d2; // alternativ 0x3c2;

Ok, mir ist klar, dass es etwas Arbeit bedeutet, aber die Umstellung von u8glib auf u8g2 wäre u.U. sinnvoll. Gerade für eine andere I2C Adresse gibt es jetzt einen eigenen Befehl inder U8g2 (dafür ist das u8g2.begin() jetzt zwingend erforderlich).

Es gibt auch eine eigene Seite mit Portierungs Hinweisen: u8gvsu8g2 · olikraus/u8g2 Wiki · GitHub

Felipe2017:
1.) Kann die Schriftart der U8glib zentriert werden? Im Netz stieß ich auf den command "setPrintPosCenter", was der Compiler aber nicht akzeptiert.

Dafür gibt es keinen eigenen Befehl, aber man kann sich das leicht ausrechnen.

Nehmen wir mal an, Du willst einen Text t mittig auf dem Display zentrieren. Gesucht ist also die x Koordinate an der du t hinmalen willst. x berechnet sich dann wie folgt:

x = (128 - getStrWidth(t))/2;

Das war's schon.

Oliver

Hi

Wegen der begrenzten Lebenszeit eines dauern beschriebenen EEprom ja FRAM - gibt Es als SPI, I²C, Parallel, ist beliebig oft beschreibbar, hat keine Wartezeiten wie ein EEprom, undbehält die Daten, ähnlich einem EEprom, auch bei SPannungsausfall.

MfG

Felipe2017:
Ich denke der Niederschlag aus den letzten drei Tagen und die letzten Stunden sind für die Pflanzenbewässerung von Relevanz, um ggf. die Wasserzufuhr der Bewässerungsanlage zu unterbinden. Das ist aber eine Unterstellung von mir - ich selber habe keine Gartenerfahrungen.

Habe mit automatischer Gartenbewässerung auch keine Erfahrung, hab nur mitgekriegt, dass üblicherweise die Bodenfeuchte gemessen wird. Dass man sich das evtl. sparen kann, wenn es schon regnet, wäre eine Anwendung für deinen Regensensor. Aber die Regenmenge vor ein paar Tagen.., ich weiß nicht recht.

Der Arduino-RAM ist auch nicht das ideale Langzeit-Archiv, und dass es dir um den sportlichen Ehrgeiz geht, etwas Unsinniges trotzdem hinzukriegen, sehe ich eher nicht.

michael_x:
Habe mit automatischer Gartenbewässerung auch keine Erfahrung, hab nur mitgekriegt, dass üblicherweise die Bodenfeuchte gemessen wird. Dass man sich das evtl. sparen kann, wenn es schon regnet, wäre eine Anwendung für deinen Regensensor. Aber die Regenmenge vor ein paar Tagen.., ich weiß nicht recht.

Die Bodenfeuchtigkeit hatte ich der Vergangenheit, wie auf Youtube mehrfacht vorgestellt, mit Edelstahl-Schrauben per AnalogRead gemessen - dann mit vergoldeten Schrauben probiert; letztere korrodieren durch den Stromfluss ebenso, wenn auch langsamer. Das taugt wirklich nichts und muss ~halbjährlich gewartet bzw. ausgetauscht werden. Der kapazitative Bodensensor (Giesomat, wurde auch hier im Forum vorgestellt) hat zwar positive Bewertungen in seiner Funktion, sei allerdings schwierig im Sketch zu implementieren. Die Menge kann damit auch nicht so gut gemessen werden und so bot sich der Regensensor an, der mir genau zur rechten Zeit von einer alten Wetterstation (Froggit WH3080) in die Hände fiel.

Den Regensensor hatte ich ohne weitere Module/Sensoren testweise am Nano und Mega - das funktionierte wirklich gut! Mittels RTC nullte ich um Mitternacht die Tages-, zum Montag die Wochen- und zum 1. die Monatswerte. Zwei Probleme hatte ich nicht lösen können:
1.) die Messwerte stimmten nicht mehr, sobald der Arduino in der Loop mehrere Sensoren und LCD-Anzeigen durchlaufen musste -was bei recht vielen Bildschirmanzeigen mit diversen Sensoren etwa eine Minute dauert- und
2.) ist das Resetten genau dann unsinnig, wenn es kurz zuvor zu Regnen aufhörte und der Arduino denkt: toll, es hat nicht geregnet - Wasser marsch!

Es folgte meine Überlegung, die Variablen "rainLast3Days" und "rainLast7Days" so zu kumulieren, dass die letzten Messwerte von der Niederschlagsmenge übergeben werden. Wenn der Tageswert korrekt ist und nicht um Mitternacht des Folgetages genullt wird, wie würde dann

Bei deinem 3- und 7-Tageszähler würde dann täglich einmal um einen Tag weitergeschaltet.

aussehen? Wenn ein FRAM hilft und ich hier im Forum die Hilfestellung erhalte, dann kann ich natürlich so Breakout Board kaufen und das im Projekt ergänzen - es soll endlich der Jungendtraum meines Vaters in Erfüllung gehen und ich krebse schon recht lange mit dem Projekt herum, ohne ihn bislang zuverlässig erfüllt zu haben.

olikraus:
Ok, mir ist klar, dass es etwas Arbeit bedeutet, aber die Umstellung von u8glib auf u8g2 wäre u.U. sinnvoll. Gerade für eine andere I2C Adresse gibt es jetzt einen eigenen Befehl inder U8g2 (dafür ist das u8g2.begin() jetzt zwingend erforderlich).

Oha, das schaue ich mir bereits an... klasse Arbeit, auch wenn ich für jeden neuen Schritt eine Ewigkeit brauche!

postmaster-ino:
Wegen der begrenzten Lebenszeit eines dauern beschriebenen EEprom ja FRAM - gibt Es als SPI, I²C, Parallel, ist beliebig oft beschreibbar, hat keine Wartezeiten wie ein EEprom, und behält die Daten, ähnlich einem EEprom, auch bei SPannungsausfall.

Ich glaub's nicht! Dann wäre ja auch das Problem wiederkehrender Stromausfälle (1-2x im Monat) behoben!?!

Vielen Dank für Eure Hilfe!!