Automatische Pflanzenbewässerung mit ESP32 – Komplettes Tutorial

Nie wieder vertrocknete Pflanzen – baue dir Schritt für Schritt dein eigenes smartes Bewässerungssystem. Vom einfachen Feuchtigkeitsmesser bis zum WLAN-Dashboard mit automatischer Pumpensteuerung.

Warum automatische Bewässerung?

Jeder kennt das: Der Urlaub steht an, die Pflanzen sind prächtig gewachsen – und zwei Wochen später kommt man nach Hause und findet braune, vertrocknete Blätter vor. Oder man vergisst im Alltagsstress einfach mal das Gießen. Mit einem automatischen Bewässerungssystem gehört dieses Problem der Vergangenheit an.

In diesem Artikel zeige ich dir, wie du mit einem ESP32-Mikrocontroller und einem kapazitiven Bodenfeuchte-Sensor ein zuverlässiges System baust, das deine Pflanzen automatisch bewässert – und dich per WLAN über den Zustand deiner Pflanzen auf dem Laufenden hält. Dabei steigern wir uns von einem einfachen Messaufbau bis zu einem vollwertigen Web-Dashboard mit Live-Daten.

Die benötigten Komponenten bekommst du übrigens alle in unserem Shop – die Links zu den einzelnen Produkten findest du im jeweiligen Abschnitt.

Kapazitive vs. resistive Bodenfeuchte-Sensoren – der entscheidende Unterschied

Bevor wir in den Aufbau einsteigen, müssen wir über eine grundlegende Entscheidung sprechen: Welchen Sensor-Typ verwendest du? Es gibt zwei Varianten von Bodenfeuchte-Sensoren auf dem Markt, und die Unterschiede sind gravierend.

🔬 Resistive Bodenfeuchte-Sensoren

Resistive Sensoren messen den elektrischen Widerstand zwischen zwei Elektroden, die in die Erde gesteckt werden. Feuchter Boden leitet besser → geringerer Widerstand. Trockener Boden leitet schlechter → höherer Widerstand.

  • Sehr günstig in der Anschaffung (oft unter 1 €)
  • Einfache analoge Spannungsmessung
  • Aber: Die Elektroden korrodieren innerhalb weniger Wochen
  • Ständiger Stromfluss durch die Elektroden beschleunigt die Korrosion
  • Ungenaue Messwerte nach kurzer Zeit
  • Müssen regelmäßig getauscht werden – auf Dauer teurer und unzuverlässig

✅ Kapazitive Bodenfeuchte-Sensoren

Kapazitive Sensoren messen die Dielektrizitätskonstante des Bodens, die sich mit dem Feuchtigkeitsgehalt ändert. Die Elektronik ist vollständig durch eine Schutzschicht isoliert und hat keinen direkten Kontakt zum feuchten Boden.

  • Keine Korrosion – die Elektronik ist komplett versiegelt
  • Langlebig und wartungsfrei
  • Präzisere Messwerte über die gesamte Lebensdauer
  • Nur minimal teurer als resistive Modelle
  • Ausgangsspannung typischerweise 0–3V (je nach Feuchtigkeit)
👉 Unser kapazitiver Bodenfeuchte-Sensor ist die klare Empfehlung für jedes Projekt, das länger als zwei Wochen laufen soll.

💡 Fazit: Für ernsthafte, dauerhafte Projekte führt kein Weg am kapazitiven Sensor vorbei. Der minimale Mehrpreis zahlt sich durch Zuverlässigkeit und Langlebigkeit sofort aus. Resistive Sensoren taugen allenfalls zum ersten Experimentieren auf dem Breadboard – für den echten Einsatz im Blumentopf sind sie ungeeignet.

Projekt 1: Basis – Feuchtigkeit messen und am Serial Monitor ausgeben

Im ersten Projekt geht es um das Fundament: Wir lesen den Sensor aus und zeigen die Werte an. Dafür brauchst du nur drei Komponenten.

🛒 Benötigte Komponenten für Stufe 1

Verkabelung

Der Sensor hat drei Pins: VCC, GND und AOUT (analoger Ausgang). Die Verkabelung mit dem ESP32 ist denkbar einfach:

Sensor-Pin ESP32-Pin Beschreibung
VCC 3.3V Spannungsversorgung (Sensor akzeptiert 3.3V–5V)
GND GND Masse
AOUT GPIO 34 Analoges Signal (nutzt ADC1 des ESP32)

