Smarthome-Geräte mit ESP-Mikrokontrollern
Um den Einstieg in ein Selbstbau-SmartHome zu machen, bieten sich ESP82661 oder ESP322 Mikrocontroller an. Sie sind klein, günstig, brauchen wenig Strom und sind leicht zu programmieren. Ein sehr einfaches Gerät, könnte ein Raumthermometer sein. So etwas kann man sich sehr einfach mit einem ESP8266, einem 4,7 kOhm Widerstand, einem DHT223 Sensor und ein paar Kabeln basteln. Programmiert werden kann so ein ESP8266 z.B. über die Arduino Entwicklungsumgebung4, die kostenlos heruntergeladen werden kann.
Der ESP8266 ist ein kleiner Mikrocontroller, den es auch bereits fertig aufgelötet auf einer Platine mit USB Port gibt. Diese Variante ist zumindest für den Einstieg zu empfehlen, da so direkt eine Verbindung mit der Arduino Umgebung hergestellt werden kann.
Damit die Arduino Umgebung mit dem ESP reden mag, muss man unter File / Preferences
unter Additional Board Manager URLs
die folgende Adresse hinterlegen: https://arduino.esp8266.com/stable/package_esp8266com_index.json
Unter Tools / Board / Board manager
kann man nun das nötige Paket ESP8266
installieren.
Anschließend kann man unter Tools / Board / ESP8266 Boards
das Board Generic ESP8266 module
wählen.
Den ESP8266 verkabelt man mit dem DHT22 wie folgt, hierzu nutzt man zunächst am Besten ein sog. Breadboard5:
Bei der Verkabelung ist es wichtig am ESP dieselben Pins zu nutzen, wie in der Abbildung. Sollte dies nicht möglich sein, müssen die genutzten Pins im Programmcode entsprechend angepasst werden, damit der ESP die anfallenden Daten korrekt auslesen kann.
Ist die Verkabelung erledigt, kann man sich an die Programmierung des ESP machen. Hierzu überträgt man den folgenden Code in die Arduino Umgebung, steckt den ESP8266 via USB an und lädt den Code in dessen Speicher. Damit der Code funktioniert, müssen in der Arduino Umgebung die mittels #include
importieren Bibliotheken vorhanden sein, diese kann man einfach über Tools / Library manager
suchen und installieren. Sollte eine Bibliothek nicht im Library manager vorhanden sein, hilft oft eine Google-Suche weiter, aber Achtung, es gibt viele gleich oder ähnlich benannte Bibliotheken, die untereinander aber nicht kompatibel sind. Es ist daher eine gute Idee, im Programmcode hinter die #include
-Anweisung die Downloadadresse der Bibliothek zu notieren:
#include <FS.h> //builtin
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager, via lib manager
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson, via lib manager
#include <PubSubClient.h> //https://github.com/knolleary/pubsubclient, via lib manager
#include <DHT.h> //https://github.com/adafruit/DHT-sensor-library, via lib manager
// Pinbelegung
#define TRIGGER_PIN 0
#define DHTPIN 5
#define DHTTYPE DHT22
// Defaultwerte und sonstige Variablen
char mqtt_server[64] = "mqtt-broker.local";
char mqtt_port[6] = "1883";
char mqtt_username[64] = "MQTT_USER";
char mqtt_password[64] = "MQTT_PASSWORD";
char mqtt_topic[255] = "home/esp/SENSOR";
bool shouldSaveConfig = false;
unsigned long lastMsg = 60000;
// Initialisierung
WiFiManager wifiManager;
WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHTPIN, DHTTYPE);
// Callbackfunktion Captive Portal
void saveConfigCallback () {
Serial.println("Should save config");
shouldSaveConfig = true;
}
// Config-Reset Button
void checkButton(){
if ( digitalRead(TRIGGER_PIN) == LOW ) {
delay(50);
if( digitalRead(TRIGGER_PIN) == LOW ){
Serial.println("Button Pressed");
delay(3000);
if( digitalRead(TRIGGER_PIN) == LOW ){
Serial.println("Button Held");
Serial.println("Erasing Config, restarting");
wifiManager.resetSettings();
SPIFFS.format();
ESP.restart();
}
if (!wifiManager.startConfigPortal("OnDemandAP")) {
Serial.println("failed to connect or hit timeout");
delay(3000);
ESP.restart();
}
}
}
}
// MQTT reconnect
void reconnect() {
String port = String(mqtt_port);
client.setServer(mqtt_server, port.toInt());
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
String clientId = "homesensor-";
clientId += String(random(0xffff), HEX);
Serial.println(clientId);
if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.println(client.state());
delay(5000);
}
}
}
// Einrichtung während des Startvorgangs
void setup() {
Serial.begin(115200);
pinMode(TRIGGER_PIN, INPUT);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
// Einlesen der Konfigurationsdatei
Serial.println("mounting FS...");
SPIFFS.begin();
Serial.println("mounted file system");
Serial.println("reading config file");
File configFile = SPIFFS.open("/config.json", "r");
Serial.println("opened config file");
size_t size = configFile.size();
std::unique_ptr<char[]> buf(new char);
configFile.readBytes(buf.get(), size);
DynamicJsonDocument json(1024);
auto deserializeError = deserializeJson(json, buf.get());
if ( ! deserializeError ) {
Serial.println("parsed json");
}
configFile.close();
// Konfiguration der custom Parameter im Captive Portal
WiFiManagerParameter custom_mqtt_server("mqtt_server", "mqtt server", mqtt_server, 64);
WiFiManagerParameter custom_mqtt_port("mqtt_port", "mqtt port", mqtt_port, 6);
WiFiManagerParameter custom_mqtt_username("mqtt_username", "mqtt username", mqtt_username, 64);
WiFiManagerParameter custom_mqtt_password("mqtt_password", "mqtt password", mqtt_password, 64);
WiFiManagerParameter custom_mqtt_topic("mqtt_topic", "mqtt topic", mqtt_topic, 255);
wifiManager.addParameter(&custom_mqtt_server);
wifiManager.addParameter(&custom_mqtt_port);
wifiManager.addParameter(&custom_mqtt_username);
wifiManager.addParameter(&custom_mqtt_password);
wifiManager.addParameter(&custom_mqtt_topic);
wifiManager.setSaveConfigCallback(saveConfigCallback);
//wifiManager.resetSettings();
// Speichern der Configdatei
if (shouldSaveConfig) {
Serial.println("saving config");
DynamicJsonDocument json(1024);
json["mqtt_server"] = mqtt_server;
json["mqtt_port"] = mqtt_port;
json["mqtt_username"] = mqtt_username;
json["mqtt_password"] = mqtt_password;
json["mqtt_topic"] = mqtt_topic;
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("failed to open config file for writing");
}
serializeJson(json, configFile);
configFile.close();
}
// Setzen der Konfiguration
strcpy(mqtt_server, json["mqtt_server"]);
strcpy(mqtt_port, json["mqtt_port"]);
strcpy(mqtt_topic, json["mqtt_topic"]);
strcpy(mqtt_username, json["mqtt_username"]);
strcpy(mqtt_password, json["mqtt_password"]);
Serial.println("The values in the file are: ");
Serial.println("\tmqtt_server : " + String(mqtt_server));
Serial.println("\tmqtt_port : " + String(mqtt_port));
Serial.println("\tmqtt_topic : " + String(mqtt_topic));
Serial.println("\tmqtt_username: " + String(mqtt_username));
Serial.println("\tmqtt_password: ********");
// WLAN connect
if (!wifiManager.autoConnect()) {
Serial.println("failed to connect and hit timeout");
delay(3000);
ESP.restart();
}
Serial.println("WiFi connected...");
// DHT sensor initialisieren
dht.begin();
}
void loop() {
digitalWrite(LED_BUILTIN, LOW);
// MQTT Verbindung herstellen
if (!client.connected()) {
reconnect();
}
client.loop();
// Reset-Button checken
checkButton();
// DHT sensor auslesen
float temp = dht.readTemperature();
float humi = dht.readHumidity();
// DHT sensordaten als JSON string
String payload = "{\"temperature\": "+String(temp)+", \"humidity\": "+String(humi)+"}";
Serial.println(payload);
// Alle 60 sekunden JSON daten an MQTT senden
unsigned long now = millis();
if (now - lastMsg > 60000) {
lastMsg = now;
Serial.println("Publish MQTT message to " + String(mqtt_topic));
client.publish(mqtt_topic, payload.c_str());
}
// 10 sek. pause
digitalWrite(LED_BUILTIN, HIGH);
delay(10000);
}
Der Programmcode ist so aufgebaut, dass die Sensordaten des DHT22 zu einem MQTT-Broker gesendet werden. Was ein MQTT-Broker ist und wie man ihn installiert habe ich hier beschrieben.
Um die Übertragung des Programmcodes zu starten kann man einfach die Tastenkombination Strg + U
drücken. Es dauert dann einen kleine Moment bis der Quelltext in Maschinencode übersetzt wurde. Im Anschluss an diesen Vorgang startet die Übertragung des Programm auf den ESP8266, eventuell muss man hierzu den FLASH-Button auf dem ESP drücken, damit dieser Vorgang gestartet werden kann.
Wurde der Programmcode erfolgreich auf den ESP übertragen, startet dieser neu und baut ein eigenes WLAN auf. Zu diesem kann man sich z.B. mit seinem Telefon verbinden. Anschließend kann man die Zugangsdaten zum eigenen WLAN, sowie die Daten für den MQTT-Broker eintragen, dieser erhält dann die Messdaten des DHT22-Sensors. Damit man die Daten im Broker zuordnen kann, wird für jedes Gerät ein sog. Topic genutzt. In diesem Beispiel verwenden wir home/living/SENSOR
. So weiß ich direkt, dass der Sensor sich bei mir Zuhause im Wohnzimmer befindet. Dies hilft auch später, wenn man mit anderen Programmen auf die MQTT-Daten zugreift.
Die gesendeten Daten kann man am MQTT-Broker wie folgt einsehen, wichtig ist hierbei die exakt selbe Topic Einstellung zu nutzen, wie bei der Einrichtung des ESP (da der ESP nur jede Minute einmal seine aktuellen Messdaten sendet, kann es einen Moment dauern, bis entsprechende Daten angezeigt werden):
mosquitto_sub -h localhost -t home/living/SENSOR -u $username -P $password
{"temperature": 26.40, "humidity": 34.10}
{"temperature": 26.50, "humidity": 34.00}
Sollten auch nach über einer Minute keine Daten angezeigt werden, kann man in der Arduino Umgebung Strg + Umschalt (Shift) + M
drücken, daraufhin öffnet sich der Serial-Monitor, hier finden sich Statusinformationen zum ESP, unter anderem sollte jede Minute die Zeile Publish MQTT message to home/living/SENSOR
zu lesen sein. Möglicherweise findet sich aber auch die Angabe failed to connect and hit timeout
. In diesem Fall ist die Verbindung mit dem WLAN fehlgeschlagen. Durch Halten des RST
-Buttons auf dem ESP kann der Einrichtungsvorgang erneut gestartet werden.