Heizung ESP8266

Die Kommunikation zwischen Mega und ESP geschieht über Seriellports. Die Kommunikation mit dem Heimserver/Internet geschieht über eine Rest-Api (PHP GET requests) und wird in eine MySQL Datenbank eingetragen. Daten aus der Sql-Tabelle "toESP" werden genutzt um die Heizung zu Steuern. Die Tabelle "fromESP" nutzen wir als Langzeitspeicher für alles was vom Mega kommt. Da diese nur alle paar Minuten geupdatet wird um den Server nicht zu bombardieren, nutzen wir noch "fromESPrt" zum späteren anzeigen der Daten auf der Webseite. Die PHP´s für die API und die Erstellung der Datenbank werden in einem anderen Kapitel behandelt.

secrets.h

Als Erstes erstellen wir eine zusätzliche Datei mit der Endung ".h" im gleichen Verzeichnis wie die ".ino" Datei, welche wir gleich schreiben. Diese Datei enthält alle sensiblen Daten, die zur Verbindungsherstellung notwendig sind.


#define SECRET_SSID "MySSID"    
#define SECRET_PASS "MyPassword"  
#define API_KEY "MyApiKey"
#define SERVER_URL "MyIpAdress"
            

Wifi-Verbindung

Standard Script zur Verbindung eines ESP8266 mit dem heimischen Wlan. Wir fügen die ESP8266Wifi Bibliothek und unsere secrets.h ein und kontrollieren die Verbindung anhand der integrierten LED auf dem Board.


#include ESP8266WiFi.h
#include "secrets.h"

char ssid[] = SECRET_SSID;   
char pass[] = SECRET_PASS;   
char host[] = SERVER_URL;     // URL PHP Api
char ApiK[] = API_KEY;

void setup() {
    WiFi.mode(WIFI_STA);
    Serial.begin(9600);
    pinMode(2, OUTPUT);
    digitalWrite(2, HIGH);  //LED aus
}

void loop() {
    if (WiFi.status() != WL_CONNECTED) {
      digitalWrite(2, HIGH);    // LED aus = Verbindung mit WIFI unterbrochen
      Serial.print("Attempting to connect to SSID: ");
      Serial.println(SECRET_SSID);
      while (WiFi.status() != WL_CONNECTED) {
        WiFi.begin(ssid, pass);  
        Serial.print(".");
        delay(5000);
      }
      Serial.println("\nConnected");
      digitalWrite(2, LOW);    //LED an = Verbunden mit WIFI
    }
}
            

Lesen der Daten vom Mega

Da der ESP nur einen Seriellport hat nutzen wir Softseriell und machen zwei frei IO-Ports (14 und 12) zu einer serielle Schnittstelle. Die Daten der Steuerung kommen als Komma getrennter String in den ESP. Dafür werden erstmal Platzhaltervariablen erstellt. Den String teilen wir jetzt an jedem Komma, wandel die Ergebnisse in Integer und füllen damit die Platzhalter. Danach haben wir die Temperaturen, Zustände und Einstellungen der Heizungssteuerung auf dem ESP verfügbar.


SoftwareSerial swSer(14, 12);
String Data, rest;
int tk, ts, br, sp, hp, zp, ks, sm, hk, ta;

void setup() {
    WiFi.mode(WIFI_STA);
    Serial.begin(9600);
    swSer.begin(9600);
}

// read Data from Mega
  if (swSer.available()) {
    Data = swSer.readString();
    ta = Data.substring(0, Data.indexOf(',')).toInt();
    rest = Data.substring(Data.indexOf(',') + 1);
    tk = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    ts = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    br = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    sp = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    hp = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    zp = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    ks = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    sm = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    hk = rest.substring(0, rest.indexOf(',')).toInt();
  }
            

Server lesen und schreiben zu unterschiedlichen Zeiten

Damit wir das Lesen und Schreiben unterschiedlich timen können, nutzen wir "millis()" und erstellen uns die "Timing" Variablen als long Integer. Nach Ablauf der werden die entsprechenden Funktionen aufgerufen.


long writeTimingSeconds = 180;
long readTimingSeconds = 10;
long startReadTiming = 0;
long elapsedReadTime = 0;
long startWriteTiming = 0;
long elapsedWriteTime = 0;  

// insert Data to sql "fromESP"
  elapsedWriteTime = millis() - startWriteTiming;
  if (elapsedWriteTime > (writeTimingSeconds * 1000)) {
    WriteDB();
    startWriteTiming = millis();
    delay(500);
  }
  // read_all data from sql "toESP"
  // update data to sql "fromESPrt"
  elapsedReadTime = millis() - startReadTiming;
  if (elapsedReadTime > (readTimingSeconds * 1000)) {
    ReadDB();
    RealtimeDB();
    startReadTiming = millis();
    delay(500);
  }
}

Schreiben auf sql "fromESP"