⚡ Wichtiger Hinweis: Verwende für den analogen Eingang einen Pin aus dem ADC1-Block des ESP32 (GPIO 32–39). ADC2-Pins werden vom WiFi-Stack belegt und liefern unzuverlässige Werte, sobald WLAN aktiv ist. GPIO 34 ist dafür ideal, da er ausschließlich als Eingang dient und nicht anderweitig verwendet wird.

Code für den Serial Monitor

Der folgende Code liest den Sensor aus und gibt die Feuchtigkeit als Prozentwert im Serial Monitor aus. Hochgeladen wird er über die Arduino IDE mit installiertem ESP32-Board.

/*
 * Projekt 1: Bodenfeuchte auslesen mit kapazitivem Sensor
 * Gibt Feuchtigkeitswerte in % auf dem Serial Monitor aus
 * 
 * Verkabelung:
 * Sensor VCC  → ESP32 3.3V
 * Sensor GND  → ESP32 GND
 * Sensor AOUT → ESP32 GPIO 34
 */

#define SENSOR_PIN 34       // Analoger Eingang (ADC1)
#define TROCKEN_WERT 2900   // Kalibrierwert für trockenen Boden
#define NASS_WERT 1400      // Kalibrierwert für nassen Boden

void setup() {
  Serial.begin(115200);
  delay(1000);
  
  Serial.println("========================================");
  Serial.println("  Bodenfeuchte-Sensor am ESP32");
  Serial.println("  Kapazitiver Sensor an GPIO 34");
  Serial.println("========================================\n");

  // ADC-Auflösung auf 12 Bit setzen (0–4095)
  analogReadResolution(12);
}

void loop() {
  // Rohwert vom Sensor lesen
  int sensorRohWert = analogRead(SENSOR_PIN);
  
  // Rohwert in Prozent umrechnen
  // Niedriger Wert = nass, hoher Wert = trocken
  int feuchtigkeitProzent = map(sensorRohWert, TROCKEN_WERT, NASS_WERT, 0, 100);
  
  // Auf gültigen Bereich begrenzen (0–100%)
  feuchtigkeitProzent = constrain(feuchtigkeitProzent, 0, 100);
  
  // Ausgabe auf dem Serial Monitor
  Serial.print("Rohwert ADC: ");
  Serial.print(sensorRohWert);
  Serial.print("  |  Feuchtigkeit: ");
  Serial.print(feuchtigkeitProzent);
  Serial.println(" %");
  
  // Alle 2 Sekunden messen
  delay(2000);
}

Öffne nach dem Upload den Serial Monitor (Strg+Shift+M) mit 115200 Baud. Du solltest nun alle zwei Sekunden einen Messwert sehen. Steckst du den Sensor in trockene Erde, zeigt er einen niedrigen Prozentwert. In feuchter Erde steigt der Wert.

Schwellwert-Kalibrierung – trocken, feucht, nass

Jeder Sensor und jede Erde ist etwas anders. Deshalb solltest du deine eigenen Schwellwerte kalibrieren, bevor du automatisierte Entscheidungen triffst. So gehst du vor:

  1. Luftwert messen: Halte den Sensor in die Luft (völlig trocken). Notiere den Rohwert. Das ist dein absolutes Maximum.
  2. Nasswert messen: Tauche den Sensor in ein Glas Wasser. Notiere den Rohwert. Das ist dein Minimum.
  3. Praxistest: Stecke den Sensor in die Erde. Gieße die Pflanze gründlich und warte 10 Minuten. Notiere den Wert → das ist dein „nass"-Schwellwert.
  4. Trockentest: Lass die Erde einige Tage austrocknen, bis die Pflanze Wasser bräuchte. Notiere den Wert → das ist dein „trocken"-Schwellwert.
  5. Schwellwert festlegen: Wähle einen Wert etwa 10–15 % über dem Trockenwert als Auslöser für die Bewässerung.

📊 Beispiel-Kalibrierung: Bei meinem Sensor ergaben sich diese Werte: Luft = 3200, Wasser = 1100, feuchte Blumenerde = 1400, trockene Blumenerde (Pflanze dürstet) = 2600. Der Bewässerungs-Schwellwert liegt also bei etwa 2400. Deine Werte werden abweichen – kalibriere unbedingt selbst!

Projekt 2: Mittelstufe – Automatische Pumpensteuerung mit Relais und OLED-Display

Jetzt wird es praktisch: Wir schalten eine Pumpe automatisch ein, wenn die Erde zu trocken ist. Ein OLED-Display zeigt den aktuellen Status an.

