- Deutsch (de)
- English (en)
Unsere Wasserversorgung hier in Thailand kommt aus dem Tiefbrunnen (120m) und wird in zwei Tanks (1000l + 2000l) gespeichert. Dazu gesellt sich ein 1000l Tank als Ausgleichsbehälter für den Pool. Die Füllstände aller Tanks werden über mechanische Schwimmschalter gesteuert, die die jeweiligen Pumpen bei Bedarf einschalten. Das funktioniert meist zuverlässig aber eine aktuelle Füllstandsanzeige wäre schon hilfreich. Vor allem wenn man sich schon mit Home Assistant einem Smart Home annähert. Ein Füllstandsmesser muss her.
Fertige Lösungen sind wie immer sehr teuer und ausserdem ist ja auch der Weg das Ziel. Eine mechanische Lösung habe ich von vorne herein ausgeschlossen um wartungsfrei zu bleiben, Auch wollte ich keine Lösung, wo ich die Tanks unterhalb der max. Wasserstandshöhe anbohren muss und dann die Abdichtung ein Problem werden kann. Da die Geometrie der Tanks bekannt ist, bietet sich eine Ultraschall-Abstandsmessung der Wasseroberfläche an. Selbst während des Pumpvorganges ist die Wasseroberfläche nicht so rau, das es zu deutlichen Fehlmessungen kommen wird. Beispiele für DIY Lösungen sind auch hier zu finden (teilweise nach meiner Implementierung in 2021 erschienen):
Da wir es hier ja mit Wasser zu tun haben, scheidet das verbreitete, preiswerte Ultraschallmodul HC-SR04 aus. Aber es gibt ein ähnliches, wasserfestes Modul AJ-SR04M.
Das AJ-SR04M Modul hat einige gute Eigenschaften:
Vom HC-SR04 Modul kennen wir die Pulseweitenmessung. Zur Bestimmung der Entfernung messen wir die Zeit vom Triggersignal bis zum eintreffenden Echosignal. Das AJ-SR04M Modul hat einen eigenen Microcontroller an Board und bietet diese Zeitmessung an, kann das Ergebnis aber seriell zurückgeben. Das entbindet uns von zeitkritischen Pulsmessungen. Um das Modul in den jeweiligen Betriebsmodus zu bringen muss der Widerstand R19 richtig bestückt werden:
Modus | R19 | Standby Current | Low Power Current |
---|---|---|---|
1. Compatible HC-SR04 trigger mode (default) | open | <2mA | - |
2. Low Power mode | 300kΩ | <2mA | <40µA |
3. Automatic serial port mode | 120kΩ | <2mA | - |
4. Low power Serial port mode | 47kΩ | <2mA | <20µA |
5. ASCII code output mode | 0kΩ | <2mA | <20µA |
Ich habe mich für den Mode 4 entschieden bei mit der UART serial ein Triggerbyte gesendet wird und anschliessend das Messergebnis seriell empfangen werden kann. Dazu brauchen wir einen 47KΩ Widerstand. Vorgesehen ist eine 0805 Bauform für R19 aber durch die großen Pads läßt sich auch leicht ein bedrahteter Widerstand anlöten.
Die Baudrate ist übrigens 9600 Baud und nach erfolgter Messung werden fünf Bytes mit folgendem Format übertragen:
// AJ_SR04M_Sensor serial format: // Trigger: 0x00 // Response: Byte1 Byte2 Byte3 Byte4 Byte5 // Start Byte=FF MSB LSB Checksum (LSB+MSB) 00
Das Ergebnis ist in Millimeter. Durch den seriellen Ansatz läßt sich der Sensor jetzt mit jedem PC (mit USB-Serial Adapter) oder einem Controller auslesen, der eine UART besitzt. Bisher messen wir nur den Abstand zur Wasseroberfläche. Mit etwas Mathematik können wir den Abstand in eine Literangabe oder einen Prozentwert umrechnen.
Dank an Omar Al-Janabi der die Berechnung schon detailliert beschrieben hat. Zur Berechnung brauchen wir die Tiefe des Tanks in Richtung der Messung. Den Abstand des Sensors vom höchst möglichen Wasserstand (min 20cm gemäß Sensorspezifikation), die Tankkapazität und natürlich den Messwert . Die Formel lautet dann für die Maßeinheit Zentimeter:
In meinem Fall wie weiter unten angegeben gelten folgende Daten für den Wassertank: (x in Meter)
Die Formel läßt sich mit den realen Werten vereinfachen aber wegen dem besseren Verständnis habe ich sie so wie sie ist implementiert.
Mit einem ESP8266 Modul und ESPHome gelingt die Integration in Home Assistant sehr einfach. Das serielle Protokoll läßt sich am einfachsten mit einem Custom Component auswerten. Das folgende kleine C Programm schreibt das Trigger Byte beim Update Request und berechnet das Messergebnis (in Meter) anhand der empfangenen fünf Bytes.
#include "esphome.h" class AJ_SR04M_Sensor : public PollingComponent, public UARTDevice, public Sensor { public: AJ_SR04M_Sensor(UARTComponent *parent) : PollingComponent(5000), UARTDevice(parent) {} void update() override { char frame[5]; int pos = 0; float value = 0.0; write(0x00); while (available()) { frame[pos] = read(); pos++; if(pos==5) { if ((frame[0] == 0xFF) && (frame[4] == 0x00) && ((frame[1]+frame[2])==frame[3])) { value = ((frame[1]<<8) + frame[2]) / 1000.0; publish_state(value); } break; } } } };
In ESPHome generieren wir ein neues Device und fügen den eigenen Code mit einem includes hinzu. Das File AJ_SR04M_Sensor.h
muss dazu in /config/esphome
kopiert werden.
esphome: name: water-level-serial comment: "Measure water level of water tanks by ultrasonic wave" includes: - AJ_SR04M_Sensor.h
Dann fügen wir die UART Schnittstelle hinzu. Falls wir keine Pins der Hardware UART verwenden wird automatisch eine Software UART implementiert. Da ich in meiner Lösung insgesamt 3 Module anschließe, gehen mir sowieso die H/W UARTs aus.
Zu guter Letzt die Implementierung der Sensoren. Der Sensor mit der ID dist1
gibt die gemessene Distanz in Meter her. Mit Template Sensoren lässt sich dann der Wert wie oben beschrieben in eine Prozent- und Literangabe umrechnen.
uart: - id: uart_dist1 tx_pin: GPIO14 rx_pin: GPIO12 baud_rate: 9600 sensor: - platform: custom lambda: |- auto my_sensor = new AJ_SR04M_Sensor(id(uart_dist1)); App.register_component(my_sensor); return {my_sensor}; sensors: unit_of_measurement: m accuracy_decimals: 3 name: "Water Tank Distance" id: dist1 - platform: template name: 'Water Tank Level' unit_of_measurement: '%' accuracy_decimals: 0 update_interval: 10s icon: mdi:water-percent lambda: |- return (((175-20)-((id(dist1).state*100)-20))/(175-20))*100; - platform: template name: 'Water Tank Volume' unit_of_measurement: 'l' icon: mdi:cup-water accuracy_decimals: 0 update_interval: 10s lambda: |- return (((175-20)-((id(dist1).state*100)-20))/(175-20))*1000;
Der Sensorkopf hat am Rand Klemmlaschen und ist so für eine Öffnung von ca. 20mm geeignet.
In dünnwandigen Tankgehäusen kann man mit einem passenden Leerrohrverbinder den Sensor stabil einbauen und gleichzeitig das Kabel mit einem Kabelschlauch ummanteln.
Hier die Montage oben auf unserem Dwell Tank. Der Sensor ist so gut gegen mechanischen Stress und Umwelteinflüsse geschützt. Bei uns hat er so jetzt schon mehr als 1 Jahr fehlerfrei überstanden.
In den meisten Fällen wird wahrscheinlich das 2.5m lange Sensorkabel ausreichen. Bei mir war die Entfernung zu zwei Tanks leider größer. Versuche das Sensorkabel zu verlängern zogen Messungenauigkeiten mit sich. Deshalb habe ich die AJ-Steuerplatine abgesetzt und in einem kleinen, wasserdichten Gehäuse verstaut. Das Bild zeigt nur das innere 3D-gedruckte Gehäuse.
Das ESPHome Modul ist in einem etwas größeren, wasserfesten Gehäuse untergebracht. Das Innenleben ist auf einem 3D-gedruckten Träger untergebracht. Für zwei Wassertanks kommt das serielle Signal über ein 4-adriges Kabel an die Schraubklemmen (der oben gezeigte Adapter ist ja dazwischen)) und der näher gelegene Wassertank wird direkt an das Messmodul angeschlossen. Als Netzteil verwende ich übrigens immer diese Apple-ähnlichen USB Netzteile. Der Eingang wurde von mir auf Schraubklemmen modifiziert. Diese Lösung ist recht zuverlässig, sehr preiswert und sicher da vollkommen gekapselt.
Nach getaner Arbeit kann man jetzt den Füllstand der Tanks beobachten und auch Aktionen daraus ableiten. Hier ein Blick auf das übersichtliche Panel von Home Assistant für den Aussenbereich. Dort werden die Füllstände der Tanks live angezeigt.
Natürlich kann man sich auch den Verlauf ansehen:
Der unterschiedliche Füllstand liegt am manuelle Auffüllen, da der mechanische Endschalter bei der Erstellung des Artikels defekt war.
Wenn ihr meine Arbeit unterstützen wollt, so könnt ihr mir gerne einen Cappuccino oder so spenden: .