Wenn Du noch aufzeigen könntest, woher Du den Sketch genommen hast, könnte man prüfen, für welches Umfeld und unter welchen Bedingungen er einmal erstellt worden ist.
Hier ein paar (längliche) Texte meinerseits ...
, als Anregung:
Beschreibung zu Deinem Sketch
- Bei korrekter Beschaltung und Verwendung ändert ein “Feuchtesensor” seinen Widerstand in Abhängigkeit von der ihn umgebenden Bodenfeuchte:
- Je feuchter seine Umgebung, um so geringer der Widerstand und damit die über dem Sensor abfallende Spannung (der Messpunkt nähert sich dem Massepotential). Der Messwert wird kleiner, der gemappte Wert steigt an.
- Unterschreitet die “gemappte” Feuchtigkeit einen bestimmten Wert, so wird ein Digitalpin auf LOW gezogen, und nach einer Sekunde wieder auf HIGH gesetzt. Außerdem wird eine entsprechende Meldung auf der seriellen Schnittstelle ausgegeben.
- Der Messvorgang findet alle drei, wenn der Schwellwert unterschritten wird, alle vier Sekunden statt (aufgrund der zusätzlichen Ein-Sekunden-Pause im if-Zweig).
- Ist der Wert der Variablen “feuchtigkeit” gleich oder größer als der Schwellwert, so wird regelmäßig eine entsprechende Meldung per Serial abgesetzt und der relayPin auf HIGH gesetzt.
- Allgemeine Punkte
- Die serielle Schnittstelle wird mit einer Baudrate von 9600 Bd betrieben.
- Analogmesswerte außerhalb der Grenzen von 550 und 10 werden entsprechend der map() Funktion so abgebildet, dass ein unterer Wert von 0 zu 101 und der obere Wert von 1023 zu -87 wird.
- Für die in 8 Bit passenden Pin-Nummern werden integer Konstante verwendet.
- Die Parameter des Mappings sind als Zahlenwerte im Code programmiert.
Bewertung
- Aus Programmiersicht handelt es sich um einen grundsätzlich funktionsfähigen Sketch.
- Die Baudrate kann auf 115200 Bd gesetzt werden, da dies heutzutage als gängige Übertragungsrate auf der USB-Schnittstelle verwendet wird. Zeitkritisch ist das jedoch in der vorliegenden Anwendung nicht.
- Der Einsatz von delays innerhalb der loop() ist vielleicht nicht "schön", aber unschädlich, solange der Sketch keine weiteren, ggf. zeitkritischen Funktionen bedienen muss.
- Die Grenzwerte von 550 und 10 in der map()-Funktion werden in diversen Tutorials verwendet und basieren auf empirischen Messungen (können sich also im Einzelfall unterscheiden).
- Die von der map() Funktion für Messwerte oberhalb von 550 und unterhalb von 10 gelieferten Werte sind zwar kleiner als 0 bzw. größer als 100, bedienen aber die if-Abfrage korrekt. Die Verwendung von constrain(), um die Daten auf den Bereich 0 ... 100 zu begrenzen, ist daher nicht erforderlich, solange man sich nicht darauf verlässt, dass nur Werte zwischen 0 und 100 auftauchen.
- Die programmierte Abfolge führt dazu, dass die über das Relais angesteuerte Pumpe u.U. mehrfach hintereinander jeweils für nur eine Sekunde eingeschaltet wird, solange der Messwert nicht dafür sorgt. dass feuchtigkeit >= Schwellwert ist.
- Das regelmäßige Setzen des relayPin auf HIGH, wenn genügend Feuchte gemessen wird, ist nicht erforderlich, aber auch nicht schädlich.
- Die regelmäßigen seriellen Ausgaben sind vermutlich nur beim Testen sinnvoll; es sei denn die Bewässerungsanlage hängt ständig an einem Rechner.
- Die Grenzwerte aus dem Mapping hängen im Detail von der jeweiligen Anwendung ab. Vermutlich decken die empirischen Werte aus den Tutorials die meisten Fälle ausreichend ab, aber verschiedene Faktoren können eine Rolle spielen:
- Wie tief reicht der Sensor in das "Erdreich"?
- Welche Beschaffenheit liegt vor: Pflanzerde? Granulat?
- Leitfähigkeit des Boden/Wasser-Gemisches?
- Übergangswiderstand Sensor-Umgebung
- Wie weit sind Sensor und Wasserzuführung voneinander entfernt?
- Wie schnell und wie gleichmäßig verteilt sich das Wasser im Boden?
Verbesserungspotential
- Anstelle der delays() kann man millis()-basierendes Timing verwenden, um die loop() für ggf. später interessante zeitkritische Funktionen nicht zu blockieren.
- Die optimalen Sensorwerte für die individuelle Anwendung kann man mittels Trocknung und manueller Befeuchtung durch einen Test ermitteln. Dazu gibt man einfach regelmäßig den Sensorwert aus, notiert sich die Werte, die als "trocken" und "ausreichend feucht" gelten sollen und benutzt diese dann im Code.
- Zudem könnte man die Pumpe solange durchlaufen lassen, wie dies zum Erreichen der Wunschfeuchte sinnvoll ist. Diese Zeit müsste man ebenfalls durch manuelle Tests ermitteln. Dies könnte u.U. die Lebensdauer der Pumpe und des Relais verlängern ...
- Es empfiehlt sich, anstatt fester Zahlenwerte innerhalb des Codes mit Konstanten zu arbeiten. Das vereinfacht es, diese Daten entsprechend den individuellen Messergebnissen anzupassen, ohne die Stellen innerhalb des Codes zu identifizieren und anzupassen.
- Datentypen sollten möglichst gut zum Verwendungszweck ausgewählt werden. Pins also als (vorzeichenloser) Byte-Type deklariert werden.
- Die serielle Ausgabe ist vermutlich in der Entwicklungsphase sinnvoll, wird im Einsatz eher nicht helfen. Hier könnte man stattdessen eine Led-Anzeige ergänzen, die den Feuchtezustand anzeigt und den Messzyklus signalisiert: Grün für "ok" und Rot für "zu trocken". Beide Led blinken kurz, wenn die Messung erfolgt, so dass erkennbar ist, dass der Controller nicht "steht". Zudem zeigt eine rote Led an, dass die Pumpe im Einsatz sein muss. Wenn Rot und kein Pumpeinsatz => Fehler!
Und hier noch ein Sketch, der das meiste davon berücksichtigt, eine nicht-blockierende Lösung aufzeigt und über eine Led-Anzeige zum Zustand inkl. kurzem "Blinken" bei jeder Messung verfügt. Muss man nicht so machen, aber vielleicht als weitere Anregung von Interesse ...
Non_blocking Sketch
/*
Forum: https://forum.arduino.cc/t/hilfe-zum-automatischen-bewasserungscode/1426822
Wokwi: https://wokwi.com/projects/454040248062179329
Angepasster Sketch zur Bewässerungsanlage
Statt der Pumpe -> Blaue Led
Statt des Feuchtesensors -> Schiebepotentiometer
Zusätzlich:
Rote Led: Zeigt den Zustand "zu trocken" an
Grüne Led: Zeigt den Zustand "ausreichend feucht" an
Beide Led blinken kurz, wenn eine Feuchtemessung erfolgt ist
Magenta Led: Wird regelmäßig ein/ausgeschaltet, um das Nicht-Blockieren der loop() zu demonstrieren
Die Parameter für das Mapping des Sensorwertes sowie der Schwellwert für "zu trocken" sind als Konstanten
im Header deklariert. Ebenso die Werte für den Messzyklus und die Dauer des Pumpens. Die Ausschaltzeit der
Anzeige-Led ist ebenfalls dort zu finden.
Bei Einsatz der Pumpe wird die folgende Messung um die Pumpdauer nach hinten verschoben, um so das im
Originalsketch durch delay() erzeugte Timing nachzubilden. Dazu wird die Variable letzteMessung mit dem
Ausschalten der Pumpe neu gesetzt.
2026/01/24
ec2021
*/
constexpr uint8_t sensorPin {A0}; // Bodenfeuchtesensor
constexpr uint8_t relayPin {4}; // Relais / Pumpe
constexpr uint8_t greenPin {5}; // Anzeige "ausreichend feucht"
constexpr uint8_t redPin {6}; // Anzeige "zu trocken/Pumpeneinsatz"
constexpr uint16_t sensorDry {550}; // Schwellwert Analogsensor für "zu trocken"
constexpr uint16_t sensorWet { 10}; // Schwellwert Analogsensor für "ausreichend feucht"
constexpr int16_t dryThreshold {60}; // Schwellwert der umgerechneten Feuchte für "zu trocken"
constexpr unsigned long measureInterval {3000}; // Messintervall in ms
constexpr unsigned long pumpDuration {1000}; // Pumpdauer in ms
constexpr unsigned long blinkDuration {250}; // Dauer der Aus-Zeit der Led-Anzeige in ms
enum class STATUS {MESSEN, PUMPEAN, PUMPEN};
STATUS Status = STATUS::MESSEN;
unsigned long startZeit;
unsigned long letzteMessung;
boolean istZuTrocken = false;
boolean messungErfolgt = false;
void setup() {
Serial.begin(115200);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH); // Pumpe aus (LOW-aktives Relais)
Serial.println("Bewässerungsanlage gestartet ...");
}
void loop() {
ledAnzeige();
bewaesserungsMaschine();
flackern(); // Nur zur Demo: Für die Bewässerungsanlage nicht benötigt!
}
void bewaesserungsMaschine() {
switch (Status) {
case STATUS::MESSEN:
if (istTrocken()) {
Serial.println("Boden zu trocken -> Bewässerung AN");
Status = STATUS::PUMPEAN;
}
break;
case STATUS::PUMPEAN:
startZeit = millis();
startePumpe();
Status = STATUS::PUMPEN;
break;
case STATUS::PUMPEN:
if (millis() - startZeit >= pumpDuration) {
stoppePumpe();
letzteMessung = millis(); // Setzt die nächste Messung auf Jetztzeit + measureInterval
Status = STATUS::MESSEN;
}
break;
}
}
boolean istTrocken() {
if (millis() - letzteMessung >= measureInterval) {
letzteMessung = millis();
int sensorWert = constrain(analogRead(sensorPin), sensorWet, sensorDry);
int feuchtigkeit = map(sensorWert, sensorDry, sensorWet, 0, 100);
Serial.print("Feuchtigkeit: ");
Serial.print(feuchtigkeit);
Serial.println(" %");
istZuTrocken = (feuchtigkeit < dryThreshold);
messungErfolgt = true;
return istZuTrocken;
}
return false;
}
void startePumpe() {
Serial.println("Starte Pumpe");
digitalWrite(relayPin, LOW);
}
void stoppePumpe() {
Serial.println("Stoppe Pumpe");
digitalWrite(relayPin, HIGH);
}
void ledAnzeige() {
static unsigned long lastBlink;
if (messungErfolgt) {
messungErfolgt = false;
lastBlink = millis();
digitalWrite(greenPin, LOW);
digitalWrite(redPin, LOW);
}
if (millis() - lastBlink > blinkDuration) {
digitalWrite(greenPin, !istZuTrocken);
digitalWrite(redPin, istZuTrocken);
}
}
// Dient nur der Demo der nicht-blockierenden StateMachine
void flackern() {
constexpr uint8_t magentaPin {7};
constexpr unsigned long magentaBlink {150};
static boolean firstCall = true;
if (firstCall) {
pinMode(magentaPin, OUTPUT);
firstCall = false;
}
static unsigned long lastBlinking;
if (millis() - lastBlinking > magentaBlink) {
lastBlinking = millis();
byte state = !digitalRead(magentaPin);
digitalWrite(magentaPin, state);
}
}
Zum Testen auf Wokwi: https://wokwi.com/projects/454040248062179329
Und Dein Originalsketch hier https://wokwi.com/projects/454040210678341633
Viel Erfolg!
ec2021
[Edit] Und hier noch eine einfacher gehaltene nicht-blockierende Lösung, die ohne die Enumerations arbeitet:
Siehe bei Wokwi: https://wokwi.com/projects/454058836291026945
Sketch
/*
Forum: https://forum.arduino.cc/t/hilfe-zum-automatischen-bewasserungscode/1426822
Wokwi: https://wokwi.com/projects/454058836291026945
Angepasster Sketch zur Bewässerungsanlage
Statt der Pumpe -> Blaue Led
Statt des Feuchtesensors -> Schiebepotentiometer
Einfache non-blocking Lösung
2026/01/24
ec2021
*/
constexpr byte sensorPin {A0}; // Bodenfeuchtesensor
constexpr byte relayPin {4}; // Relais / Pumpe
constexpr uint16_t sensorTrocken {550}; // Schwellwert Analogsensor für "zu trocken"
constexpr uint16_t sensorFeucht { 10}; // Schwellwert Analogsensor für "ausreichend feucht"
constexpr int16_t schwellWert {60}; // Schwellwert der umgerechneten Feuchte für "zu trocken"
constexpr unsigned long messIntervall {3000};// Zeitintervall zwischen zwei Messungen in ms
constexpr unsigned long pumpDauer {1000}; // Dauer des Pumpens in ms
int feuchtigkeit; // Feuchtigkeit in Prozent
unsigned long letzteMessung;
unsigned long pumpeEinZeit;
boolean pumpeAn = false;
void setup() {
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH); // Pumpe aus (LOW-aktives Relais)
Serial.begin(115200);
Serial.println("Bodenfeuchtigkeit wird gemessen...");
}
void loop() {
if (pumpeAn) {
pumpen();
} else {
messenUndAuswerten();
}
flackern();
}
void messenUndAuswerten() {
static boolean ersterAufruf = true;
if (millis() - letzteMessung > messIntervall || ersterAufruf) {
letzteMessung = millis();
ersterAufruf = false;
int sensorWert = constrain(analogRead(sensorPin), sensorFeucht, sensorTrocken);
feuchtigkeit = map(sensorWert, sensorTrocken, sensorFeucht, 0, 100);
Serial.print("Feuchtigkeit: ");
Serial.print(feuchtigkeit);
Serial.println(" %");
if (feuchtigkeit < schwellWert) {
Serial.println("Boden zu trocken -> Bewässerung AN");
digitalWrite(relayPin, LOW);
pumpeAn = true;
pumpeEinZeit = millis();
}
}
}
void pumpen() {
if (millis() - pumpeEinZeit > pumpDauer) {
Serial.println("Pumpe Aus");
digitalWrite(relayPin, HIGH);
pumpeAn = false;
letzteMessung = millis(); // Neue Startzeit für die nächste Messung setzen,
// nachdem die Pumpe wieder ausgeschaltet wurde
}
}
// Dient nur der Demo der nicht-blockierenden StateMachine
void flackern() {
constexpr uint8_t magentaPin {7};
constexpr unsigned long magentaBlink {150};
static boolean firstCall = true;
if (firstCall) {
pinMode(magentaPin, OUTPUT);
firstCall = false;
}
static unsigned long lastBlinking;
if (millis() - lastBlinking > magentaBlink) {
lastBlinking = millis();
byte state = !digitalRead(magentaPin);
digitalWrite(magentaPin, state);
}
}
Ist vielleicht besser nachzuvollziehen als die StateMachine ...