🛒 Benötigte Komponenten für Stufe 2

Warum ein Relais?

Der ESP32 kann an seinen GPIO-Pins nur 3,3V mit maximal 20 mA liefern. Eine Pumpe braucht aber oft 5V–12V und mehrere hundert Milliampere. Das KY-019 Relais fungiert als elektronischer Schalter: Der ESP32 schaltet mit seinem schwachen Signal das Relais, und das Relais schaltet den Starkstromkreis der Pumpe. Wichtig: Das KY-019 ist ein High-Level-Trigger-Modul – es schaltet bei HIGH ein und bei LOW aus.

⚡ Sicherheitshinweis: Betreibe die Pumpe immer über ein separates Netzteil – niemals direkt über den 5V-Pin des ESP32! Der Spannungsregler auf dem DevKit ist nicht für die Stromaufnahme einer Pumpe ausgelegt und könnte durchbrennen.

Verkabelung (Projekt 2)

Komponente Pin ESP32-Pin
Bodenfeuchte-Sensor VCC / GND / AOUT 3.3V / GND / GPIO 34
OLED-Display (I²C) VCC / GND 3.3V / GND
OLED-Display (I²C) SDA / SCL GPIO 21 / GPIO 22
Relais KY-019 VCC / GND / SIG 5V / GND / GPIO 26
Relais (Schaltkontakt) COM / NO Pumpe und externes Netzteil (in Reihe)

Code für Relais-Steuerung mit OLED-Status

Zusätzlich zur Arduino-IDE-Bibliothek WiFi.h brauchst du nun die Bibliotheken Adafruit SSD1306 und Adafruit GFX Library. Installiere sie über den Bibliotheksverwalter (Sketch → Bibliothek einbinden → Bibliotheken verwalten).

/*
 * Projekt 2: Automatische Bewässerung mit OLED-Display
 * Schaltet Pumpe per Relais bei Trockenheit
 * Zeigt Status auf 0.96" OLED (SSD1306, I²C)
 */

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// ---- Pin-Definitionen ----
#define SENSOR_PIN     34    // Bodenfeuchte-Sensor
#define RELAIS_PIN     26    // Relais-Steuerung

// ---- Kalibrierwerte (nach eigener Kalibrierung anpassen!) ----
#define TROCKEN_WERT   2600
#define NASS_WERT      1400
#define SCHWELLWERT    35    // Prozent unterhalb => Pumpe an

// ---- OLED-Display ----
#define SCREEN_WIDTH   128
#define SCREEN_HEIGHT  64
#define OLED_ADDR      0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// ---- Bewässerungs-Logik ----
unsigned long letzteMessung   = 0;
unsigned long pumpenStart     = 0;
const unsigned long MESS_INTERVALL = 5000;   // Alle 5 Sekunden messen
const unsigned long PUMPEN_DAUER   = 8000;   // Pumpe 8 Sekunden laufen lassen
const unsigned long RUHE_ZEIT      = 60000;  // 1 Minute warten nach Bewässerung
bool pumpeAktiv = false;
bool inRuhezeit = false;

void setup() {
  Serial.begin(115200);

  // ADC einrichten
  analogReadResolution(12);

  // Relais-Pin
  pinMode(RELAIS_PIN, OUTPUT);
  digitalWrite(RELAIS_PIN, LOW);  // Relais aus (HIGH-Trigger!)

  // OLED initialisieren
  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
    Serial.println(F("OLED nicht gefunden! Prüfe Verkabelung."));
    while (true);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println(F("Bewaesserung v2"));
  display.println(F("Starte..."));
  display.display();
  delay(2000);
}

