Pflanzen-Bewässerungssystem

Motivation

Wer in seiner Wohnung Pflanzen hat, kennt das Problem: Man verlässt die Wohnung für eine Weile, aber trotzdem müssen die Pflanzen gegossen werden. Um die Sicherheit zu haben, dass die Pflanzen nicht austrocknen oder sie sogar automatisch zu bewässern, kann man Mikrocontroller einsetzen.

Funktionsweise

Folgender Code ermöglicht es, mithilfe eines Feuchtigkeitssensors und Mikrocontrollers den Feuchtigkeitsgrad der Pflanzenerde zu ermitteln und diesen an einen MQTT-Server zu übertragen, um die Werte weltweit ablesen zu können und sogar Nachrichten per Telegram zu versenden. Sobald die Pflanzenerde für ein bestimmtes Zeitintervall einen definierten Feuchtigkeitswert unterschreitet, wird man über das Smartphone benachrichtigt, dass die Pflanze gegossen werden muss.

Anleitung

Hardware

Benötigt werden folgende Komponenten:

  • ESP32 Mikrocontroller
  • Mikro-USB Kabel
  • USB-Netzteil oder andere Spannungsquelle
  • Kapazitiver Bodenfeuchtesensor
  • 3 x Jumper-Kabel (Male-Female)
  • (Epoxidharz)
  • (3:1 Schrumpfschlauch mit Kleber, max. 30mm Innendurchmesser)

Aufbau

Zuerst müssen die Kontakte des Bodenfeuchtesensors mit den richtigen Pins des ESP32 Boards verbunden werden.

  • GND -> GND
  • VCC -> 5V
  • AOUT -> G32 (oder anderer | In Code berücksichtigen)

Sobald das Board mit der Spannungsquelle verbunden wird und der Code inkl. aller erforderlichen Informationen auf das Board geladen wurde, funktioniert das System auch schon.

Bei der aktuellen Hardwarekonfiguration kann es allerdings noch zu Komplikationen kommen, da der Bodenfeuchtesensor paradoxerweise nicht wasserdicht ist. Dennoch ist ein kapazitiver Sensor im Vergleich zu Sensoren mit offenen Elektroden besser, da letztere in der feuchten Erde schnell korrodieren. Problematisch bei den kapazitiven Sensoren ist jedoch, dass die Platine seitlich nicht abgedichtet ist und sich dadurch unter der Kunststoffbeschichtung Wasser ansammeln und diese aufplatzen kann. Außerdem sind die Komponenten unterhalb des Anschlusses ungeschützt und die Lötstellen korrodieren schnell in der feuchten Umgebung — auch wenn darauf geachtet wird, dass keine der Komponenten in direktem Kontakt mit der feuchten Erde stehen.

Um die Bodenfeuchtesensoren beständiger zu machen, kann man die Platinen mit Epoxidharz bepinseln und anschließend den oberen Teil der Platine mit den Elektrokomponenten zusätzlich mit einem Schrumpfschlauch schützen.

Software

Zu Beginn müssen die aktuelle Arduino IDE sowie die erforderlichen Repositories heruntergeladen und installiert werden.

Zu den erforderlichen Repositories zählen folgende:

Die Tingg und Telegram Dokumentationen zeigen außerdem, wie man die erforderlichen Informationen wie ChatID, BOTtoken, mqttDeviceId und mqttPassword erhält. Alle Variablen unter “Customize” müssen geändert werden. Die Variable “trigger” in Zeile 13 legt dabei fest, ab welchem Bodenfeuchtewert in Prozent eine Meldung über Telegram verschickt werden soll.

Tingg Type Konfiguration

Bei der Tingg Type Konfiguration ist darauf zu achten, dass die Topic-Namen (“Soil_Moisture” und “Level”) mit denen im Code übereinstimmen.

Sollte der analoge Output des Bodenfeuchtesensors nicht am Pin 32 angeschlossen sein, kann alternativ der Pin in Zeile 35 geändert werden.

Da der Bodenfeuchtesensor analog ist, muss das Signal außerdem umgewandelt werden. In den Zeilen 51 und 52 werden die extremalen Werte festgelegt und anschließend auf einen digitalen Wert gemappt (ab Zeile 125). Damit der Mikrocontroller auch die richtigen Werte übermittelt, bietet es sich an, den Sensor vor der Installation ins Wasser zu halten und den Maximalwert zu notieren. Im Codebeispiel lag der maximale Output bei knapp über 1500 bzw. mit Epoxidharz-Coating bei etwa 1300. Die Sensorwerte können im Testbetrieb im Serial Monitor der Arduino IDE ausgelesen werden. Die Verbindung zu einem Computer ist im Normalbetrieb natürlich nicht mehr notwendig.

