Problem mit Zeitmessung von Funktionen mit millis()

Hallo,

Ich baue gerade an einem Projekt mit einem Arduino UNO board. Es geht um eine Bewässerungsanlage für Topfpflanzen.

Ich nutze dazu ein RTC Modul um alle 24h Feuchtigkeitswerte mit Sensoren zu messen und dann ggf. eine Pumpe zu aktivieren.

Der Code soll wie folgt funktionieren:
Sind wieder 24h vorbei?

  • Grüner Sensor an
  • 2 Sekunden warten (mit millis)
  • Grüner Sensor auslesen
  • Wert an Pumpe übergeben

Boden trocken?

  • Grüne Pumpe an
  • Warte eine Sekunde
  • Grüne Pumpe aus

Dann das gleiche mit blauem und weißem Sensor wiederholen.

Der Code sieht wie folgt aus:

// Arduino soil moisture sensor and real time clock
// This sketch works as an self watering flower pot
// https://iotspace.dev/arduino-bodenfeuchtesensoren---anleitung-und-sketch
//

#include "RTClib.h"
#include <Wire.h>

// Configuration, setup moisture threshold and checking time
// high number means dry soil
const int MOISTURE_THRESHOLD = 300;
// Intervall check
const long INTERVALL_PUMP_CHECK = 60;
// wait for the sensor before measuring
const long SENSOR_WAIT_TIME = 2000;
// pump time in ms, determines how much water will flow
const long PUMP_INTERVAL = 1000;

// Timer variable
long lastPumpCheckCycle = 0;

// Clock
RTC_DS3231 rtc;

// declare pins for power for sensor and pump
int GREEN_SENSOR = 6;
int GREEN_PUMP = 7;

int BLUE_SENSOR = 8;
int BLUE_PUMP = 9;

int WHITE_SENSOR = 10;
int WHITE_PUMP = 11;

// Analog pins
int GREEN_MOISTURE = A0;
int BLUE_MOISTURE = A1;
int WHITE_MOISTURE = A2;

void setup() {
  // serial communication with 9600 baud rate
  Serial.begin(1000000);

#ifndef ESP8266
  while (!Serial)
    ;  // wait for serial port to connect. Needed for native USB
#endif

  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }

  Serial.println("Setting the time...");
  // When time needs to be set on a new device, or after a power loss, the
  // following line sets the RTC to the date & time this sketch was compiled
  rtc.adjust(DateTime(__DATE__, __TIME__));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));

  // set up power pins for sensor and pump
  pinMode(GREEN_SENSOR, OUTPUT);
  digitalWrite(GREEN_SENSOR, HIGH);
  pinMode(GREEN_PUMP, OUTPUT);
  digitalWrite(GREEN_PUMP, HIGH);

  pinMode(BLUE_SENSOR, OUTPUT);
  digitalWrite(BLUE_SENSOR, HIGH);
  pinMode(BLUE_PUMP, OUTPUT);
  digitalWrite(BLUE_PUMP, HIGH);

  pinMode(WHITE_SENSOR, OUTPUT);
  digitalWrite(WHITE_SENSOR, HIGH);
  pinMode(WHITE_PUMP, OUTPUT);
  digitalWrite(WHITE_PUMP, HIGH);

  // set up pin to measure moisture
  pinMode(GREEN_MOISTURE, INPUT);
  pinMode(BLUE_MOISTURE, INPUT);
  pinMode(WHITE_MOISTURE, INPUT);

  delay(500);
}

void loop() {
  long nowTime = rtc.now().secondstime();
  long difference = nowTime - lastPumpCheckCycle;

  // Check if the current time is the predetermined check time
  // If so, execute the checkMoisture function
  if (lastPumpCheckCycle == 0 || difference > INTERVALL_PUMP_CHECK) {
    Serial.println("Check moisture");
    lastPumpCheckCycle = nowTime;

    // Measure green sensor
    int greenMoisture = checkMoisture(GREEN_SENSOR, GREEN_MOISTURE, "green");
    startPump(GREEN_PUMP, greenMoisture, "green");

    // Measure blue sensor
    int blueMoisture = checkMoisture(BLUE_SENSOR, BLUE_MOISTURE, "blue");
    startPump(BLUE_PUMP, blueMoisture, "blue");

    // Measure white sensor
    int whiteMoisture = checkMoisture(WHITE_SENSOR, WHITE_MOISTURE, "white");
    startPump(WHITE_PUMP, whiteMoisture, "white");
  }
}


int checkMoisture(int sensorPin, int moisturePin, String colour) {
  // Turn on the sensor
  digitalWrite(sensorPin, LOW);

  unsigned long startMeasuring = millis();

  while (millis() - startMeasuring < SENSOR_WAIT_TIME) {
    // Do nothing, wait for SENSOR_WAIT_TIME
  }

  int moisture = analogRead(moisturePin);
  Serial.println(colour + " moisture: " + moisture);
  // Turn off the sensor
  digitalWrite(sensorPin, HIGH);
  return moisture;
}