void loop() {
  unsigned long jetzt = millis();

  // Nur messen, wenn nicht in Ruhezeit und Intervall erreicht
  if (!inRuhezeit && (jetzt - letzteMessung >= MESS_INTERVALL)) {
    letzteMessung = jetzt;

    // Sensor lesen und umrechnen
    int rohWert = analogRead(SENSOR_PIN);
    int feuchtigkeit = map(rohWert, TROCKEN_WERT, NASS_WERT, 0, 100);
    feuchtigkeit = constrain(feuchtigkeit, 0, 100);

    // Bewässerungsentscheidung
    if (feuchtigkeit < SCHWELLWERT && !pumpeAktiv) {
      starteBewaesserung();
    }

    // OLED aktualisieren
    aktualisiereDisplay(feuchtigkeit, rohWert);

    // Debug-Ausgabe
    Serial.printf("Roh: %d | Feuchte: %d%% | Pumpe: %s | Ruhe: %s\n",
                  rohWert, feuchtigkeit,
                  pumpeAktiv ? "AN" : "AUS",
                  inRuhezeit ? "JA" : "NEIN");
  }

  // Pumpe nach Ablauf der Pumpzeit ausschalten
  if (pumpeAktiv && (jetzt - pumpenStart >= PUMPEN_DAUER)) {
    stoppeBewaesserung();
  }

  // Ruhezeit nach Bewässerung abwarten
  if (inRuhezeit && (jetzt - pumpenStart >= RUHE_ZEIT)) {
    inRuhezeit = false;
    Serial.println("Ruhezeit beendet – Sensor wieder aktiv.");
  }
}

void starteBewaesserung() {
  pumpeAktiv = true;
  pumpenStart = millis();
  digitalWrite(RELAIS_PIN, HIGH);
  Serial.println(">>> Pumpe eingeschaltet! <<<");
}

void stoppeBewaesserung() {
  digitalWrite(RELAIS_PIN, LOW);
  pumpeAktiv = false;
  inRuhezeit = true;
  Serial.println("<<< Pumpe ausgeschaltet – Ruhezeit beginnt. <<<");
}

void aktualisiereDisplay(int feuchte, int rohWert) {
  display.clearDisplay();
  display.setTextSize(2);

  // Große Prozentanzeige zentriert
  int xPos = 40;
  display.setCursor(xPos, 5);
  display.print(feuchte);
  display.println(F("%"));

  display.setTextSize(1);
  display.setCursor(0, 32);
  display.print(F("Rohwert: "));
  display.println(rohWert);

  // Statusleiste unten
  display.setCursor(0, 48);
  if (pumpeAktiv) {
    display.print(F("PUMPE AKTIV"));
  } else if (inRuhezeit) {
    display.print(F("Ruhephase..."));
  } else if (feuchte < SCHWELLWERT) {
    display.print(F("TROCKEN -> Bald giessen"));
  } else {
    display.print(F("Alles ok - feucht"));
  }

  // Fortschrittsbalken für Feuchtigkeit
  int balkenBreite = map(feuchte, 0, 100, 0, 128);
  display.fillRect(0, 58, balkenBreite, 6, SSD1306_WHITE);

  display.display();
}

So funktioniert die Steuerung

Der Code enthält eine durchdachte Bewässerungslogik:

  • Messintervall: Alle 5 Sekunden wird der Sensor ausgelesen.
  • Einschaltschwelle: Fällt die Feuchtigkeit unter 35 %, schaltet das Relais die Pumpe ein.
  • Feste Pumpendauer: Die Pumpe läuft 8 Sekunden (je nach Pumpenleistung anpassbar).
  • Ruhezeit nach Bewässerung: Nach dem Ausschalten wird 60 Sekunden gewartet, damit sich das Wasser in der Erde verteilen kann. Ohne diese Pause würde der Sensor sofort wieder Trockenheit messen und eine Endlos-Schleife auslösen.
  • OLED-Visualisierung: Das Display zeigt den Feuchtigkeitswert in Prozent, den Rohwert, den aktuellen Status und einen grafischen Feuchtigkeitsbalken.

Projekt 3: Fortgeschritten – Web-Dashboard mit Live-Daten per WLAN

Die Königsklasse: Ein Webserver auf dem ESP32 zeigt alle Daten in einem schicken Dashboard an – erreichbar von jedem Gerät im gleichen WLAN. Perfekt, um vom Sofa aus zu checken, ob die Pflanzen Wasser brauchen!

🛒 Benötigte Komponenten für Stufe 3

Code für den ESP32-Webserver mit Live-Dashboard

Der ESP32 erstellt einen eigenen Access Point oder verbindet sich mit deinem WLAN. Über einen integrierten Webserver liefert er eine responsive HTML-Seite mit den aktuellen Sensorwerten aus – inklusive Echtzeit-Aktualisierung per Meta-Refresh. Die Pumpe kann auch manuell per Knopfdruck gesteuert werden.

/*
 * Projekt 3: Web-Dashboard mit Live-Bodenfeuchte und Pumpensteuerung
 * ESP32 als WiFi-Webserver mit responsive Dashboard
 * 
 * Benötigte Bibliotheken: WiFi.h, Wire.h, Adafruit GFX, Adafruit SSD1306
 */