Da je nach WLAN Verbindungsqualität ab und zu die Verbindung zum Router oder zum MQTT-Server verloren geht und in seltenen Fällen nicht automatisch wiederhergestellt wird, startet der Mikrocontoller nach einem definierten Zeitintervall ohne Verbindung neu und sendet eine Nachricht per Telegram, wenn der Contoller neu gestartet wurde. Die Nachricht kann in Zeile 187 deaktiviert werden.

Code

/*
   Plant Wartering Alert & Monitor
   Components: ESP32, Capacitive Soil Moisture Sensor, Micro-USB Cable, Power Supply, Jumper Wires
   By Andreas Blum Media (https://blummedia.de)
*/

#include <WiFi.h>
#include <PubSubClient.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
#include <ArduinoJson.h>

//------------Customize--------------
#define CHAT_ID "123456789" //Define unique Chat ID of User
#define BOTtoken "1234567890:ABC_1A2B3C4D5E6F7G8H9I" //Define unique ID Token of Telegram Bot


const String plant = "PFLANZENNAME"; //Define unique name of plant
const int trigger = 20; //Trigger Telegram message for moisture level below trigger value in percent

const char* ssid = "SSID_OF_WIFI_ROUTER";
const char* password = "WIFI_PASSWORD";

const char* mqttDeviceId = "UNIQUE_TINGG_DEVICE_ID";
const char* mqttPassword = "DEVICE_PASSWORD";


//----------Check Config Settings----------
// Topics Config
const char* soilMoistureTopic = "Soil_Moisture";
const char* levelTopic = "Level";
const char* subTopic = "Soil_Moisture";

// Tingg Config
const char* mqttServer = "mqtt.tingg.io";
const char* mqttUsername = "thing";
const int port = 1883;

// Pins Config
//const int LightPin = 0;
const int sensorPin = 32;
const int DefaultPin = 2;

WiFiClient espClient;
PubSubClient client(espClient);

//Telegram Wifi Setup
WiFiClientSecure telegramClient;
UniversalTelegramBot bot(BOTtoken, telegramClient);

//Checks for new messages every 1 second.
int botRequestDelay = 1000;
unsigned long lastTimeBotRan;

// Vars
char buf[12];
const int wet = 1500;
const int dry = 0;
int tinggPostDelay = 5000;
unsigned long lastTinggPost;
const int iTemp = 3600 / 5; //Water shortage trigger delay
const int iRestartDelay = 120; //Restart delay if trying to reconnect to MQTT
int iRestart = iRestartDelay; //Restart Counter
int iStep = 0;
int iCounter = iTemp;
int iToggle = 0;

// Function to validate connection with the board
void blinking_loop(){
  while (ssid == "SSID") {
    digitalWrite(DefaultPin,HIGH);
    delay(500);
    digitalWrite(DefaultPin,LOW);
    delay(500);
    Serial.println("Default light should be blinking. Please set up your Wifi configuration.");
  }
}

void setup_wifi() {
  delay(10);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);

  iRestart = iRestartDelay * 5;

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (iRestart <= 0) {
      Serial.print("Restarting...");
      ESP.restart();
    }
    iRestart--;
  }

  randomSeed(micros());

  Serial.println("");
  Serial.print("Congratulations!! WiFi connected on IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("");
  Serial.println("Now, follow up on the steps for the MQTT configuration. ");
}