void startPump(int pumpPin, int moistureLevel, String colour) {
  // Turn on the pump for 1 second if moisture level is above the threshold
  if (moistureLevel > MOISTURE_THRESHOLD) {

    unsigned long startPumping = millis();

    // Turn on the pump
    Serial.println("Turning pump on at " + colour);
    digitalWrite(pumpPin, LOW);

    while (millis() - startPumping < PUMP_INTERVAL) {
      // Do nothing, wait for PUMP_INTERVAL
    }

    digitalWrite(pumpPin, HIGH);  // Turn off the pump after the time interval
  } else {
    Serial.println("No need for irrigation at " + colour);
  }
}

Die Messung der Zeitintervalle nehme ich dabei mit millis() vor.

Wenn ich nur einen Sensor verwende funktioniert das noch. Wenn ich aber alle drei Sensoren auslesen will, wird alles durcheinander und die Schaltungen funktionieren nicht mehr richtig. Der Output sind da ca. so aus:

Check moisture
green moisture: 486
Turning pump on at green
on at blue
irrigation at white
���Setting the time...
Check moisture
Setting the time...
Check moisture
Setting the time...
Check moisture
green moisture: 489
Turning pump on at green
on at blue

Ich kann leider nicht nachvollziehen, warum der Code sich nicht so verhält wo oben beschrieben.
Ich hab auch schon verschiedene Baudrates (9600, 500000 und 1000000) verwendet, ohne Erfolg.

Gibt es vielleicht in diesem Fall bessere Methoden, um die Zeit zu messen um die Funktionen für eine bestimmte Dauer auszuführen?

VG
Gregor

Hallo, wenn du eine RTC verbaut hast, warum nimmst die nicht für Zeitmessungen ?

Weil ich gelesen hatte, dass für so kurze Intervalle die millis() funktion besser geeignet ist.
Denkst du, es wäre besser auch den RTC zu nutzen? Ginge das dann auch direkt in den Funktionen "checkMoisture" und "startPump"?

Welche RTC nutzt Du?
Es gibt funktionsreichere Libs für manche RTC, als die RTC.h

Gruß Tommy

Ich nutze den RTC_DS3231

Gut. Dann schaue mal, ob Dir die DS3231.h etwas bringt.
Evtl. die Alarm-Funktionalität, wenn Du zu einer bestimmten Uhrzeit täglich etwas tun willst.

Gruß Tommy

Ja die gibt es. Da wäre es sogar möglich alle drei Meß- und Pumpzyklen parallel zu fahren.

Hier verwendest so etwas, das noch schlechter als delay() ist, nämlich leere Endlosschleifen.
Meines Wissens mögen das ESPs gar nicht, da ist ein richtiges delay() oder wenigstens ein delay(1); innerhalb der tu-nix- Schleife besser.

Aber, um ehrlich zu sein, warum dein Sketch sich bei einem normalen Arduino so verhält, sehe ich nicht. Welche Art zu warten du verwendest, sollte egal sein.

Warum sollte das nicht funktionieren ?
Startzeit auslesen, Laufzeit hinzurechnen und nach erreichen der Ablaufzeit reagieren.

Danke, ich schaue es mir mal durch und hoffe da was zu finden, um die Laufzeit von den Funktionen zu messen. Der tägliche Check so wie er implementiert ist, hat schon sehr zuverlässig funktioniert

Was wäre denn eine bessere Methode um das gewünschte Verhalten zu erreichen?

In einem kleineren Beispiel mit nur einem Sensor und Pumpe habe ich mit delay() gearbeitet, erzeugte aber die gleichen Fehler. Die tu nix Schleife war ehrlich gesagt ein Vorschlag von chatgpt, und da kann ja auch mal Quatsch rauskommen

Ich verstehe das Verhalten ehrlich gesagt auch nicht, deshalb hab ich den Beitrag erstellt :grinning_face:

Hast Du Dir in der IDE mal das Beispiel "BlinkWithoutDelay" angeschaut und es verstanden?
Ansonsten schau Dir mal die MoBaTools an.

Gruß Tommy

Schön, dass du das fehlerhafte "Setting the time" drin hast, daran sieht man, dass du mehrfach durch setup läufst, also ein elektrisches Problem hast.

Teste doch erstmal ohne Pumpe, nur mit einer LED (und Vorwiderstand).
Und, bis du BlinkWithoutDelay verstanden hast, warte per normalem delay(SENSOR_WAIT_TIME); statt dem chatGPT - Mist.

Dieser Text darf nur genau einmal nach dem Einschalten (Reset) angezeigt werden. Kommt er wie bei Dir mehrfach vor, hat der UNO zwischendrin mal nicht genügend Strom zur Verfügung. Beispielsweise zieht die Pumpe mehr Strom, als das Netzteil liefern kann, wodurch die Spannung auch beim UNO kurzzeitig zusammenbricht.

@michael_x war schneller :slightly_smiling_face:

Sehe ich das richtig, dass du auch die Sensoren von der Spannung trennst , und das an den Pins des Arduino ?
Warum machst du das ?
Zeige uns doch mal welche Sensoren und Pumpen du verwendest. Poste bitte einen Link. Und zeige auch wie du alles verdrahtet hast.