#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// ---- WLAN-Zugangsdaten ----
const char* ssid     = "DEIN_WLAN_NAME";
const char* passwort = "DEIN_WLAN_PASSWORT";

// ---- Webserver auf Port 80 ----
WiFiServer server(80);

// ---- Pin-Definitionen ----
#define SENSOR_PIN     34
#define RELAIS_PIN     26

// ---- OLED ----
#define SCREEN_WIDTH  128
#define SCREEN_HEIGHT 64
#define OLED_ADDR     0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// ---- Kalibrierwerte ----
#define TROCKEN_WERT  2600
#define NASS_WERT     1400
#define SCHWELLWERT   35

// ---- Zustandsvariablen ----
unsigned long letzteMessung  = 0;
unsigned long pumpenStart    = 0;
const unsigned long MESS_INTERVALL = 5000;
const unsigned long PUMPEN_DAUER   = 8000;
const unsigned long RUHE_ZEIT      = 60000;
bool pumpeAktiv = false;
bool inRuhezeit = false;
bool manuellModus = false;
int aktuelleFeuchte = 0;
int aktuellerRohWert = 0;
int bewasserungenHeute = 0;
unsigned long letzteBewaesserung = 0;

// ---- IP-Adresse merken ----
String ipAdresse = "";

void setup() {
  Serial.begin(115200);

  analogReadResolution(12);
  pinMode(RELAIS_PIN, OUTPUT);
  digitalWrite(RELAIS_PIN, LOW);

  // OLED
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);

  // WLAN verbinden
  display.setCursor(0, 0);
  display.println("Verbinde WLAN...");
  display.display();

  WiFi.begin(ssid, passwort);
  int versuche = 0;
  while (WiFi.status() != WL_CONNECTED && versuche < 30) {
    delay(500);
    Serial.print(".");
    versuche++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    ipAdresse = WiFi.localIP().toString();
    server.begin();
    Serial.println("\nWiFi verbunden!");
    Serial.print("Web-Dashboard erreichbar unter: http://");
    Serial.println(ipAdresse);

    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("WiFi OK!");
    display.print("http://");
    display.println(ipAdresse);
    display.display();
  } else {
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("WiFi fehlgeschlagen!");
    display.display();
  }
}

void loop() {
  unsigned long jetzt = millis();

  // ---- Sensormessung ----
  if (!inRuhezeit && (jetzt - letzteMessung >= MESS_INTERVALL)) {
    letzteMessung = jetzt;
    aktuellerRohWert = analogRead(SENSOR_PIN);
    aktuelleFeuchte = map(aktuellerRohWert, TROCKEN_WERT, NASS_WERT, 0, 100);
    aktuelleFeuchte = constrain(aktuelleFeuchte, 0, 100);

    // Auto-Bewässerung (nur wenn kein manueller Modus)
    if (!manuellModus && aktuelleFeuchte < SCHWELLWERT && !pumpeAktiv) {
      starteBewaesserung();
      bewasserungenHeute++;
      letzteBewaesserung = jetzt;
    }
  }

  // Pumpe nach Pumpzeit ausschalten
  if (pumpeAktiv && (jetzt - pumpenStart >= PUMPEN_DAUER)) {
    stoppeBewaesserung();
  }

  // Ruhezeit beenden
  if (inRuhezeit && (jetzt - pumpenStart >= RUHE_ZEIT)) {
    inRuhezeit = false;
    manuellModus = false;  // Nach Ruhezeit zurück in Automatik
  }

  // OLED aktualisieren
  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(30, 5);
  display.print(aktuelleFeuchte);
  display.println(F("%"));
  display.setTextSize(1);
  display.setCursor(0, 32);
  display.print(F("IP: "));
  display.println(ipAdresse);
  display.setCursor(0, 48);
  if (pumpeAktiv) display.print(F("PUMPE AN"));
  else if (inRuhezeit) display.print(F("Ruhephase..."));
  else display.print(F("Bereit"));
  display.display();

  // ---- Web-Client behandeln ----
  WiFiClient client = server.available();
  if (client) {
    String request = client.readStringUntil('\r');
    client.flush();
    Serial.println("HTTP: " + request);

    // Manuelle Pumpensteuerung per URL
    if (request.indexOf("/pumpe=an") != -1 && !pumpeAktiv && !inRuhezeit) {
      manuellModus = true;
      starteBewaesserung();
      bewasserungenHeute++;
      letzteBewaesserung = jetzt;
    }
    else if (request.indexOf("/pumpe=aus") != -1 && pumpeAktiv) {
      stoppeBewaesserung();
    }
    else if (request.indexOf("/reset") != -1) {
      bewasserungenHeute = 0;
    }

    // HTML-Dashboard ausliefern
    String html = buildDashboard();
    client.println("HTTP/1.1 200 OK");
    client.println("Content-Type: text/html; charset=UTF-8");
    client.println("Connection: close");
    client.println("Refresh: 3");  // Alle 3 Sekunden aktualisieren
    client.println();
    client.println(html);
    client.stop();
  }
}

