Oder: Meine Wärmepumpe will ins Internet

Zur Überwachung unserer “fast wartungsfreien” Wärmepumpe ist eine regelmäßige Kontrolle des Abflusses der Abtauwanne erforderlich, Vorgabe des Herstellers lautet jährlich. Wenn der Abfluss aber in der Zwischenzeit verstopft, kann jedoch Wasser austreten und im ungünstigen Fall einen Wasserschaden erzeugen.

Um eine Verstopfung und somit stehendes Wasser rechtzeitig zu erkennen, habe ich einen Feuchtesensor aufgebaut und diesen über die Arduino-IDE programmiert. Der Zustand wird einmal täglich überprüft und via MQTT publiziert. Zusätzlich erfolgt ein Versand einer E-Mail, wenn Wasser erkannt wurde. Prinzipiell eignet sich der Sensor und die Software ebenso für andere Anwendungen wie z. B. Spülmaschine, Waschmaschine o.ä.

Komponenten

Im Folgenden erläutere ich die notwendige Hard- und Software. Folgende Bauteile habe ich verwendet (siehe Foto):
– Wemos D1 mini pro
– Bodenfeuchte-Sensor-Modul für Arduino
– 3m Micro-USB-Kabel

Kern des Wemos D1 mini pro ist ein 32-bit-Microcontroller (ESP8266) mit integriertem WLAN. Hilfreiche Details und Tipps dazu liefert Wolle. Der Feuchtesensor (Widerstand + Verstärkereinheit) kann analog oder digital abgefragt werden, für meine Anwendung genügt der digitale Zustand bzw. Ausgang.

Software bzw. Funktionen

Die Abfrage des Sensors und somit des Status erfolgt einmal täglich delay(86400000). Da ich keine tägliche E-Mail empfangen möchte, wird der Status per MQTT publiziert, um auch regelmaßig zu kontrollieren, dass das System noch arbeitet. Lediglich bei detektierter Feuchtigkeit, d. h. dem Status “Wasser“, schickt der ESP8266 ein HTTP-Request an meinen Webserver, der dann den Versand einer E-Mail via PHP-Skript übernimmt. Alternativ (und auch einfacher) ist ein E-Mail-Versand via SMTP mit der Bibliothek ESP_Mail_Client.

WiFiClient und WiFiClientSecure

Da mein Webserver über eine sichere Verbindung kommuniziert, war die Lösung etwas kniffelig. Erforderlich ist deshalb eine sichere WiFi-Verbindung sowie eine Verifizierung des Fingerabdrucks meines Zertifikates mit der Bibliothek WiFiClientSecure.h . Da MQTT mit dem normalen WiFiClient initialisiert wird, ist zusätzlich auch noch die Bibliothek WiFiClient.h nötig. Wer nicht über eine gesicherte Verbindung kommuniziert oder eine E-Mail per SMTP verschickt, kommt mit der Bibliothek WiFiClient.h aus.

MQTT

Die Verbindung und Kommunikation zu einem öffentlichen MQTT-Broker ist relativ einfach und kann dort auch direkt überprüft werden. Hier ist nur zu überlegen, ob dies das einzige Topic bleibt oder noch weitere Themen hinzukommen, also wie die Struktur aussieht: haus/waermepumpe/status.

E-Mail via HTTP-Post und PHP

Als Basis habe ich die Anleitung von Rui Santos verwendet, allerdings modifiziert für den Versand über eine sichere Verbindung. Dabei verwende ich direkt den WiFiSecureClient und nicht den HTTPClient, da eine Kombination wie in dieser Anleitung beschrieben bei einer verschlüsselten Verbindung nicht funktionierte. Zuerst wird also der Fingerabdruck überprüft if(!secureclient.setFingerprint(fingerpr)). Erst danach kann der HTTP-Post abgeschickt werden. Die zu übertragenden Daten sind ein Key, damit nicht unbefugte das PHP-Skript für Spam nutzen können, sowie der Status des Sensors: String httpRequestData = "api_key=" + apiKeyValue + "&value1=" + SensorData;

Sensor in der Wärmepumpe

Der Sensor selber wird in der Wärmepumpe derart platziert, dass kein direkter Kontakt mit dem Metallboden der Abtauwanne besteht und ein Signal nur bei stehendem Wasser erfolgt (siehe Foto). Die Stromversorgung erfolgt über den Micro-USB-Anschluss des Wemos Boards, der mit einem 3m langen Kabel und einem alten Handy-Netzteil verbunden ist.