Ich hoffe es ist zu erkennen, wie ich es verschaltet habe. Der Einfachheit halber, hab ich nur einen Sensor und eine Pumpe hinzugefügt. Die anderen sind gleich verkabelt.

Im Endeffekt liefert der Arduino den Strom für die Sensoren und die Pumpen. Ich bin aber davon ausgegangen, dass das kein Problem ist, wenn ich die jeweils nur nacheinander einschalte. Deshalb habe ich den Sensor auch nicht dauerhaft an, sondern nur wenn ich messen will.

Relais: ELEGOO 8 Channel DC 5V Relay Module with Optocoupler for Arduino UNO R3 1280 DSP ARM PIC AVR STM32 Blue : Amazon.de: Electronics & Photo
Sensor: APKLVSR Soil Moisture Sensor, Capacitive Analogue Hygrometer, Moisture Sensor without Corrosion, Pack of 5: Amazon.de: Computer & Accessories
Pumpen: RUNCCI-YUN Mini Water Pump Submersible Pump Micro Motor Pump DC 3V 5V + 3M PVC Hose for Aquarium Garden Watering Plants Flowers Pack of 3 : Amazon.de: Pet Supplies

Es hat funktioniert mit einem Sensor und einer Pumpe, wenn ich den ganz normalen delay versucht habe.
Als ich das dann skalieren wollte mit 3 Sensoren und Pumpen hat es nicht mehr funktioniert. Deshalb suche ich nach anderen Lösungen, aber bisher scheint keine zu funktionieren. Aber wie hier bereits vermutet wurde kann es auch an der Stromversorgung liegen...

Du hast gerade eine wunderbare Vorlage für eine Schrittkette geliefert.
Allerdings ist mir nicht klar, warum Du versuchst den Feuchtesensor über einen Arduino-Pin mit Spannung zu versorgen.
Wenn Du da versuchst den Sensor direkt zu betreiben, geht das in die Hose.
Warum machst Du das? ( Und jetzt bitte nicht Strom sparen als Grund nennen )

Ansonsten wär das eigentlich relativ infach abzuhandeln.

Ja, das ist gut zu erkennen.
Allerdings verstehe ich nicht, warum du den Sensor über ein Relais aus bzw. ein schaltest. Da du mit Netzteil atbeitest, kann der imm angeschaltet bleiben und du fragst zur gegebenen Zeit diesen ab.
Ein Link zum Sensor und zur Pumpe fehlt noch. Auch fehlt eine Freilaufdiode an der Pumpe.
Sorry, übersehen.

Ich habe hier mal ein ungetestetes Beispiel der Basis-Funktionen und diese ohne die RTC.
Alles auf Basis der "millis".
Dieses Beispiel kannst du für deine Anwendung skalieren um auf drei Sensoren und drei Pumpen zu kommen. Du musst noch deine Sensorfunktionen und Pumpensteuerungen an geeigneter Stelle einbauen.

#include "INTERVAL.h"                 // http://forum.arduino.cc/index.php?topic=413734.0

unsigned int pumpeEinZeit = 5;        // Zeit in Sekunden
unsigned long startMillis = 0;
bool pumpe_an = false;


void sensoren_auslesen()
{
  // hier den Sensor auslesen
  
  if( trocken == true )                 // wenn trocken dann:
  {
    pumpe_starten();
    pumpe_an = true;
    startMillis = millis();             // millis für Pumpzeit speichern
  }
}


void pumpe_ausschalten()
{
  // Pumpe ausschalten
  pumpe_an = false;
}


void setup()
{

}

void loop()
{
  INTERVAL(60000UL * 1440)            // alle 24 Std.
  {
    sensoren_auslesen();
  }
  
  if ( pumpe_an == true )
  {
    if (millis() - startMillis > pumpeEinZeit * 1000UL)          // auf 5 Sekunden gestellt
    {
      pumpe_ausschalten();            // wenn Pumpe Ein Zeit um, dann ausschalten
    }
  }
}

Evtl. kommst du damit klar, sonnst einfach fragen.

Der Sensor bekommt seine Spannung ganz normal von dem 5V Anschluss vom Arduino.
Der Pin 6 ist dazu da, das Relais zu schalten und den Sensor einzuschalten.

Ich habe gelesen, dass es besser für den Sensor sei, ihn nicht dauerhaft zu betreiben:
"Pin 7 dient als Stromversorgung für den Feuchtigkeitssensor. Dieser liefert sein Messsignal dann am Analog-Pin A3. Diese Feuchtigkeitssensoren neigen leider dazu stark zu korrodieren, wenn sie zu oft/lange benutzt werden. Es empfiehlt sich daher, den Sensor nur zum Messen einzuschalten und das auch nicht ganz so oft." (Automatische Pflanzenbewässerung mit Beleuchtung - SelberMachenBlog)
Allerdings verwendet die Person auch andere Sensoren. Kann es sein, dass Korrosion bei kapazitiven Sensoren kein Problem ist? Dann kann ich natürlich die Sensoren auch ohne Relais betreiben, was die Zeitmessung da erübrigen würde.