String buildDashboard() {
  String statusText;
  String statusFarbe;
  if (pumpeAktiv) {
    statusText = "Pumpe läuft!";
    statusFarbe = "#4ade80";
  } else if (inRuhezeit) {
    statusText = "Ruhephase";
    statusFarbe = "#f59e0b";
  } else if (aktuelleFeuchte < SCHWELLWERT) {
    statusText = "Zu trocken!";
    statusFarbe = "#ef4444";
  } else {
    statusText = "Optimal";
    statusFarbe = "#4ade80";
  }

  String feuchteFarbe;
  if (aktuelleFeuchte < 25) feuchteFarbe = "#ef4444";
  else if (aktuelleFeuchte < 50) feuchteFarbe = "#f59e0b";
  else feuchteFarbe = "#4ade80";

  unsigned long sekundenSeitLetzter = letzteBewaesserung > 0
    ? (millis() - letzteBewaesserung) / 1000
    : 9999;

  String seit = "Nie";
  if (sekundenSeitLetzter < 120)
    seit = String(sekundenSeitLetzter) + " Sekunden";
  else if (sekundenSeitLetzter < 7200)
    seit = String(sekundenSeitLetzter / 60) + " Minuten";
  else
    seit = String(sekundenSeitLetzter / 3600) + " Stunden";

  String html = R"rawliteral(
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>makeroo – Pflanzenbewässerung</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background: #101520;
    color: #e0e6ed;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    padding: 1rem;
  }
  .dashboard {
    max-width: 550px;
    width: 100%;
    background: linear-gradient(135deg, #141b2d 0%, #1a2332 100%);
    border: 1px solid #252e42;
    border-radius: 16px;
    padding: 2rem;
    box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  }
  h1 {
    font-size: 1.5rem;
    color: #fff;
    margin-bottom: 0.25rem;
  }
  .subtitle {
    color: #8b95a5;
    font-size: 0.85rem;
    margin-bottom: 1.5rem;
  }
  .gauge {
    text-align: center;
    margin: 1.5rem 0;
    position: relative;
  }
  .gauge-value {
    font-size: 5rem;
    font-weight: 700;
    color: )rawliteral"
+ feuchteFarbe + R"rawliteral(;
    line-height: 1;
  }
  .gauge-label {
    color: #8b95a5;
    font-size: 0.9rem;
    margin-top: 0.25rem;
  }
  .gauge-bar {
    width: 100%;
    height: 12px;
    background: #1a2332;
    border-radius: 6px;
    overflow: hidden;
    margin-top: 0.75rem;
  }
  .gauge-bar-fill {
    height: 100%;
    background: )rawliteral"
+ feuchteFarbe + R"rawliteral(;
    border-radius: 6px;
    transition: width 0.5s ease;
    width: )rawliteral"
+ String(aktuelleFeuchte) + R"rawliteral(%;
  }
  .status {
    background: #1a2332;
    border-radius: 10px;
    padding: 1rem;
    margin: 1rem 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .status-badge {
    background: )rawliteral"
+ statusFarbe + R"rawliteral(;
    color: #0A0E14;
    padding: 0.3rem 0.8rem;
    border-radius: 20px;
    font-weight: 600;
    font-size: 0.85rem;
  }
  .info-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
    margin: 1rem 0;
  }
  .info-card {
    background: #1a2332;
    border-radius: 10px;
    padding: 0.75rem;
  }
  .info-card .label {
    font-size: 0.75rem;
    color: #8b95a5;
    text-transform: uppercase;
    letter-spacing: 0.5px;
  }
  .info-card .value {
    font-size: 1.1rem;
    font-weight: 600;
    color: #fff;
    margin-top: 0.25rem;
  }
  .btn-row {
    display: flex;
    gap: 0.75rem;
    margin-top: 1.25rem;
  }
  .btn {
    flex: 1;
    padding: 0.75rem;
    border: none;
    border-radius: 10px;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    transition: all 0.2s ease;
  }
  .btn-pump {
    background: #3b82f6;
    color: #fff;
  }
  .btn-reset {
    background: transparent;
    color: #8b95a5;
    border: 1px solid #252e42;
  }
  .btn:hover {
    opacity: 0.85;
    transform: translateY(-1px);
  }
  .footer {
    text-align: center;
    margin-top: 1.5rem;
    font-size: 0.75rem;
    color: #8b95a5;
  }
  .footer a {
    color: #3b82f6;
    text-decoration: none;
  }