Um GET request´s mit dem ESP auszuführen brauchen wir die ESP8266HTTPClient Bibliothek. Dann muss nur der URL-String zusammengebaut werden und definiert werden, wohin (ip) und als was (GET) der request gesendet wird.


#include ESP8266HTTPClient.h
WiFiClient  client;

void WriteDB() {
    HTTPClient http;
    String url = host;
    url += "/fromESP/insert.php?";
    url += "API_KEY=";
    url += ApiK;
    url += "&ta=";
    url += int(ta);
    url += "&tk=";
    url += int(tk);
    url += "&ts=";
    url += int(ts);
    url += "&br=";
    url += int(br);
    url += "&sp=";
    url += int(sp);
    url += "&hp=";
    url += int(hp);
    url += "&zp=";
    url += int(zp);
    url += "&ks=";
    url += int(ks);
    url += "&sm=";
    url += int(sm);
    url += "&hk=";
    url += int(hk);
    http.begin(client, url);
    int httpCode = http.GET();
    Serial.print("fromESP=");
    Serial.println(url);
    String result = http.getString();
    Serial.print("database=");
    Serial.println(result);
    Serial.println(">>>");
    http.end();
  }

  void RealtimeDB() {
    HTTPClient http;
    String url = host;
    url += "/fromESPrt/update.php?";
    url += "API_KEY=";
    url += ApiK;
    url += "&ta=";
    url += int(ta);
    url += "&tk=";
    url += int(tk);
    url += "&ts=";
    url += int(ts);
    url += "&br=";
    url += int(br);
    url += "&sp=";
    url += int(sp);
    url += "&hp=";
    url += int(hp);
    url += "&zp=";
    url += int(zp);
    url += "&ks=";
    url += int(ks);
    url += "&sm=";
    url += int(sm);
    url += "&hk=";
    url += int(hk);
    http.begin(client, url);
    int httpCode = http.GET();
  
    Serial.print("fromESPrt=");
    Serial.println(url);
    String result = http.getString();
    Serial.print("database=");
    Serial.println(result);
    Serial.println(">>>");
    http.end();
  }

Lesen von sql "toESP"

Der Aufbau ist analog zum Schreiben in die Datenbank. Nur, dass wir jetzt Daten im JSON-Format bekommen. Deshalb brauchen wir nun noch die Bibliothek "ArduinoJson". Wenn der Responsecode eine "200" ist, heißt das die Anfrage war erfolgreich und wir bekommen eine Json-String las Daten. Diesen nehmen wir auseinander und verpacken alles wieder in einen kommagetrennten String, welcher über Seriell an den Mega gesendet wird.


#include ArduinoJson.h

void ReadDB() {
    HTTPClient http;
    String url = host;
    url += "/toESP/read_all.php?";
    url += "API_KEY=";
    url += ApiK;
    http.begin(client, url);
    int httpCode = http.GET();
    Serial.print("toESP=");
    Serial.println(url);
    if (httpCode == 200) {
      String result = http.getString();
      Serial.print("database=");
      Serial.println(result);
      Serial.println(">>>");
  
      StaticJsonDocument<700> doc;
      DeserializationError err = deserializeJson(doc, result);
      if (err) {
        Serial.print(F("deserialize failed: "));
        Serial.println(err.c_str());
      }
      String wwT = doc["toESP"][0]["Status"];
      String smT = doc["toESP"][1]["Status"];
      String sfT = doc["toESP"][2]["Status"];
      String hkT = doc["toESP"][3]["Status"];
      String tks = doc["toESP"][4]["Status"];
      String bta = doc["toESP"][5]["Status"];
      RESET = doc["toESP"][6]["Status"];
  
      String data2send = wwT;
      data2send += ",";
      data2send += smT;
      data2send += ",";
      data2send += sfT;
      data2send += ",";
      data2send += hkT;
      data2send += ",";
      data2send += tks;
      data2send += ",";
      data2send += bta;
      data2send += "}";
      swSer.println(data2send);
    }
    http.end();
  }

Komplett

Da es Serverseitig (gerade wenn er im Internet und nicht nur im heimischen Netzwerk ist) immer zu Problem wie Verbindungsabbrüche kommen kann, ist es notwendig Rückfallwerte auf der eigentlichen Steuerung zu haben. Aus diesem grund wurde auch eine Trennung von Steuerung und Kommunikation gewählt.


#include ESP8266WiFi.h
#include "secrets.h"
#include SoftwareSerial.h
#include ArduinoJson.h
#include ESP8266HTTPClient.h

SoftwareSerial swSer(14, 12);//, false, 128);
char ssid[] = SECRET_SSID;   // your network SSID (name)
char pass[] = SECRET_PASS;   // your network password
char host[] = SERVER_URL;     // URL PHP Api
char ApiK[] = API_KEY;
WiFiClient  client;

String Data, data2send, rest;
int RESET = 0;
int tkLast;
int tk, ts, br, sp, hp, zp, ks, sm, hk, ta;
long writeTimingSeconds = 180;
long readTimingSeconds = 10;
long startReadTiming = 0;
long elapsedReadTime = 0;
long startWriteTiming = 0;
long elapsedWriteTime = 0;