void reconnect() {
  while (!client.connected()) {
    Serial.println("Attempting to connect MQTT...");
    if (client.connect(mqttDeviceId, mqttUsername, mqttPassword)) {
      Serial.println("connected");
      client.subscribe(subTopic);
      iRestart = iRestartDelay;
    } else {
      if (iRestart <= 0) {
        Serial.print("Restarting...");
        ESP.restart();
      }
      iRestart--;
      Serial.print("iRestart = ");
      Serial.println(iRestart);
      Serial.print(" Still not connected...");      // Serial.print(client.state());
      Serial.println(" trying again in 5 seconds"); // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

//Telegram Functions
// Get sensor readings and return them as a String variable
String getReadings(){
  int sensorVal = 4095 - analogRead(sensorPin);
  int moisturePercentage = map(sensorVal, dry, wet, 0, 100);

  if (moisturePercentage > 100) {
    moisturePercentage = 100;
  } else if (moisturePercentage < 0) {
    moisturePercentage = 0;
  }
  
  String message = plant + "\n";
  message += "Moisture: " + String(sensorVal) + "\n";
  message += "Level: " + String(moisturePercentage) + " % \n";
  return message;
}

//Handle what happens when you receive new messages
void handleNewMessages(int numNewMessages) {
  Serial.println("handleNewMessages");
  Serial.println(String(numNewMessages));

  for (int i=0; i<numNewMessages; i++) {
    // Chat id of the requester
    String chat_id = String(bot.messages[i].chat_id);
    if (chat_id != CHAT_ID){
      bot.sendMessage(chat_id, "Unauthorized user", "");
      continue;
    }
    
    // Print the received message
    String text = bot.messages[i].text;
    Serial.println(text);

    String from_name = bot.messages[i].from_name;

    if (text == "/start") {
      String welcome = "Welcome, " + from_name + ".\n";
      welcome += "Use the following command to get current readings.\n\n";
      welcome += "/state \n";
      bot.sendMessage(chat_id, welcome, "");
    }

    if (text == "/state") {
      String readings = getReadings();
      bot.sendMessage(chat_id, readings, "");
    }  
  }
}



//Main Functions
void setup() {
  //pinMode(LightPin,OUTPUT);
  pinMode(sensorPin,INPUT);
  pinMode(DefaultPin,OUTPUT);

  Serial.begin(9600);
  blinking_loop();
  setup_wifi();
  client.setServer(mqttServer, port);

  bot.sendMessage(CHAT_ID, plant + " restarted!", ""); //Send message if controller restarted
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  int sensorVal = analogRead(sensorPin);
  sensorVal = 4095 - sensorVal;

  int moisturePercentage = map(sensorVal, dry, wet, 0, 100);

  if (moisturePercentage > 100) {
    moisturePercentage = 100;
  } else if (moisturePercentage < 0) {
    moisturePercentage = 0;
  }

  if (millis() > lastTinggPost + tinggPostDelay) {
    Serial.print("Value: ");
    Serial.println(sensorVal);
    Serial.print("Level: ");
    Serial.print(moisturePercentage);
    Serial.println("%");
  
    client.publish(soilMoistureTopic,itoa(sensorVal, buf, 10));
    client.publish(levelTopic,itoa(moisturePercentage, buf, 10));

    //Send Telegram Message if Moisture Level
    switch (iStep) {
      case 0:
        iCounter = iTemp;
        iToggle = 1;
        iStep = 1;
      break;

      case 1:
        if (iCounter <= 0) {
          iStep = 2;
          iToggle = 1;
        } else if (moisturePercentage < trigger) {
          iCounter--;
          Serial.print("Counter: ");
          Serial.println(iCounter);
        } else if (moisturePercentage >= trigger) {
          iStep = 0;
        }
      break;

      case 2:
        if (moisturePercentage < trigger && iToggle == 1) {
          String alert = plant + " gießen!";
          bot.sendMessage(CHAT_ID, alert, "");
          Serial.println("Message sent!");
          iToggle = 0;
        } else if (moisturePercentage > trigger) {
          iCounter = iTemp;
          iStep = 3;
        }
      break;

      case 3:
        if (iCounter <= 0 && moisturePercentage > trigger) {
          iStep = 0;
        } else if (moisturePercentage > trigger) {
          iCounter--;
          Serial.print("Counter: ");
          Serial.println(iCounter);
        } else if (moisturePercentage <= trigger) {
          iStep = 2;
        }
      break;
    }

    lastTinggPost = millis();
  }


  //Telegram Message Request
  if (millis() > lastTimeBotRan + botRequestDelay)  {
    int numNewMessages = bot.getUpdates(bot.last_message_received + 1);

    while(numNewMessages) {
      Serial.println("got response");
      handleNewMessages(numNewMessages);
      numNewMessages = bot.getUpdates(bot.last_message_received + 1);
    }
    lastTimeBotRan = millis();
  }
}

Auch auf GitHub mit Highlights unter folgendem Link verfügbar: Link

Resultat

Erweiterte Funktionalität

Das System könnte u.a. durch eine Pumpe erweitert werden, die die Pflanze gießt, sobald die Bodenfeuchte ein gewisses Level erreicht hat. Funktioniert die Pumpe jedoch nicht ordnungsgemäß und man ist nicht in der Nähe, um das Problem zu beheben, bevor die Pumpe die Wohnung unter Wasser setzt, sollten andere Sicherheitsvorkehrungen getroffen werden, die eine Fehlfunktion verhindern.

Außerdem könnte das System noch mit einer zeitgesteuerten Beleuchtung, einer Kamera oder einem Temperatursensor erweitert werden, um der Pflanze eine bestmögliche Umgebung zu bieten.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.