</style>
</head>
<body>
<div class="dashboard">
  <h1>🌿 makeroo Bewässerung</h1>
  <div class="subtitle">Aktualisiert sich automatisch alle 3 Sekunden</div>

  <div class="gauge">
    <div class="gauge-value">)rawliteral"
+ String(aktuelleFeuchte) + R"rawliteral(%</div>
    <div class="gauge-label">Bodenfeuchte</div>
    <div class="gauge-bar">
      <div class="gauge-bar-fill"></div>
    </div>
  </div>

  <div class="status">
    <span style="color:#c5cdd8;">Systemstatus</span>
    <span class="status-badge">)rawliteral"
+ statusText + R"rawliteral(</span>
  </div>

  <div class="info-grid">
    <div class="info-card">
      <div class="label">Sensor-Rohwert</div>
      <div class="value">)rawliteral"
+ String(aktuellerRohWert) + R"rawliteral(</div>
    </div>
    <div class="info-card">
      <div class="label">Bewässerungen heute</div>
      <div class="value">)rawliteral"
+ String(bewasserungenHeute) + R"rawliteral(×</div>
    </div>
    <div class="info-card">
      <div class="label">Letzte Bewässerung</div>
      <div class="value">)rawliteral"
+ seit + R"rawliteral(</div>
    </div>
    <div class="info-card">
      <div class="label">WiFi Signal</div>
      <div class="value">)rawliteral"
+ String(WiFi.RSSI()) + R"rawliteral( dBm</div>
    </div>
  </div>

  <div class="btn-row">
    <a href="/pumpe=an" class="btn btn-pump">💧 Jetzt gießen</a>
    <a href="/reset" class="btn btn-reset">Zähler reset</a>
  </div>

  <div class="footer">
    Powered by <a href="https://makeroo.de" target="_blank">makeroo.de</a>
    · ESP32 · )rawliteral"
+ ipAdresse + R"rawliteral(
  </div>
</div>
</body>
</html>
)rawliteral";

  return html;
}

void starteBewaesserung() {
  pumpeAktiv = true;
  pumpenStart = millis();
  digitalWrite(RELAIS_PIN, HIGH);
}

void stoppeBewaesserung() {
  digitalWrite(RELAIS_PIN, LOW);
  pumpeAktiv = false;
  inRuhezeit = true;
}

Nach dem Upload verbindet sich der ESP32 mit deinem WLAN und gibt die IP-Adresse im Serial Monitor aus. Öffne diese Adresse im Browser – und du hast dein eigenes Pflanzen-Dashboard!

🌐 Dashboard-Funktionen auf einen Blick: Live-Feuchtigkeitsanzeige mit farbcodiertem Balken · Systemstatus (Pumpe/OK/Trocken) · Sensor-Rohwert · Bewässerungszähler · Zeit seit letzter Bewässerung · Wi-Fi-Signalstärke · Manueller Gieß-Button · Automatische Aktualisierung alle 3 Sekunden · Responsive – funktioniert auf Handy und Desktop

Praktische Tipps für den dauerhaften Einsatz

🛡️ Sensor-Lebensdauer maximieren

Der kapazitive Sensor ist zwar korrosionsbeständig, aber nicht für dauerhaftes Untertauchen in feuchter Erde konzipiert. Stecke den Sensor nur so tief in die Erde, dass die Messfläche bedeckt ist, aber die Elektronik oberhalb trocken bleibt. Für Langzeitprojekte empfiehlt es sich, den Sensor nur periodisch zu aktivieren – z. B. alle 15 Minuten messen und dann den Strom zum Sensor per MOSFET oder GPIO-Pin abschalten. So hält der Sensor jahrelang.

🔋 Stromversorgung für Pumpe und ESP32