void setup() {
  //WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  Serial.begin(9600);
  swSer.begin(9600);
  startReadTiming = millis();
  startWriteTiming = millis();
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
}
void loop() {
  // Connect or reconnect to WiFi
  //WiFi.softAPdisconnect (true);
  if (WiFi.status() != WL_CONNECTED) {
    digitalWrite(2, HIGH);
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(SECRET_SSID);
    while (WiFi.status() != WL_CONNECTED) {
      WiFi.begin(ssid, pass);  // Connect to WPA/WPA2 network. Change this line if using open or WEP network
      Serial.print(".");
      delay(5000);
    }
    Serial.println("\nConnected");
    digitalWrite(2, LOW);
  }
  // read Data from Mega
  if (swSer.available()) {
    Data = swSer.readString();
    ta = Data.substring(0, Data.indexOf(',')).toInt();
    rest = Data.substring(Data.indexOf(',') + 1);
    tk = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    ts = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    br = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    sp = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    hp = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    zp = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    ks = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    sm = rest.substring(0, rest.indexOf(',')).toInt();
    rest = rest.substring(rest.indexOf(',') + 1);
    hk = rest.substring(0, rest.indexOf(',')).toInt();
  }
  // insert Data to sql "fromESP"
  elapsedWriteTime = millis() - startWriteTiming;
  if (elapsedWriteTime > (writeTimingSeconds * 1000)) {
    WriteDB();
    startWriteTiming = millis();
    delay(500);
  }
  // read_all data from sql "toESP"
  // update data to sql "fromESPrt"
  elapsedReadTime = millis() - startReadTiming;
  if (elapsedReadTime > (readTimingSeconds * 1000)) {
    ReadDB();
    RealtimeDB();
    startReadTiming = millis();
    delay(500);
  }
}

void RealtimeDB() {
  HTTPClient http;
  String url = host;
  url += "/fromESPrt/update.php?";
  url += "API_KEY=";
  url += ApiK;
  url += "&ta=";
  url += int(ta);
  url += "&tk=";
  url += int(tk);
  url += "&ts=";
  url += int(ts);
  url += "&br=";
  url += int(br);
  url += "&sp=";
  url += int(sp);
  url += "&hp=";
  url += int(hp);
  url += "&zp=";
  url += int(zp);
  url += "&ks=";
  url += int(ks);
  url += "&sm=";
  url += int(sm);
  url += "&hk=";
  url += int(hk);
  http.begin(client, url);
  int httpCode = http.GET();

  Serial.print("fromESPrt=");
  Serial.println(url);
  String result = http.getString();
  Serial.print("database=");
  Serial.println(result);
  Serial.println(">>>");
  http.end();
}

void ReadDB() {
  HTTPClient http;
  String url = host;
  url += "/toESP/read_all.php?";
  url += "API_KEY=";
  url += ApiK;
  http.begin(client, url);
  int httpCode = http.GET();
  Serial.print("toESP=");
  Serial.println(url);
  if (httpCode == 200) {
    String result = http.getString();
    Serial.print("database=");
    Serial.println(result);
    Serial.println(">>>");

    StaticJsonDocument<700> doc;
    DeserializationError err = deserializeJson(doc, result);
    if (err) {
      Serial.print(F("deserialize failed: "));
      Serial.println(err.c_str());
    }
    String wwT = doc["toESP"][0]["Status"];
    String smT = doc["toESP"][1]["Status"];
    String sfT = doc["toESP"][2]["Status"];
    String hkT = doc["toESP"][3]["Status"];
    String tks = doc["toESP"][4]["Status"];
    String bta = doc["toESP"][5]["Status"];
    RESET = doc["toESP"][6]["Status"];

    String data2send = wwT;
    data2send += ",";
    data2send += smT;
    data2send += ",";
    data2send += sfT;
    data2send += ",";
    data2send += hkT;
    data2send += ",";
    data2send += tks;
    data2send += ",";
    data2send += bta;
    data2send += "}";
    swSer.println(data2send);
  }
  http.end();
}

void WriteDB() {
  HTTPClient http;
  String url = host;
  url += "/fromESP/insert.php?";
  url += "API_KEY=";
  url += ApiK;
  url += "&ta=";
  url += int(ta);
  url += "&tk=";
  url += int(tk);
  url += "&ts=";
  url += int(ts);
  url += "&br=";
  url += int(br);
  url += "&sp=";
  url += int(sp);
  url += "&hp=";
  url += int(hp);
  url += "&zp=";
  url += int(zp);
  url += "&ks=";
  url += int(ks);
  url += "&sm=";
  url += int(sm);
  url += "&hk=";
  url += int(hk);
  http.begin(client, url);
  int httpCode = http.GET();
  Serial.print("fromESP=");
  Serial.println(url);
  String result = http.getString();
  Serial.print("database=");
  Serial.println(result);
  Serial.println(">>>");
  http.end();
}