4.2 Umsetzung des Programms

Zunächst wurde die fertiggestellte Hardware mit einem einfachen Blink-Programm bespielt, um zu sehen, ob Programme einwandfrei auf den ESP8266 übertragbar sind.

Als nächstes sollten die analogen Werte des Sensors auf einem seriellen Monitor angezeigt werden. Dazu musste das Blink-Programm, welches am Anfang genutzt wurde, modifiziert werden. Der passende PIN A0 des ESP8266 musste am Sensor angeschlossen und im Programm als analoger Input definiert werden.

Nachdem der Sensor analoge Werte übermittelte, war es für die Funktionsweise der Bewässerungsanlage zu berücksichtigen, dass bestimmte Schwellenwerte dem Programm vorgegeben werden müssen, sodass das Programm erkennen kann, ob die Pflanze Wasser benötigt oder nicht.
Es wurden vier verschiedene Modi definiert, die je nach Schwellenwert aktiviert werden.

  if(sensorValue>0 && sensorValue<340){
      ledOff();
      msg = "Sensor is full WET";
      msg_id = 1;
  } else if(sensorValue>=340 && sensorValue<=530) {
      ledOff();
      msg = "Sensor is wet";
      msg_id = 2;
  } else if(sensorValue>530 && sensorValue<1023) {
      ledOn();
      msg = "Sensor is DRY ";
      msg_id = 3;
      //pump();
  } else {
      ledOff();
      msg = "Unexpected sensor value ";
      msg_id = 4;
  }

Anschließend wurde die Pumpe angeschlossen und in einem separaten Programm angesteuert. Nachdem die Funktionsweise der Pumpe und die Schwellenwerte des Sensors bekannt war, sollte nun das Programm von zuvor erweitert werden. Jedes Mal, wenn der Sensor meldet, dass die Pflanze trocken ist, sollte die Pumpe für 2 Sekunden angehen.

void pump(){
  // Turn pump on for 2000s
  int pumpPeriode = 2000;
  if(minDelayOfPump < millis() - timeStamp1){
    startingProcedure();
    pumpOn();
    delay(pumpPeriode);
    pumpOff();
    timeStamp1 = millis();
  }
}

Damit die Erde der Pflanze das Wasser aufnehmen kann, wird gemessen wann die Pumpe das letzte Mal aktiviert wurde. Erst wenn eine bestimmte Pause vergangen ist, darf die Pumpe wieder aktiviert werden. Somit kann das Programm nach einer bestimmten Zeit entscheiden, ob die Wassermenge ausreicht oder nicht. Falls die Wassermenge nicht ausreicht, wird die Pumpe wieder aktiviert. Dieser Ablauf wiederholt sich solange, bis die Erde der Pflanze ausreichend Wasser aufgenommen hat.

Nun sollten die Werte des Sensors zusätzlich via MQTT übertragen werden können. Dazu sollte wieder ein separates Programm genutzt werden, um den Broker, den Port und das Topic testen zu können. Anschließend wurde die MQTT-Funktionalität mit in das vorherige Programm mit aufgenommen.

Durch kleinere Programme ließen sich die Funktionen separat testen, bevor zu einem größeren Programm zusammengefügt wurden. Dies hat mögliche Fehlerquellen seit dem Anfang der Umsetzung des Quellcodes ausschließen können.

Abb.: Flussdiagramm zur Darstellung der Funktionsweise des Programms auf dem ESP8266.

Das Programm wurde noch um eine weitere Funktion erweitert, sodass es auch möglich ist die Pumpe via MQTT zu aktivieren.

/**
 * 
 *    brief: read values from water-sensor and send via WiFi to an application
 *    date: 05.07.2019
 *    project: CvO/Informatik/DDI3_µC
*/
//#include <WiFi.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

/********************************************************/
// Update these with values suitable for your network.

const char* ssid = "HIER DER NETZWERK NAME";
const char* password = "HIER DAS PASSWORT DES NETZWERKS";
/********************************************************/

const char* mqtt_server = "broker";
const char* mqttTopicPlant = "topic1";
const char* mqttTopicValues = "topic2";
const int port = 1883;
/********************************************************/
char* msg = ""; // msg to the broker
int msg_id = 0;
int msg_id_temp = 0;
int MOTOR = 14;
unsigned long timeStamp1 = 0, timeStamp2 = 0,  minDelayOfPump = 20000;

WiFiClient espClient;
PubSubClient client(espClient);

/********************************************************/
/* SETUP  */ 
/********************************************************/
void setup() {
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  pinMode(MOTOR, OUTPUT);
  pinMode(A0, INPUT);
  Serial.begin(115200);             // initialize serial communication at 115200 bits per second
  startingProcedure();              // blinking BuildIn_LED as starting procedure
  
  setup_wifi();
  client.setServer(mqtt_server, port);
  client.setCallback(callback);
  
  timeStamp1 = millis();
  timeStamp2 = millis();
}

/********************************************************/
/* MAIN LOOP  */ 
/********************************************************/
// the loop routine runs over and over again forever:
void loop() {
  
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);

  sensorStates(sensorValue);

  delay(1000);
}
/********************************************************/