Die Pumpe braucht unbedingt ein eigenes Netzteil. Ein häufiger Fallstrick: Wenn du eine 12V-Pumpe verwendest und nur ein 5V-USB-Netzteil zur Hand hast, hilft dir unser MT3608 Step-Up-Modul. Es wandelt 5V effizient in bis zu 28V um – perfekt, um eine 12V-Pumpe aus einer USB-Powerbank oder einem alten Handynetzteil zu betreiben.

🌡️ Erweiterung: Klimaüberwachung mit DHT22

Warum nur die Bodenfeuchte messen? Mit einem DHT22 Temperatur- und Feuchtigkeitssensor kannst du zusätzlich die Lufttemperatur und Luftfeuchtigkeit um deine Pflanzen herum erfassen. Gerade bei empfindlichen Pflanzen wie Orchideen oder Kräutern ist das Raumklima entscheidend. Der DHT22 lässt sich mit nur drei Drähten (VCC, GND, DATA) und einer zusätzlichen Library einbinden und die Werte im Dashboard anzeigen.

⚠️ Nicht dauerhaft im Wasser: Stecke den Sensor nicht dauerhaft in dauerhaft feuchte oder gar staunasse Erde. Der Sensor ist spritzwassergeschützt, aber nicht für dauerhafte Unterwasser-Bedingungen ausgelegt. Bei Hydrokultur-Setups den Sensor oberhalb des Wasserpegels platzieren.

Alles auf einen Blick – die Gesamtausstattung

Hier findest du alle Produkte, die du für die drei Projektstufen brauchst, gesammelt mit Links in unseren Shop. Klick dich durch und stell dir dein persönliches Bewässerungssystem zusammen!

Komponente Projekt-Stufe Link
Kapazitiver Bodenfeuchte-Sensor 1, 2, 3 Zum Produkt
ESP32 DevKit (USB-C) 1, 2, 3 Zum Produkt
1-Kanal-Relais KY-019 2, 3 Zum Produkt
0,96" OLED-Display 2, 3 Zum Produkt
DHT22 Temperatur-Sensor 3 (optional) Zum Produkt
MT3608 Step-Up-Modul 2, 3 (optional) Zum Produkt

Stöbere auch in unseren Kategorieseiten für weitere passende Komponenten:

Bereit für dein smartes Bewässerungssystem?

Mit den Komponenten aus unserem Shop und diesem Guide bist du bestens ausgerüstet. Egal ob du nur mal die Feuchtigkeit checken willst (Projekt 1), eine automatische Pumpe mit Display möchtest (Projekt 2) oder ein vollwertiges Web-Dashboard aufbaust (Projekt 3) – der Einstieg ist einfacher, als du denkst.

Alle genannten Produkte findest du in unserem Shop: makeroo.de

Hast du Fragen zu den Komponenten oder zum Aufbau? Schreib uns einfach – wir helfen gern! 🚀

Fazit

Ein automatisches Bewässerungssystem mit ESP32 und kapazitivem Bodenfeuchte-Sensor ist kein Hexenwerk. Mit den drei in diesem Artikel vorgestellten Ausbaustufen findest du den für dich passenden Einstieg:

  • Projekt 1 gibt dir einen schnellen, funktionierenden Feuchtigkeitsmesser für kleines Geld.
  • Projekt 2 automatisiert die Bewässerung und zeigt den Status lokal auf einem OLED-Display an.
  • Projekt 3 bringt deine Pflanzen ins WLAN und gibt dir ein professionelles Dashboard – perfekt für Tech-Enthusiasten und alle, die ihre Pflanzen auch aus der Ferne im Blick haben wollen.

Der größte Hebel für ein zuverlässiges System ist die Wahl des richtigen Sensors. Setze auf einen kapazitiven Sensor – er kostet nur minimal mehr, hält aber ungleich länger und liefert stabile Messwerte ohne Korrosionsprobleme. Der zweite wichtige Punkt ist die sorgfältige Kalibrierung: Nimm dir die Zeit, deine persönlichen Schwellwerte für trocken und nass zu ermitteln. Nur so trifft dein System die richtigen Entscheidungen.

Von hier aus stehen dir alle Türen offen: Erweitere das System um einen DHT22 für kombinierte Klimaüberwachung, integriere eine Akku-Versorgung mit dem MT3608 Step-Up-Modul, oder verbinde das Ganze mit MQTT und Home Assistant für die ultimative Smart-Home-Integration. Deiner Kreativität sind keine Grenzen gesetzt.

Viel Spaß beim Bauen – deine Pflanzen werden es dir danken! 🌱

Zurück zum Blog