ESP32 Mikrocontroller stellen eine günstige, relativ performante, WLAN- und Bluetooth-fähige Basis für Internet of Things (IoT) – Implementierungen bereit. Sie lassen sich zudem relativ einfach mit SmartHomeNG nutzen.

Das hier vorgestellte Beispiel, das u.a. für die Überwachung des Füllstands einer Zisterne verwendet werden kann, ist auf Basis eines Espressif ESP32 entwickelt worden, der zum Zeitpunkt dieses Artikels für ca. 15 Euro zu haben ist. Dazu wird für die Entfernungsmessung ein HC-SR04 Ultraschallsensor verwendet, der für knapp 2 Euro erhältlich ist.

Die Daten werden dabei an ein Item übermittelt, das via Webservices-Plugin erreichbar ist. Mehr zu diesem Plugin ist unter https://www.smarthomeng.de/das-smarthomeng-webservices-plugin zu finden. Die korrekte Konfiguration des Items wird in diesem Artikel als Voraussetzung angenommen.

Update: Inzwischen ist der zweite Teil zum Thema erschienen, bei dem ich erläutere, wie man die Entfernungsdaten auf ein OLED Display bringt. Direkteinstieg unter Entfernungsmessung mit ESP32 und SmartHomeNG Teil 2: Anbindung eines SSD1306 OLED Displays.

Installation des Espressif ESP32 unter Windows

Als erstes muss der ESP32 als USB Gerät erkannt werden. Dazu kann die Anleitung unter https://dl.espressif.com/doc/esp-idf/latest/get-started/establish-serial-connection.html befolgt werden. Eine alternative Anleitung für MacOS und Linux ist dort ebenfalls zu finden.

Zusammengefasst muss der Treiber für das ESP32 Core Board von https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers installiert werden. Danach sollte das Gerät als COM3 auftauchen, wie in untenstehendem Bild zu sehen ist. In meinen Tests klappte dies nicht mit jedem Micro-USB-Kabel. Daher sind ggf. Kabel durchzuprobieren.

Einrichtung der Arduino IDE

Als nächstes muss die Arduino IDE installiert werden. Diese kann kostenlos über https://www.arduino.cc/en/main/software oder bei Windows 10 den Microsoft Store bezogen werden.

Über „Werkzeuge“ → „Board“ muss dort nun das „ESP32 Dev Module“ ausgewählt werden. Die weiteren Einstellungen sind ebenfalls im folgenden Bild zu sehen:

Wird rechts unten noch ein „None auf COM3“ angezeigt, so ist das nicht weiter dramatisch, sofern der ESP32 bereits im Geräte-Manager auf COM3 sichtbar ist.

Versuchsaufbau: Espressif ESP32 mit HC-SR04 Ultraschallsensor

Die Funktionsweise des HC-SR04 Ultraschallsensors ist schnell erklärt:

Der HC-SR04 hat einen Lautsprecher und ein Mikrofon verbaut. Über den Lautsprecher wird ein Ultraschall-Ton ausgegeben. Dieser wird von einem Objekt reflektiert und über das direkt neben dem Lautsprecher platzierte Mikrofon erfasst. Die Zeitdauer zwischen dem Senden des Signals und dem Empfang des Echos wird gemessen. Über die Schallgeschwindigkeit von 343,2 m/s bei 20° Celsius kann nun die Distanz zu demjenigen Objekt berechnet werden, an dem das Signal relektiert wird.


Da der Sensor in Mikrosekunden misst, muss zur Bestimmung der Distanz der gemessene Wert erst halbiert (einfache Strecke) und dann mit 0,03432 multipliziert bzw. grob überschlagen durch 29.1 geteilt werden (1/29.1 = 0,03436).

Für den Versuchsaufbau muss der HC-SR04 an den ESP angeschlossen werden. Der HC-SR04 besitzt dabei die Anschlüsse „Gnd“, „Echo“, „Trig“ und „Vcc“.