Den Sketch zum Upload auf den Microcontroller findet ihr als Vorlage im folgenden Abschnitt…

/* verbindung mit webserver ueber ssl und email via php
 * status via mqtt */
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <WiFiClient.h>
#include <PubSubClient.h>

#define WIFI_SSID "dein wifi"
#define WIFI_PASSWORD "wifi passwort"

//host adress
const char* host = "dein webserver mit http oder https";
const char* host_short = "dein webserver";
int port = 443; //ggf. anpassen
const char* path = "/pfad zum php skript";
const char* fingerpr ="mein fingerprint";
String apiKeyValue = "x1x2x3x4x5"; //identisch im PHP-Skript 
const char* SensorData = "";

//WifiClientSecure fuer Verbindung ueber SSL fuer Email
WiFiClientSecure secureclient; 
//WifiClient fuer Verbindung mit MQTT
WiFiClient client; 

//MQTT Client
PubSubClient mqttclient(client);
const char* mqtt_server ="dein mqtt broker";
int mqtt_port = 1883;

//Definitionen fuer den Sensor
int sensor = 12; // D6
// Hell bzw. Kontakt bzw. Wasser = 0 - Dunkel = 1
bool state;
    
void setup()
{
  Serial.begin(115200);
  //sensor definieren
  pinMode(sensor, INPUT);

  //wifi verbinden
  setup_wifi();

  //mqtt definieren
  mqttclient.setServer(mqtt_server, mqtt_port);
  mqttclient.setCallback(callback);
  Serial.print("MQTT Server ");
  Serial.print(mqtt_server);
  Serial.print(":");
  Serial.println(String(mqtt_port)); 
 
}//void setup

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

void setup_wifi(){
  //connect to wifi 
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());
} //void setup_wifi

void reconnect() {
  // Loop until we're reconnected
  while (!mqttclient.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (mqttclient.connect("ESP8266Client")) {
   
      Serial.println("connected");
      // mqttclient.subscribe("event");
    } 
    else 
    {
      Serial.print("failed, rc=");
      Serial.print(mqttclient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  } //while
} //void reconnect

void connect_and_send() {
  //Check WiFi connection status
  if(WiFi.status()== WL_CONNECTED)
  {
    //connection to server
    if(!secureclient.setFingerprint(fingerpr))
    {
      Serial.println("Verification failed");
    }
    else
    {
      //fingerprint verification successful 
      //Serial.print("connecting to "); 
      // Use WiFiClient class to create TCP connections 
      if (!secureclient.connect(host, port)) 
      { 
        Serial.println("connection failed"); 
        return; 
      } 
      else
      {
        //connection to host successful
        Serial.print("Connected to server: ");
        Serial.print(host); 
        Serial.print(" on port: ");
        Serial.println(port);
        
        // Make a HTTP request:
        String httpRequestData = "api_key=" + apiKeyValue + "&value1=" + SensorData;
        String request = String("POST ") + path + " HTTP/1.1 \r\n" +
                 "Host: " + host_short + "\r\n" +
                 "Content-Type: application/x-www-form-urlencoded\r\n" +
                 "Content-Length: " + httpRequestData.length() + "\r\n" +
                 "\r\n" + httpRequestData;
                 
        Serial.println(request);
        secureclient.print(request);
        delay(100);

        Serial.println("Respond from server:");

        while (secureclient.connected()) {
          while (secureclient.available()) {
            Serial.write(secureclient.read());
          }
        }
        secureclient.stop();
        
      } //else connection to host
    } //else fingerprint
  } //else wifi connected  
}
void loop() {

  state = digitalRead(sensor);
   
  if(state == 1){
     SensorData = "Trocken"; // keine Warnmeldung losschicken
  }
  if(state == 0){
     SensorData = "Wasser";
     // function call for http post ...
     connect_and_send();
  }
  Serial.println(SensorData);

  if (!mqttclient.connected()) {
    reconnect();
  }
  mqttclient.publish("haus/waermepumpe/status", SensorData); /// publish status 
   
  // 20 sekunden = 20000
  // 1 h = 3600 sekunden = 3600000
  // 1 d = 86400 sekunden = 86400000
  delay(86400000);
}

Feuchtesensor
Markiert in: