2 Automatisierte Gießanlage
2.1 Einführung
In diesem Projekt wird eine automatische Bewässerungsanlage realisiert, die mithilfe eines ESP8266 (D1 Mini) und eines Feuchtigkeitssensors den Feuchtigkeitsgehalt der Erde misst und eine Pumpe steuert. Sobald die Feuchtigkeit unter einen bestimmten Wert fällt, wird die Pumpe aktiviert, um die Pflanze mit Wasser zu versorgen.
Zusätzlich wird ein kleines OLED-Display verwendet, um Echtzeitdaten zur Bodenfeuchtigkeit und dem Betriebsstatus anzuzeigen.
2.2 Schaltplan
2.3 Hardware
2.3.1 D1 Mini (ESP8266)
Der ESP8266 D1 mini ist eine sogenannte „Physical-Computing-Platform“. Das Modul besteht aus zwei Komponenten, einem USB-Seriell-Konverter (CH340) sowie einem Mikrocontroller mit TCP/IP-Stack (ESP8266). Außerdem hat es einen Mikro-USB-Buchse zur Energieversorgung und als Programmier-Schnittstelle, eine steuerbare LED sowie ein Festspannungsregler.
2.3.2 Feuchtigkeitssensor
Der Bodengeuchtesensor Hygrometer Modul V1.2 ist ein einfacher sEnsor zur Messung der Bodenfeuchtigkeit. Er hat eine Spannunsgversorgung von 3,3 V - 5 V. und der Ausgang ist ein Analogsignal(AO). Das Messprinzip vom Sensor ist Kapazitiv, der ist deswegen besser als Widerstandsbasierte Sensoren, da keine Korrosion stattfindet, und somit auch eine längere Lebensdauer.
Der Sensor funktioniert folgender Maßen. Der Sensor gibt eine analoge Spannung aus, die sich mit der Bodenfeuchtigkeit verändert. Das heißt, wenn der Boden trocken ist, ist ein hoher Widerstand da, was auch eine Hoch Spannung zu folge hat. Das Gegenteil findet dann bei feuchtem Boden statt. Wir haben einen niedrigen Widerstand und somit auch eine niedrige Spannung.
2.3.3 Wasserpumpe
Die Wasserpumpe hat eine Betriebsspannung von DC 3 V und ca. 1,2 Liter pro Minute. Die Pumpe ist sehr einfach in das System zu integrieren.
2.3.4 OLED-Display
Das Display hat eine Auflösung von 128x64 Pixel. Integriert ist ein SH1106 Chip und die Betriebsspannung liegt bei 3,3V - 5V. Die Besonderheit von diesem OLED-Display ist der Hohe Kontrast für diesen Preis. Und durch die I²C(IIC) Schnittstelle lässt sich das Display mit vier Pins an den Mikrocontroller anbinden. Ein weiterer Vorteil ist die große Bibliotheken verfügbarkeit vom SH1106-Controller.
2.3.5 Relai-Modul
Wir verwenden dazu noch ein KF-301 1-Relais-Modul. Die Betriebsspannung liegt bei 3,3 V - 5 V. Es handelt sich hier um mechanisches Relai. Die Schaltspannung ist bei AC mac. 250V / 10A und bei DC max. 30V / 10A. Wichtig ist auch zu beachten das daass Relai ein LOW-aktiv ist, das heißt das Relai schaltet, wenn IN auf LOW ist. Die Pin-Belegung ist folgendermaßen: VCC ist die Versorgungsspannung / GND ist die Masse / IN Steuereingang. Die Schaltkontakte sind folgendermaßen : COM ist der gemeinsame Kontakt / NO (Normally Open) Kontakt ist offen, bis Relai schaltet / NC (Normally Close) Kontakt ist geschlossen, bis Relai schaltet.
2.4 Software
2.4.1 Einbinden von Bibliotheken
Als erstes referenzieren wir dir Bibliotheken “Wire.h” und “U8g2lib.h” um das Display ansteuern zu können.
#include <Wire.h>
#include <U8g2lib.h>2.4.2 Display Initialisierung
Anschlließend definieren wir Konstanten um das Display einzubinden: - SCREEN_WIDTH: Definiert die Breite des OLED Bildschirms - SCREEN_HEIGHT: Definiert die Höhe des Bildschirms - I2C_ADDRESS: Gibt die Adresse des I2C Busses an, welcher verwendet wird um das Display anzusteuern
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define I2C_ADDRESS 0x3C
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);2.4.3 Kalibrierung des Feuchtigkeitssensor
const int AirValue = 1024;
const int WaterValue = 590;Diese Werte stammen aus einer vorherigen Kalibrierung des Sensors mit Luft (trocken) und Wasser (nass).
2.4.4 Pumpenschwelle definieren
const int PumpThreshold = 50;Die Pumpe wird aktiviert, wenn die Bodenfeuchtigkeit unter 50% fällt.
2.4.5 Variablen für Sensorwerte und Cooldown
int moistureValue = 0;
int moisturePercentage = 0;
bool pumpActive = false;
unsigned long lastPumpTime = 0;
const unsigned long pumpCooldown = 15UL * 60UL * 1000UL;Hier werden Variablen zur Speicherung der Sensorwerte sowie zur Steuerung des cooldowns der Pumpe definiert. Nachdem die Pumpe aktiviert wurde, soll das System für eine gewisse Zeit (hier 15 Minuten) die Pumpe nicht erneut starten, damit das Wasser Zeit hat sich zu verteilen und die Pflanze dadurch nicht überwässert wird. Dazu wird der Zeitpunkt der letzten Pumpen-Aktivierung in ‘lastPumpTime’ gespeichert und später im Code mit der ‘pumpCooldown’ Zeit verglichen.
2.4.6 Setup-Funktion
void setup() {
Serial.begin(115200);
u8g2.begin();
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
pinMode(D4, OUTPUT);
}Hier wird die serielle Kommunikation mit einer Baudrate von 115200 gestartet, das OLED-Display initialisiert und der Pin der zur Pumpensteuerung verwendet wird als Ausgang gesetzt.
2.4.7 Hauptprogramm (Loop)
void loop() {
moistureValue = analogRead(A0);
moisturePercentage = map(moistureValue, AirValue, WaterValue, 0, 100);Zunächst wird der Feuchtigkeitswert vom Sensor eingelesen und in eine Prozentangabe umgewandelt.
2.4.8 Pumpe steuern
unsigned long currentMillis = millis();
bool cooldownActive = (currentMillis - lastPumpTime) < pumpCooldown;
unsigned long remainingCooldown = 0;Zuerst wird überprüft ob die letzte Pumpen-Aktivierung länger als die oben definierte ‘pumpCooldown’ Zeit ist.
if (cooldownActive) {
remainingCooldown = (pumpCooldown - (currentMillis - lastPumpTime)) / 1000UL;
}Sollte der Cooldown noch aktiv sein, wird hier die verbleibende Zeit errechnet um sie später auf dem OLED Bildschrim anzuzeigen.
// activate pump if moisture is under 50% and cooldown is not active
if (moisturePercentage < 50 && !cooldownActive) {
pumpActive = true;
digitalWrite(D4, LOW);
delay(4000); // run pump for 4 seconds
digitalWrite(D4, HIGH);
pumpActive = false;
lastPumpTime = millis(); // reset cooldown
} else {
pumpActive = false;
digitalWrite(D4, HIGH);
}Wenn die Feuchtigkeit unter 50% liegt, wird die Pumpe für 4 Sekunden aktiviert und der aktuelle Zeitpunkt als ‘lastPumpTime’ für den cooldown gespeichert.
2.4.9 OLED-Display aktualisieren
char output[50];
sprintf(output, "Moist.: %d%%", moisturePercentage);
char pumpStatus[50];
char cooldownStr[50];
if (cooldownActive) {
sprintf(pumpStatus, "Cooldown: ");
sprintf(cooldownStr, "%d sec", remainingCooldown);
} else {
sprintf(pumpStatus, "Active: %d", pumpActive);
sprintf(cooldownStr, "");
}
u8g2.clearBuffer();
u8g2.drawStr(5, 20, output);
u8g2.drawStr(5, 40, pumpStatus);
u8g2.drawStr(5, 60, cooldownStr);
u8g2.sendBuffer();
delay(1000);Abschließend werden die Sensorwerte formatiert und auf dem Display angezeigt. Auch die verbleibende Zeit des cooldowns bzw. der aktuelle Zustand der Pumpe wird bei Bedarf angezeigt. Das Display wird dabei jede Sekunde aktualisiert.
2.4.10 Gesamter Code
#include <Wire.h>
#include <U8g2lib.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define I2C_ADDRESS 0x3C
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// values from calibrating
// (with a cup of water)
const int AirValue = 1024;
const int WaterValue = 590;
const int PumpThreshold = 50;
int moistureValue = 0;
int moisturePercentage = 0;
bool pumpActive = false;
unsigned long lastPumpTime = 0;
const unsigned long pumpCooldown = 15UL * 60UL * 1000UL; // 15 minutes in milliseconds
// const unsigned long pumpCooldown = 10UL * 1000UL; // 10 seconds in milliseconds for testing purposes
void setup() {
Serial.begin(115200);
u8g2.begin();
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
pinMode(D4, OUTPUT);
}
void loop() {
moistureValue = analogRead(A0);
moisturePercentage = map(moistureValue, AirValue, WaterValue, 0, 100);
unsigned long currentMillis = millis();
bool cooldownActive = (currentMillis - lastPumpTime) < pumpCooldown;
unsigned long remainingCooldown = 0;
if (cooldownActive) {
remainingCooldown = (pumpCooldown - (currentMillis - lastPumpTime)) / 1000UL;
}
// activate pump if moisture is under 50% and cooldown is not active
if (moisturePercentage < 50 && !cooldownActive) {
pumpActive = true;
digitalWrite(D4, LOW);
delay(4000); // run pump for 4 seconds
digitalWrite(D4, HIGH);
pumpActive = false;
lastPumpTime = millis(); // reset cooldown
} else {
pumpActive = false;
digitalWrite(D4, HIGH);
}
// Display text
char output[50];
sprintf(output, "Moist.: %d%%", moisturePercentage);
char pumpStatus[50];
char cooldownStr[50];
if (cooldownActive) {
sprintf(pumpStatus, "Cooldown: ");
sprintf(cooldownStr, "%d sec", remainingCooldown);
} else {
sprintf(pumpStatus, "Active: %d", pumpActive);
sprintf(cooldownStr, "");
}
u8g2.clearBuffer();
u8g2.drawStr(5, 20, output);
u8g2.drawStr(5, 40, pumpStatus);
u8g2.drawStr(5, 60, cooldownStr);
u8g2.sendBuffer();
delay(1000);
}