„Gnd“ wird am ESP ebenfalls an „Gnd“ angeschlossen. „Vcc“ kommt an den „5V“ Anschluss für die Stromversorgung. „Echo“ und „Trig“ werden an die jeweiligen GPIOs angeschlossen. Im Beispiel wird „Echo“ auf „IO4“ und „Trig“ auf „IO15“ gelegt. Diese sind später im Programmcode zu referenzieren.

Programmierung des ESP32 via Arduino IDE und C-Code

Als letztes ist nun der ESP32 via Arduino IDE zu programmieren.

Als Bibliotheken werden via „Sketch“ → „Bibliothek einbinden“ → „Bibliotheken verwalten“ die „WiFi“ Bibliothek (1.2.7) und die „Wire“ Bibliothek (1.0.0) installiert. Erstere ist für den WiFi-Zugriff, zweitere für den Zugriff auf die GPIO’s.

Als Nächstes ist der Quellcode zu erstellen. Dabei sind im Wesentlichen die PINs für den Trigger („Trig“ → triggerPin) und den Echo-Anschluss („Echo“ → echoPin) des HC-SR04 Ultraschallsensors, die WiFi-SSID (ssid[]), das WiFi-Passwort (pass[]), die IP von SmartHomeNG (host[]) und der Port des Webservices Interfaces in SmartHomeNG (port) zu setzen. Weiterhin ist der Itempfad desjenigen Items zu setzen, dass die Entfernung in Zentimeter aufnehmen soll (cm_item[]). Im Beispiel hat dieses Item den Pfad test.numeric.

Das zugehörige Item ist bei aktiviertem Webservices-Plugin wie folgt konfiguriert:


%YAML 1.1
---
test:

    numeric:
        type: num
        visu_acl: rw
        webservices_set: outside
        webservices_data: 'val'

Die Initialisierung der WiFi-Verbindung wird in der Methode setup aufgerufen. Des Weiteren werden dort die Input- und Output GPIO PINs gesetzt.

Die Methode initialise_wifi sorgt dafür, dass die WiFi Verbindung aufgebaut wird. Am Ende der Methode werden via print_wifi_status die Daten der Verbindung ausgegeben:

Attempting to connect to SSID: 
<wifi-name>
Connected to wifi
SSID: <wifi-name>
IP Address: 192.168.178.23
signal strength (RSSI):-77 dBm

#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiServer.h>
#include <WiFiUdp.h>
#include <Wire.h>

// PINs
const int triggerPin = 15;
const int echoPin = 4;

// VARs
const int runs = 3;

// Wifi
char ssid[] = "<wlan_id>";          // your network SSID (name)
char pass[] = "<wlan_password>";    // your network password
char host[] = "192.168.178.100";    // ip of service interface in SmartHomeNG
int port = 4321;                    // port of service interface in SmartHomeNG
char cm_item[] = "test.numeric";
int status = WL_IDLE_STATUS;
WiFiClient client;

void setup() {
  Serial.begin(19200);
  initialise_wifi();
 
  // PIN-Modes
  pinMode(triggerPin, OUTPUT);
  pinMode(echoPin, INPUT);
  digitalWrite(triggerPin, HIGH);
}

void loop() {
  float distance = get_distance();
  float avg_distance = get_distance_avg();
 
  Serial.write("distance: ");
  Serial.print(distance) ;
  Serial.write(" , avg. distance: ");
  Serial.print(avg_distance) ;
  Serial.write(" cm\n");
 
  Serial.println("\nStarting connection to server...");
  // Wenn die Verbindung möglich ist, Verbindungsdaten an Serial zurückgeben.
  while (client.available()) {
    char c = client.read();
    Serial.write(c);
  }

  if (client.connect(host, port)) {
    Serial.println("\nconnected to server");
    // HTTP Request gegen SmartHomeNG / das Webservices-Plugin durchführen
    char buffer[64];
    int ret = snprintf(buffer, sizeof buffer, "GET /ws/items/%s/%f HTTP/1.1", cm_item, avg_distance);
    Serial.println(buffer);
    client.println(buffer);
    client.println("Host: ");
    client.println();
  } 
  Serial.print("\n");
  // 1 Sekunde Warten
  delay(1000);
}

float get_distance() {
  float duration=0;
  float distance=0;
 
  digitalWrite(triggerPin, LOW);
  delayMicroseconds(2); 
  noInterrupts();
  digitalWrite(triggerPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(triggerPin, LOW); 
  duration = pulseIn(echoPin, HIGH); // Erfassung - Dauer in Mikrosekunden 
  interrupts();

  distance = (duration / 2) / 29.1; // Distanz in CM
  return(distance);
}

float get_distance_avg() {
  float alt = 0;
  float avg;
  float dist;
  int i;

  delay(10);
  alt = get_distance();
  delay(10);
  for (i=0; i<runs; i++) {
    dist = get_distance();
    avg = (0.8*alt) + (0.2*dist);
    alt = avg;
    delay(10);
  }
  return (avg);
}

static void initialise_wifi(void)
{
  // Versuche ins WiFi Netzwerk zu verbinden
  while (status != WL_CONNECTED) {
    Serial.println("");
    Serial.println("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Ins WPA/WPA2 Netzwerk verbinden. 
    status = WiFi.begin(ssid, pass);
    // 10 Sekunden auf die Verbindung warten:
    delay(10000); 
  }
  Serial.println("Connected to wifi");
  print_wifi_status();
}

void print_wifi_status() {
  // SSID des WiFi Netzwerkes ausgeben:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID()); 

  // WiFi IP Adresse des ESP32 ausgeben:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // WiFi Signalstärke ausgeben:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

In der Methode loop findet nun die konkrete Ermittlung des Abstands statt. In der dort aufgerufenen Methode get_distance_avg() werden mehrere Messungen (runs) durchgeführt, um Messfehler zu minimieren. get_distance() wird zum Vergleich ebenfalls einzeln aufgerufen.

In jedem run wird in der Methode get_distance_avg() nun ebenfalls get_distance() aufgerufen. Dort findet der technische Teil des Messung statt: Zuerst wird der Trigger auf LOW gesetzt: digitalWrite(triggerPin, LOW);. Danach wird 2 Mikrosekunden gewartet und der Trigger auf HIGH gesetzt (digitalWrite(triggerPin, HIGH);). Nach erneutem Warten von 10 Mikrosekunden (erst nach dieser Zeit sendet der Sensor Ultraschallwellen) wird der Trigger wieder auf LOW gesetzt (digitalWrite(triggerPin, LOW);) und via duration = pulseIn(echoPin, HIGH); die Dauer zwischen Puls und Empfang des Echos gemessen. Während der Zeitmessung sollten Interrupts zudem abgeschaltet werden (noInterrupts();).
Zuletzt wird die Entfernung in Zentimetern, wie oben bereits erklärt, mit distance = (duration / 2) / 29.1; berechnet und zurückgegeben.

In get_distance_avg() wird dies nun mehrfach wiederholt und ein gleitender gewichteter Durchschnittswert (avg = (0.8*alt) + (0.2*dist);) gebildet. Werden dort trotzdem noch starke Abweichungen festgestellt, können bspw. die Anzahl der runs erhöht oder die Gewichtung verändert werden.

Das Ergebnis von get_distance_avg() wird dann via HTTP Get Request an das Webservices-Plugin (Simple Interface) und das konfigurierte Item übertragen. Auch diese Vorgänge werden im Code ausgegeben:

distance: 17.41 , avg. distance: 17.42 cm

Starting connection to server...
HTTP/1.1 200 OK
Content-Length: 65
Server: CherryPy/14.0.1
Content-Type: application/json
Date: Tue, 03 Apr 2018 19:31:48 GMT

{"Success": "Item with item path test.numeric set to 17.419243."}
connected to server
GET /ws/items/test.numeric/17.419930 HTTP/1.1

Wir das REST-Interface des Webservices-Plugins verwendet, so sieht der Codeblock zum Senden der Daten wie folgt aus:

#include <HTTPClient.h>
[...]
if (client.connect(host, port)) {
     HTTPClient http;    //Declare object of class HTTPClient
     char portStr[12];
     sprintf(portStr, "%d", port);
     char url[128];
     sprintf(url, "http://%s:%s/rest/items/%s", host, portStr, cm_item);
     http.begin(url);  //Specify request destination
     Serial.println(url);
     http.addHeader("Content-Type", "application/json");                //Specify content-type header
     char cmStr[12];
     sprintf(cmStr, "%f", cm);
     int httpCode = http.PUT(cmStr);                                    //Send the request
     String payload = http.getString();                                 //Get the response payload
     http.end();                                                        //Close connection
} 

Erster Programmstart und Test

Ist der ESP32 via USB verbunden, so kann das Programm über das „Haken“-Icon links oben auf Syntaxfehler geprüft und kompiliert und mittels des „Pfeil“-Icons daneben auf den ESP32 übertragen werden.

Gleichzeitig sollte vor einer Übertragung (und damit dem Programmstart) über „Werkzeuge“ → „Serieller Monitor“ der Monitor für die Testausgaben geöffnet werden.

Tipp: Plausible Distanzen kommen nur heraus, wenn der Sensor etwas erhöht, bspw. auf einer Kaffeetasse platziert wird. Anstonsten stören Reflektionen am Boden die Messwerte. Bei der Messung sollte daher generell beachtet werden, dass sich das Ultraschallsignal kegelförmig ausbreitet. Auf große Distanzen (> 1m) muss der Sensor so genau wie möglich auf das Zielobjekt ausgerichtet werden. Man sollte zudem Acht geben, dass das emittierte Signal auf einer möglichst parallelen Fläche reflektiert wird. Als Oberfläche für den Abprall der Impulse kann testweise bspw. ein Buch platziert werden. Die im Beispiel gemessenen 12,6 cm sind dabei bis auf ca. 5 mm genau.

Fazit

Mit dem ESP32 und einem HC-SR04 lässt sich für unter 20 Euro eine relativ genaue Abstandsmessung implementieren, die sehr simpel via WLAN an SmartHomeNG Daten sendet. Als Anwendungsfall wird die Installation im Sommer in meiner Zisterne installiert werden. Dazu folgt ein separater Bericht in diesem Blog.

Als Gehäuse für den HC-SR04 dient eine Vorlage von Thingiverse, die mittels 3D Drucker „gedruckt“ wurde:

Aber auch andere Sensoren lassen sich auf ähnliche Art und Weise kostengünstig und schnell verbauen und mit SmartHomeNG verbinden.

Für weniger versierte Programmierer bietet sich hier ESPEasy an, das Thema eines anderen Artikels ist.

Im zweiten Teil zum Thema wird erläutert, wie man die Entfernungsdaten auf ein OLED Display bringt. Direkteinstieg unter Entfernungsmessung mit ESP32 und SmartHomeNG Teil 2: Anbindung eines SSD1306 OLED Displays.

(Die in diesem Artikel verwendeten Screenshots und Fotos wurden selber erstellt. Für den Schaltplan wurde das Tool Fritzing verwendet.)

 


4 Kommentare

Steve · 30. April 2021 um 10:54

Hi Marc René,

Danke für das gut beschriebene HowTo. Eine Frage habe ich noch. Kann man auch die Daten abrufen, wenn man die IP im Browser eingibt? Im Moment steht im Serial Fenser immer nur der Hinweis „Starting connection to server…“

Marc René Frieß · 8. April 2018 um 7:05

Danke, habs korrigiert. Ich war mir sicher, dass Amazon mir einen für 1 € gebracht hat.. aktuell finde ich aber nichts mehr.

onkelandy · 5. April 2018 um 18:32

Sehr schöne Anleitung! 2 Euro dürfte für den Ultraschallsensor aber ein realistischerer Preis sein.

Wer mit der kegelförmigen Abstrahlung nicht klar kommt bzw. z.B. entlang einer Wand messen möchte, müsste zu einem Infrarotsensor greifen, z.B. Sharp GP2Y0A21YK für ca. 12 Euro.

Abstandsmessung – Digitale Werkstatt Biebertal · 17. Februar 2021 um 14:29

[…] Entfernungsmessung auf Basis eines ESP32 und SmartHomeNG […]

Schreibe einen Kommentar

Avatar-Platzhalter

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert