Temperaturmessung

Temperaturmessung

Grundlagen
Basis-Code
Outdoor-Einsatz
Einsatz mit einer festen Spannungsquelle („Indoor-Einsatz“)

Grundlagen

Eine Temperatur mit einem passenden Sensor (meist DS18B20) zu messen ist grundsätzlich eine simple Angelegenheit. Möchte man das ganze preiswert halten und gleichzeitig die Messwerte weitergeben und verarbeiten, so landet man bei der Prozessorwahl beim ESP – für Messungen in der Nähe einer Steckdose reicht hier schon der Kleinste, der ESP-01.

Der Anschluss des Sensors ist schnell erledigt. Der Daten-Ausgang wird über einen 4,7K-Widerstand auf 3,3V gelegt und gleichzeitig mit dem IO02-Eingang des ESP verbunden:

Die ca. 3V (eigentlich 3.3V) Betriebsspannung können zwei 1,5V-Batterien liefern, aber die sind dann auch relativ schnell leer. Besser ist eine Versorgung über ein Netzteil.

Die Messwerte sind ebenfalls leicht auszulesen. Mithilfe der Arduino-IDE (erweitert für den ESP8266) ist ein einfaches Programm schnell geschrieben. Diesen und ähnlichen Code findet man im Netz zuhauf!

Basis-Layout für DS18B20 und ESP-01

Im einfachsten Fall könnte der Code so aussehen:

Basis-Code

#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2  // DS18B20 pin

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);

void setup()
{
  Serial.begin(115200);
  DS18B20.begin();
}

void loop()
{
  float temp = 85.0;
  do {
    DS18B20.requestTemperatures();
    temp = DS18B20.getTempCByIndex(0);
  } while (temp == 85.0 || temp == (-127.0));
// Wenn der DS18B20 Unsinn misst,
// wirft er häufig einen dieser Werte aus
  Serial.println("got DATA");
  Serial.print("Temperatur: ");
  Serial.println(temp);
  delay(1000);
}

Läd man dieses Programm über die Arduino-IDE, so kann man in der seriellen Ausgabe im Minutentakt die gemessene Temperatur ablesen:

Ausgabe des seriellen Monitors

Toll 😐 Wer lässt seinen Sensor schon ständig am PC und seriellen Monitor hängen.
Aber der ESP ist ja WLAN-fähig. Dieses Feature eröffnet eine Reihe vom Möglichkeiten:

  • Verschicken der Messwerte an einen MQTT-Broker
  • Aufbau einer Webpage und Anzeige der Messwerte
  • Verschicken der Messwerte per Mail

Außerdem bieten sich noch ein paar Verbeserungen an:
Der DS18B20 liefert zwar Temperaturwerte mit zwei Dezimalen, seine Genauigkeit beträgt aber +-0,5°C! Man sollte also die Ausgabe auf maximal 1 Dezimale runden.
Der Grundaufbau des Programms für ESP + DS18B20 unterscheidet sich nur wenig vom Aufbau mit anderen Sensoren (wie DHT22 für Temperatur und Feuchte, BMP/BME 280 für Temp., Feuchte und Druck, u.s.w.)
Es ist daher keine schlechte Idee, den Messvorgang in eine eigene Methode getData() auszulagern, dann kann man die anderen Programmteile weitgehend ohne Änderungen übernehmen.

Outdoor-Einsatz

Für den Outdoor-Einsatz sind noch ein paar Gedanken zur Energieversorgung notwendig. Naheliegend ist die Idee, dem ESP einfach eine Versorgung per Batterie zu spendieren – z.B. sollten zwei AA-Zellen die 3V liefern können. Das Problem dabei ist, dass er es problemlos schafft die beiden innerhalb von wenig mehr als einem Tag leerzusaugen – ein teurer Spass.

Nun gibt es ja den Deep Sleep Modus, in dem er kaum Energie benötigt. In einem Probebetrieb eines D1 mini Pro WeMos Modells mit einem BMP 180 (Temperatur und Luftdruck) mit Deep Sleep Modus und 15 min Intervallen hielt das Ganze immerhin ca 1 Monat – war mir allerdings auch noch zu teuer. Letzendlich hatte ich die Idee, mir eine billige Solarlampe zu kaufen (bei Ebay im zweier Pack für € 11,90). Drinnen liegt als Pufferbatterie ein Li-Ion-Akku (ca. 4V). Ich lötete an den Akku zwei zusätzliche Kabel an, nahm einen ESP-01, auf den ich eine Drahtbrücke zwischen GPIO16 und Reset gelötet habe (für den Deep-Sleep) und schaltete einen StepUp-StepDown-Regler dazwischen (Pololu 3,3V – bei Ebay ca. 8€), um für stabile 3,3V zu sorgen. Den DS18B20 kaufte ich in einer gekapselten Form für den Außeneinsatz und führte ihn aus der Lampe hinaus. Der Innenteil passte auf ein Platinenstück von 3,5cm x 2,0cm und damit perfekt in die Lampe, sogar mit Sockeln für den ESP und den Pololu – und das Ganze funktioniert prächtig.
Den Pololu-Regler habe ich wieder ausgebaut! Er hat auch in der Deep-Sleep-Phase des ESP ca. 20mA verbraten. So hat dann nicht der ESP, sondern der Pololu den Akku leergesaugt. Der Akku liefert ca. 3,6V und das liegt an der oberen Grenze dessen, was der ESP verträgt. Ich denke (hoffe), dass er das auch langfristig aushält 😉 .
Zwar hat der Pololu auch einen Pin, der ihn in eine Art Tiefschlaf versetzt, aber das führt zu einem Henne-Ei-Problem: Wenn ich den Pololu über den ESP in den Tiefschlaf versetze, bekommt der ESP wahrscheinlich nicht genug Energie, um aus dem Tiefschlaf hochzufahren, (und erst dann könnte er ja den Pololu wieder aufwecken).

Wegen des Deep-Sleep-Modus ist ein Einsatz als Web-Server nicht sinnvoll – die meiste Zeit wäre der ESP nicht erreichbar! So wacht er nur kurz auf, schickt den aktuellen Messwert (und seine IP-Adresse – weil ich die immer schnell mit der der anderen Sensoren durcheinander werfe 😉 ) an den MQTT-Broker und geht wieder schlafen.

Die fast leere Platine …
… und die fertig bestückte Platine
Der DS18B20 für den Außeneinsatz

Der Code dafür ist immer noch übersichtlich (Download). Dass die loop-Methode leer ist hat den Grund, dass das Aufwachen aus dem Deep Sleep einen Reset auslöst und der ESP also wieder komplett von vorne beginnt. die loop-Methode würde also ohnehin maximal einmal durchlaufen. Deshalb kann man diesen Teil gleich in die setup-Methode hineinschreiben und den loop leer lassen.

#include <OneWire.h>
#include <DallasTemperature.h>

// Code für eigenen Aufbau

#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#define ONE_WIRE_BUS 2  // DS18B20 pin

//**** Diese Daten müssen angepasst werden ***************

#define WIFI_AP "NW-Kennung"
#define WIFI_PASSWORD "NW-Passwort"

#define TOKEN "ESP8266_DS18B20_TOKEN"

char clientID[] = "DS18B20_LAMPE";
char mqttBroker[] = "192.168.xxx.yyy"; // Adresse des MQTT-Brokers
int subNetIP = xxx; //IP des Subnets
int myIP = zzz; //nur der letzte Block der IP ändert sich
String myHostname = "DS18B20_LAMPE";
String mySensor = "DS18B20";
char mqttSubjectTemperatur[] = "wetter/lampe/temperatur";
char mqttSubjectIP[] = "wetter/lampe/ip";

//**********************************************************

String temperature = "Ablesefehler";

WiFiClient wifiClient;

PubSubClient client(wifiClient);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);
int status = WL_IDLE_STATUS;

void setup()
{
  InitWiFi();
  client.setServer( mqttBroker, 1883 );
  if ( !client.connected() ) {
    reconnect();
  }
  getData();
  sendMQTTMessage(mqttSubjectTemperatur, temperature);
  delay(100);
  //Die IP wird mitgeschickt, damit man sie nicht so leicht vergisst ;-)
  // Kann man auch weglassen!
  sendMQTTMessage(mqttSubjectIP, "192.168."+String(subNetIP)+"."+String(myIP));
  client.loop();
  ESP.deepSleep(600000000); //sleep 10 min = 600.000.000 us
}

void loop(){}

String getFormattedFloat(float x, uint8_t precision) {
  char buffer[10];
  dtostrf(x, 7, precision, buffer);
  return (buffer);
}

void getData() {
  float temp;
  do {
    DS18B20.requestTemperatures();
    temp = DS18B20.getTempCByIndex(0);
  } while (temp == 85.0 || temp == (-127.0)); 
  //eine dieser Temperaturen erscheint bei einem Messfehler
  temperature = getFormattedFloat(temp, 1);
}

void sendMQTTMessage(char mqttSubject[], String message) {
  char payload[100];
  message.toCharArray( payload, 100 );
  //mit "true" wird das "Retain-Flag" gesetzt - d.h. der Broker merkt sich
  // die Payload bis sie mit der nächsten Lieferung überschrieben wird.
  //So bekommt ein neuer Abonnent immer sofort die letzte Payload ausgeliefert!
  client.publish( mqttSubject, payload , true);
}

void connectToWiFi() {
  WiFi.begin(WIFI_AP, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void InitWiFi()
{
  // Verbindungsversuch mit WiFi
  IPAddress ip(192, 168, subNetIP, myIP);
  IPAddress gateway(192, 168, subNetIP, 1);
  IPAddress subnet(255, 255, 255, 0);
  IPAddress dns(192, 168, subNetIP, 1);
  WiFi.config(ip, dns, gateway, subnet); // auf feste IP einstellen
  WiFi.hostname(myHostname); //Hostnamen setzen
  WiFi.persistent(false);
  WiFi.mode(WIFI_OFF);
  WiFi.mode(WIFI_STA);
  connectToWiFi();
}

void reconnect() {
  // Loop bis zum Reconnect
  while (!client.connected()) {
    status = WiFi.status();
    if ( status != WL_CONNECTED) {
      connectToWiFi();
    }
    if ( client.connect(clientID, TOKEN, NULL) ) {
    } else {
      // 5 Sekunden warten
      delay( 5000 );
    }
  }
}

Einsatz mit einer festen Spannungsquelle („Indoor-Einsatz“)

Beim „Indoor“-Einsatz habe ich mich letztendlich dazu entschieden, meine Daten sowohl über MQTT, als auch per Webpage anzubieten. Die MQTT-Daten können dann an einen Broker auf einem Raspberry Pi weitergereicht werden, der diese wiederum an NodeRed weitergibt. Letzteres ist für Messzwecke eine Offenbarung – man kann sich nach dem Lego-Prinzip Ein- und Ausgabemöglichkeiten zusammenschieben und die Messwerte wunderschön als Gauge, Chart, … darstellen, sie in eine Datei schreiben lassen, sie per Mail oder Messenger verschicken u.s.w.

Über die Webpage lasse ich neben den Temperaturwerten noch andere Informationen über den ESP ausgeben, außerdem kann man über die Webpage die Ableserate verändern. Manchmal möchte man vielleicht einmal ein Ereignis in kürzeren Schritten erfassen – z.B. den Temperaturverlauf in einem Raum, wenn das Fenster geöffnet wird, im Normalfall wird es einem meist reichen, in 10 / 15 oder 30min-Intervallen zu messen. Damit man eine Information über das Alter der Daten bekommt, lasse ich den ESP die Zeit von meinem häuslichen Timeserver abfragen und auf der Webpage zusammen mit den Messwerten anzeigen. Damit der Code nicht zu unübersichtlich wird, habe ich die Timeserver-Methoden in eine eigene Bibliothek ausgelagert.
Es gibt natürlich auch fertige Libraries zum Thema ntp-Server für den ESP8266, aber die haben mir nicht gefallen – außerdem hat mich der sportliche Ergeiz gepackt, mich einmal an einer eigenen Bibliothek zu versuchen. Zusammengefasst hat der ESP8266 mit dem DS18B20 die folgenden Features:

  • MQTT-Client
  • Zugriff auf ntp-Server
  • Webserver
  • Möglichkeit eines Web-Updates
  • Änderung des Mess-Intervalls über Webpage

Das Listing ist trotz Auslagerung der Timeserver-Methoden noch recht umfangreich. Daher lege ich nur einen Link zum Download.

So sieht die fertige Webpage dann aus.

Messgenauigkeit

Was mich etwas erstaunt hat, ist die Tatsache, dass man zwar im Netz unglaublich viele Anleitungen findet, mit dem DS18B20 und einem ESP, Arduino, Raspberry Pi, … die Temperatur zu messen und darzustellen, aber anscheinend brechen alle in Jubel aus, wenn dann letztendlich etwas angezeigt wird — und das war’s dann.
Ich habe mir einmal die Mühe gemacht, neben den DS18B20 ein relativ gutes Laborthermometer zu legen – nicht zuletzt, weil mir der Raum nie so warm vorkam, wie er lt. Messung sein sollte. Und siehe da – der Sensor gab immer Temperaturen aus, die etwa 2°C höher lagen, als die Temperatur lt. Laborthermometer sein sollte. Zunächst dachte ich, ich hätte vielleicht ein „Montags-Teil“ erwischt, aber der Vergleich mit anderen DS18B20 – auch von verschiedenen Händlern – zeigte für alle das gleiche Problem!
Die Lösung war so einfach wie niederschmetternd: Das teure Laborthermometer war fehlerhaft kalibriert. Ich habe gerade Vergleichsmessungen mit zwei anderen Thermometern durchgeführt und siehe da, die Temperaturwerte der Sensoren lagen im korrekten Fehlerfenster, das im Datenblatt ausgewiesen ist!

Mein nächster Gedanke war dann, dass sich die Dinger vielleicht beim Betrieb erwärmen. Ich testete das, indem ich einen Sensor klassisch anschloss, einen zweiten schloss ich so an, dass er über eine GPIO des ESP nur dann mit Spannung versorgt wurde, wenn wirklich gemessen werden sollte. D.h. der VCC-Pin kam an die GPIO0 des ESP und diese wurde nur kurz vor einer Messung auf HIGH gelegt und gleich danach wieder auf LOW. Das Ergebnis: Kein Unterschied.

Trotz intensiver Suche im Netz und Studium des Datenblatts habe ich keine Lösung für dieses Problem gefunden.
Meine „Notlösung“ besteht darin, vor der Ausgabe der Messwerte einfach 2°C herunter zu rechnen. Demnächst habe ich vor, einmal eine Messreihe zu starten und im Intervall von 0°C bis ca. 50°C die Messwerte Grad für Grad mit dem Labotthermometer zu verifizieren (und so vielleicht eine Kalibrierungskurve aufzunehmen…).

ESP und MQTT

Um die Daten von Messstationen im Rahmen einer Wetterstation einzusammeln bietet sich MQTT als ein genial einfaches Protokoll an. Man braucht lediglich einen MQTT-Broker , der auf einem Server läuft. Die Messstation sendet dann in festgelegten Zeitintervallen die jeweiligen Messwerte an den Broker.

Bei mir läuft der MQTT-Broker Mosquitto auf einem alten Raspberry Pi. Wer (noch) keinen eigenen MQTT-Broker einrichten möchte, kann auch einen öffentlichen Broker im Internet nutzen, z.B. iot.eclipse.org.
Um die Meldungen des ESP anzuzeigen, kann man z.B. das Programm mqtt-spy benutzen, das es z.B. auf GITHUB gibt.
Hat man ohnehin Mosquitto auf einem Raspi installiert, so kann man die Meldungen auch mithilfe von mosquitto_sub abonnieren.

Im meinem Beispiels-Sketch sendet der ESP in 10-Sekunden-Intervallen unter dem topic esp/uptime die jeweilige Uptime. Das Intervall in Sekunden lässt sich über die Konstante interval einstellen.

Wer gerne zusätzlich Statusmeldungen im Seriellen Monitor sehen möchte, muss die Zeilen mit Serial. … wieder einkommentieren.

Hier nun das Programm (auch zum Download):

#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>

#define FAKTOR 1000 // 60.000 ms = 1 min

//**** Diese Daten müssen angepasst werden *********************

#define WIFI_AP "meinWLAN"
#define WIFI_PASSWORD "meinPasswort"

#define TOKEN "ESP8266_MQTT_TOKEN"
char clientID[] = "MQTT_UPTIME";
char mqttBroker[] = "192.168.1.5"; // Adresse des MQTT-Brokers
int subNetIP = 1; //IP des Subnets
int myIP = 23; //nur der letzte Block der IP ändert sich
String myHostname = "ESP8266_MQTT";
String mqttSubject = "esp/uptime";
unsigned long lastMessage;
const int interval = 10;

//****** Ende Anpassung *****************************************

WiFiClient wifiClient;

PubSubClient client(wifiClient);

int status = WL_IDLE_STATUS;

void setup()
{
  //Serial.begin(115200);
  InitWiFi();
  client.setServer( mqttBroker, 1883 );
}

void loop() {
  if ( !client.connected() ) {
    reconnect();
  }
  if ( millis() - lastMessage > interval * FAKTOR ) { // Update and send only after 30 Seconds
    sendMQTTMessage(mqttSubject, upTime());
    lastMessage = millis();
  }
  client.loop();
}

String niceStr(int i) {  //Zahlen ggf. mit führender Null
  String out = "";
  if (i < 10) out += "0";
  out += String(i);
  return out;
}

String upTime() {
  String out = "";
  unsigned long up = millis();
  up = up / 1000;
  int d = up / 86400L; // days
  out = niceStr(d) + " d ";
  up = up % 86400;
  d = up / 3600L; // hours
  out += niceStr(d) + " h ";
  up = up % 3600;
  d = up / 60L; // minutes
  out += niceStr(d) + " m ";
  d = up % 60;
  out += niceStr(d) + " s";
  return out;
}

void sendMQTTMessage(String subject, String message) {   //hier könnte der Fehler liegen?
  char payload[100];
  char sub[100];
  message.toCharArray( payload, 100 );
  subject.toCharArray(sub, 100);
  //Serial.print("MQTT-Payload: ");
  //Serial.println(message);
  //Serial.print("MQTT-Subject: ");
  //Serial.println(sub);
  client.publish( sub, payload , true);
}

void connectToWiFi() {
  WiFi.begin(WIFI_AP, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    // Serial.print(".");
  }
  //Serial.println("Connected to AccessPoint");
}

void InitWiFi()
{
  //Serial.println("Connecting to AccessPoint ...");
  // attempt to connect to WiFi network
  IPAddress ip(192, 168, subNetIP, myIP);
  IPAddress gateway(192, 168, subNetIP, 1);
  IPAddress subnet(255, 255, 255, 0);
  IPAddress dns(192, 168, subNetIP, 1);
  WiFi.config(ip, dns, gateway, subnet); // auf feste IP einstellen
  WiFi.hostname(myHostname); //Hostnamen setzen
  WiFi.persistent(false);
  WiFi.mode(WIFI_OFF);
  WiFi.mode(WIFI_STA);
  connectToWiFi();
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    status = WiFi.status();
    if ( status != WL_CONNECTED) {
      connectToWiFi();
    }
    //Serial.print("Connecting to MQTT_Broker ...");
    // Attempt to connect (clientId, username, password)
    if ( client.connect(clientID, TOKEN, NULL) ) {
      //Serial.println( "[DONE]" );
    } else {
      //Serial.print( "[FAILED] [ rc = " );
      //Serial.print( client.state() );
      //Serial.println( " : retrying in 5 seconds]" );
      // Wait 5 seconds before retrying
      delay( 5000 );
    }
  }
}

ESP als Webserver

Eine Webpage, vom ESP aufgebaut!

Um den ESP8266 in das WLAN-Netz einzubinden ist zunächst mal die Bibliothek ESP8266WiFi.h zuständig, die bereits im ESP-Paket enthalten ist. Für die Arbeit als Webserver benötigt man zusätzlich die Bibliothek ESP8266WebServer.h (ebenfalls im ESP-Paket). Ich bevorzuge für die Einbindung des ESP ins Netz feste IP-Adressen, da man ihn damit leichter wiederfindet. Außerdem kann man alle im Haus werkelnden ESPs mit aufeinanderfolgenden Adressen in einem IP-Bereich ansiedeln, der für die dynamische DNS-Vergabe gesperrt ist.

Wie das Ganze konkret funktionieren kann, illustriert das folgende Listing (hier auch zum Download):


#include <ESP8266WiFi.h>
//#include <WiFiClient.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>

//**** Diese Daten müssen angepasst werden ***************

#define WIFI_AP "myWiFi" // Bez. des Access-Points
#define WIFI_PASSWORD "myPassword" // Access-Key

// Ich bevorzuge feste IPs, weil sich der WSP dann schneller wiederfinden lässt

int subNetIP = 1; //IP des Subnets
int myIP = 23; // gewünschte IP-Nummer
String myHostname = "ESP8266";

//**********************************************************

WiFiClient wifiClient;

ESP8266WebServer server(80); //Server an port 80

void setup()
{
  Serial.begin(115200);
  InitWiFi();
  server.on("/", handleRoot); // Routine für die root-Page
  server.on("/other", doOtherThings); // Routine für eine andere Seite
  server.begin();
  Serial.println("Webpage hört auf 192.168." + String(subNetIP) + "." + String(myIP));
}

void loop() {
  server.handleClient();
}

void connectToWiFi() {
  WiFi.begin(WIFI_AP, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
}

String niceStr(int i) {  //Zahlen ggf. mit führender Null
  String out = "";
  if (i < 10) out += "0";
  out += String(i);
  return out;
}

String uptime() {
  String out = "";
  unsigned long up = millis();
  up = up / 1000;
  int d = up / 86400L; // days
  out = niceStr(d) + " d ";
  up = up % 86400;
  d = up / 3600L; // hours
  out += niceStr(d) + " h ";
  up = up % 3600;
  d = up / 60L; // minutes
  out += niceStr(d) + " m ";
  d = up % 60;
  out += niceStr(d) + " s";
  return out;
}

String macAdresse() {
  String out = "";
  byte mac[6];
  WiFi.macAddress(mac);
  for (int i = 5; i > 0; i--) {
    out += hex(mac[i]) + ":";
  }
  out += hex(mac[0]);
  return out;
}

String hex(byte b) {
  String out = String(b, HEX);
  if (out.length() == 1) out = "0" + out;
  return out;
}

void InitWiFi()
{
  Serial.println("Verbinde mit AccessPoint ...");
  //  Versuch, mit dem WiFi-Netz zu verbinden
  IPAddress ip(192, 168, subNetIP , myIP);
  IPAddress gateway(192, 168, subNetIP, 1);
  IPAddress subnet(255, 255, 255, 0);
  IPAddress dns(192, 168, subNetIP, 1);
  WiFi.config(ip, dns, gateway, subnet); // auf feste IP einstellen
  WiFi.hostname(myHostname); //Hostnamen setzen
  WiFi.persistent(false);
  WiFi.mode(WIFI_OFF);
  WiFi.mode(WIFI_STA);
  connectToWiFi();
  Serial.println(" verbunden!");
}

void doOtherThings() {
  String msg = "<html><head><title>Another Page</title></head><body>";
  msg += "<center><br><br><br><br><br><H1>Dies ist noch eine Webpage</H1>";
  msg += "<a href = \"/\"> zurück zur Hauptseite</a></center></body></html>";
  server.send(200, "text/html", msg);
}

void handleRoot() {
  String msg = "<meta http-equiv=\"refresh\" content=\"60\"> ";
  msg += "<center><H1><u>" + String(myHostname) + "</u></H1> </p>";
  msg += "<br />Diese Seite aktualisiert im Minutentakt die Uptime-Zeit!<br /><br />";
  msg += "<b>Online seit:</b> " + uptime() + "<br />";
  msg += "<b>IP-Adresse:</b> 192.168." + String(subNetIP) + "." + String(myIP) + "<br />";
  msg += "<b>MAC-Adresse:</b> " + macAdresse() + "<br />";
  msg += "===================================================<br />";
  msg += "<a href = \"other\">Hier gehts zu einer anderen Webpage ...</a><br />\n";
  server.send(200, "text/html", msg);
}
Der ESP8266 (ESP-01)

Der ESP8266 (ESP-01)

Die meisten meiner Sensoren habe ich an verschiedene Varianten des ESP8266 angeschlossen. Die ESPs sind preiswerter als die Arduinos und haben zudem den Vorteil, dass sie WLAN bereits onboard haben. Die Kommunikation mit einem Server ist also kein Problem.

Den ESP8266 gibt es in unglaublich vielen Varianten (d.h. er ist auf sehr vielen unterschiedlichen Boards verbaut). Die Modelle unterscheiden sich vor allem darin, wieviel Anschlüsse des Prozessors herausgeführt werden.

Ich werde hier nicht auf alle Einzelheiten eingehen, aber ein wesentlicher Unterschied dieser Modelle sollte genannt werden: Der ESP8266 besitzt nämlich einen Anschluss, der erforderlich ist, um ihn in einen sogenannten „Deep Sleep„-Modus zu bringen, in dem er so gut wie keine Energie verbraucht. Um ihn aus dem Deep-Sleep-Modus automatisch wieder „aufzuwecken“, muss dieser Anschluss (GPIO16 bzw. D0) mit dem Reset verbunden sein, sonst wachte der ESP nicht automatisch auf.

Das einfachste Modell ist der ESP-01, bei dem der GPIO16 nicht herausgeführt ist. Damit ist er nicht ohne weiteresDeep Sleep„-fähig. (Mehr dazu weiter unten!)

Seine wesentlichen Vorteile sind der Preis (weniger als 2,50€ – Stand März 2020) sowie die geringe Baugröße.

Nachteile:

  • Will man ihn auf einem Breadboard einsetzen, so hat man die Wahl zwischen „frei fliegender“ Verkabelung oder der Anschaffung eines Adapters
  • Zum Programmieren benötigt man zusätzlich einen USB-TTL-Adapter (der aber eigentlich in jede Elektronik-Bastelkiste gehört).

Will man den ESP-01 flashen, so muss man folgende Verbindungen zum USB-TTL-Adapter herstellen:

GND → GND
VCC → 3.3V
RX → TX
TX → RX

Damit der ESP in den „Flash“-Mode versetzt wird, muss beim Einschalten der GPIO0-Anschluss auf Masse gelegt werden. Es empfielt sich, dies über einen Pushbutton zu realisieren, denn nach dem Flashen läuft das Programm gleich los und wenn der GPIO0 dann noch auf Masse liegt, im Programm aber auf 3,3V gesetzt wird, gibt es Probleme!

ESP-01
Breadboard-Adapter

USB-TTL-Adapter

Ich programmiere den ESP über die Arduino IDE. Diese muss allerdings zunächst für den Umgang mit dem ESP fit gemacht werden. Wie das geht ist im Netz ausführlich beschrieben, z.B besonders ausführlich hier auf einer Seite des Heise-Verlags.

Die Bedeutung der Anschlüsse

Zum Teil habe ich die Anschlüsse ja bereits oben beschrieben. Ground und VCC(3.3V) sind ohnehin klar. TX und RX können so wie GPIO0 und GPIO2 als GPIOs betrieben werden: TX wäre dann GPIO1, RX wäre GPIO3. Der CH_PD-Pin muss zum Betrieb des ESPs über einen Pullup Widerstand (10k) auf High gezogen werden. Bei einigen Modellen ist das bereits auf dem Board erledigt, bei anderen nicht. Am besten misst man nach – ich habe anfangs oft völlig verzweifelt nach einem Fehler gesucht, wenn eine Schaltung mal funktionierte, mal nicht, bis ich diesen Umstand bemerkte. Einfach mal den Widerstand zwischen CH_PD und VCC messen – ca. 10k ist in Ordnung, sonst (bei weit größerem Widerstand) ist ein eigener Pullup-Widerstand (4.7k – 10k) erforderlich! Wie ich aus anderen Quellen entnommen habe, war der ESP früher einmal gedacht, um den Arduino WLAN-fähig zu machen. Der CH_PD-Pin war in dem Zusammenhang als Möglichkeit vorgesehen, den ESP auszuschalten, wenn er nicht gebraucht wurde. Im Alleinbetrieb ist der Pin obsolet und wird wie oben beschrieben entweder auf per Pullup aktiviert oder gar nicht benutzt – je nach ESP-Variante.

Auch für den Reset-Pin wird ein Pullup manchmal empfohlen – bei mir war das aber bei allen Modellen bereits über das Board erledigt – im Zweifel auch hier messen! Für einen Reset muss der Pin auf Masse gelegt werden.

Damit wäre (fast) alles zum ESP-01 gesagt. Der in meinen Augen größte Nachteil dieser Bauform ist die Tatsache, dass der GPIO16 nicht herausgeführt wurde. Deshalb kann der ESP aus dem Deep Sleep nicht von allein wieder erwachen. Mit einem bisschen Fummelei geht es allerdings doch: Der GPIO16 ist direkt auf dem Chip verhältnismäßig leicht zu erreichen, weil er auf einer Ecke liegt (rot markiert). Mit etwas Geschick gelingt es, dort einen feinen Draht anzulöten und ihn darüber mit dem Reset-Pin zu verbinden.

ESP mit angelötetem Draht für den Deep Sleep

Bei einigen ESP-01-Varianten existiert eine rote LED, die die Spannungsversorgung anzeigt (grün markiert). Diese erlischt auch im Deep Sleep Modus nicht und verbraucht Energie. Man kann sie jedoch leicht entfernen. Ich habe sie einfach vorsichtig mit einem Teppichmesser weggekratzt. Der Betrieb des ESP wird durch das Entfernen nicht beeinflusst!

Anders, als bei anderen ESP-Modellen läßt sich der ESP-01 übrigens auch mit fester Drahtverbindung zwischen GPIO16 und Reset weiterhin wie gewohnt flashen. Die Drahtbrücke muss dafür nicht unterbrochen werden

Möchte man sich die Löterei sparen, aber dennoch nicht auf den Deep Sleep Modus verzichten (bzw. das automatische Aufwachen daraus), so muss man auf ein anderes Modell des ESP8266 ausweichen. Einige davon werde ich in einem späteren Blog besprechen…

Man kann den ESP auch ohne jede Peripherie einfach als Webserver betreiben. Wie das geht, habe ich in einem anderen Blog beschrieben: ESP als Webserver

Einen Beispielssketch, der die Arbeitsweise des ESP als MQTT-Client zeigt habe ich im Blog ESP und MQTT beschrieben.

Projekt Wetterstation

Projekt Wetterstation

Noch eine Wetterstation? Ich weiß, dass es im Netz nur so von Beiträgen dieser Art wimmelt, aber jeder Beitrag hat letztendlich seine eigene Note!

Sensoren für Umwelt- und Wettermessungen gibt es online für kleines Geld – wenn man direkt aus China kauft bekommt man die Sensoren fast nachgeschmissen.

Die erste Frage, die sich beim Aufbau einer Messstation stellt, ist die Frage nach dem geeigneten Board. Man findet viele Beispiele, die den Raspberry Pi einsetzen, aber meiner Meinung nach würde man in diesem Fall mit Kanonen auf Spatzen schießen. Der Raspi würde sich die meiste Zeit langweilen. Außerdem ist er von allen Boards die teuerste Variante, selbst der Raspberry Zero kostet noch um die 20 €.

Der Arduino ist schon ein Stück preiswerter, in der Version Arduino Nano bekommt man ihn für ca. 5€. Hier steht man allerdings vor dem Problem, dass es keine direkte Implementierung von LAN, WLAN oder ähnlichem gibt – das muss man noch extra nachrüsten, was den Preis in die Höhe treibt.

Ich bin dann letztendlich beim ESP8266 gelandet. Die einfachste Version, ESP-01, bekommt man bereits für etwas mehr als 2€ pro Stück. (Eine ausführlichere Beschreibung des ESP-01 in einem anderen Blog!) Er hat bereits ein WLAN-Modul onboard sowie 1MB Speicher für Programme. Um einen Temperatursensor (z.B. den Dallas DS18B20), einen Temperatur-/Luftfeuchtigkeitssensor (z.B. DHT22) oder selbst einen Sensor für Temperatur, Feuchte und Luftdruck (BME/BMP 280) zu betreiben, reicht er völlig aus.

ESP8266
ESP-01
Dallas DS18B20
DHT11/DHT22/AM2302

Der grundsätzliche Aufbau ist absolut simpel: Man verbindet jeweils Masse (Ground) des Sensors mit Masse des ESP, entprechens VCC mit VCC. Die Datenleitung wird einmal mit dem IO02-Pin des ESP verbunden sowie über einen 4,2k Widerstand auf 3,3V gelegt. Hier das Beispiel-Layout für des DS18B20:

Fritzing Layout für ESP und DS18B20
Fritzing-Layout

Als Spannungsquelle habe ich hier zwei AA-Batterien gesetzt. Man kann natürlich die ca 3,3V auf jede beliebige Weise bereitstellen. Ich habe z.B. alte Steckernetzteile von irgendwelchem Elektronikschrott reaktiviert und über Step-Down-Regler auf 3,3V herunter geregelt. (Man sollte da nicht gerade ein 12V Netzteil nehmen – dann fängt der Stepdown-Regler ganz ordentlich an zu heizen!)

Fehlt noch die Software („Firmware“) für den ESP und der Sensor kann seine Daten liefern. Für das Ausliefern der Daten gibt es natürlich gleich wieder eine Vielzahl von Möglichkeiten – Speichern auf einem Server, Abruf von einer Webpage (der ESP kann als Webserver arbeiten), Anschluss an einen MQTT-Broker …

Ich habe mich für eine Kombination aus MQTT und Webpage entschieden. Zunächst MQTT, weil es eine super simple Art ist, die Daten anderen Geräten zur Verfügung zu stellen, dann die Webpage, weil es mir darüber möglich ist, weitere Infos abzurufen sowie Einstellungen am ESP zu verändern.

Für den MQTT-Broker habe ich meinen ältesten Raspberry B2 ausgesucht – darauf läuft der Broker ohne Probleme, zusätzlich betreibe ich darauf noch NodeRed um die eingehenden Daten graphisch aufzubereiten und zu veröffentlichen und letztendlich habe ich auf den Raspi noch einen BME/BMP 280 und einen 433MHz-Sender gesteckt, so dass der Raspi in dem Raum, in dem er steht (mein Büro) Luftdruck, Temperatur und Feuchte misst, der 433MHz-Sender steuert meine alten Funksteckdosen.

Raspberry B2 mit Aufsteckplatine und Sensor / 433MHz Sender
Raspberry B2 mit Bme/BMP280 und 433MHz-Sender

Die Details werde ich in weiteren Blog-Beiträgen veröffentlichen!