/********************************************************/
/* PROCESS  */ 
/********************************************************/

void sensorStates(int sensorValue){
  if(sensorValue>0 && sensorValue<340){
      ledOff();
      msg = "Sensor is full WET";
      msg_id = 1;
  } else if(sensorValue>=340 && sensorValue<=530) {
      ledOff();
      msg = "Sensor is wet";
      msg_id = 2;
  } else if(sensorValue>530 && sensorValue<1023) {
      ledOn();
      msg = "Sensor is DRY ";
      msg_id = 3;
      //pump();
  } else {
      ledOff();
      msg = "Unexpected sensor value ";
      msg_id = 4;
  }

  if(minDelayOfPump < millis() - timeStamp2){
    timeStamp2 = millis();
    statusCheckOfPlant(msg_id, sensorValue);
  }

  if(msg_id != msg_id_temp){
    msg_id_temp = msg_id;
    statusCheckOfPlant(msg_id, sensorValue);
  }
}

void statusCheckOfPlant(int msg_id, int sensorValue){
  sendDataToBroker(sensorValue);
  // DRY?
  if(msg_id == 3){
    //Turn pump on
    pump();
  }
}

void sendDataToBroker(int sensorValue){
    // Serial
    Serial.println(msg);
    Serial.println(sensorValue);
    // MQTT
    client.publish(mqttTopicPlant, msg);
    sendSensorValueViaMqtt(sensorValue);    
}

/********************************************************/
/* Pump  */ 
/********************************************************/
void pump(){
  // Turn pump on for 2000s
  int pumpPeriode = 2000;
  if(minDelayOfPump < millis() - timeStamp1){
    startingProcedure();
    pumpOn();
    delay(pumpPeriode);
    pumpOff();
    timeStamp1 = millis();
  }
}

void pumpOn(){
  ledOn();
  // pump on
  analogWrite(MOTOR, 512);
}

void pumpOff(){
  ledOff();
  // pump off
  analogWrite(MOTOR, 0);
     
}

/********************************************************/
/* LED  */ 
/********************************************************/
void ledOn(){
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED on (HIGH is the voltage level)
  delay(250);
}

void ledOff(){
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(250);
}

// blinking procedure when device is switched on
void startingProcedure(){
  ledOn();
  ledOff();
  ledOn();
  ledOff();
}

/********************************************************/
/* MQTT  */ 
/********************************************************/

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}


void callback(char* topic, byte* payload, unsigned int length) {
  char receivedPayload[length];
  /*Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");*/
  for (int i = 0; i < length; i++) {
    //Serial.print((char)payload[i]);
    receivedPayload[i] = (char) payload[i];
  }
  Serial.println();

  // Received Message
  int receivedMessage = atoi(receivedPayload);
  if(receivedMessage==1){
    Serial.print("MESSAGE: ");
    Serial.println(receivedMessage);
    pump();
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish("outTopic", "hello world");
      //client.publish(mqttTopicPlant, "hello world!");
      client.subscribe(mqttTopicPlant);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void sendSensorValueViaMqtt(int sensorValue){
    // Publish int (auch hier wieder char array)
    String pi_str = String(sensorValue);
    char pi_buff[pi_str.length()+1];
    pi_str.toCharArray(pi_buff, pi_str.length()+1);
    client.publish(mqttTopicValues, pi_buff);
}

4.1 Umsetzung der Hardware

Hauptsächlich steht bei der Umsetzung der Hardware die Fertigstellung der Platine mit den zugehörigen Bauteilen im Vordergrund. Die anderen Elemente, wie beispielsweise die Pumpe, werden lediglich miteinander verbunden.

Abb.: Bild der fertiggestellten Platine mit allen Bauteilen.

Der Hauptbestandteil ist der ESP8266, welcher die Prozesse für die automatische Bewässerung steuert. Auch die Kommunikation über WLan ist durch den ESP8266 gewährleistet.

Die Platine beinhaltet außerdem den Spannungsteiler, welcher 12V auf 3,3V regulieren kann. Dieser wird nach einer gewissen Zeit sehr warm und sollte daher nach Möglichkeit an eine zusätzliche Metallfläche angeschlossen werden.

Die Platine kann nach Fertigstellung in eine Kunststoffbox installiert werden, um diese vor Feuchtigkeit zu schützen. Auch die Anschlüsse vom Netzteil und der Pumpe können an der Platine angeschlossen werden.

Die Platine kann nach Fertigstellung

4.0 Umsetzung

Bei der Umsetzung wurde nach der Methode “Bottom Up” alles kleinschrittig und möglichst gekapselt umgesetzt. Zu Beginn wurde die Hardware und anschließend die Software fertiggestellt. Als Zusatz wurde noch eine eigene App für die automatische Bewässerungsanlage implementiert, welche keinen Einfluss auf den automatisierten Prozess der Bewässerungsanlage hat, sondern lediglich zur Kontrolle und zur zusätzlichen Steuerung dient.
Durch die kleinschrittige und transparente Umsetzung, konnten die einzelnen Hardware- und Software-Elemente getestet und ggf. angepasst und verbessert werden.