In diesem Projekt erfährst du, wie du mit einem Arduino, einem Durchflussmesser und einem OLED-Display einen einfachen, aber effektiven Wasserzähler baust. Der Zähler misst die verbrauchte Wassermenge in Echtzeit und visualisiert sie direkt auf dem Display.
Diese Bauteile brauchst du
- 1× Arduino Nano oder Arduino UNO Im Aufbau wird ein Arduino Nano verwendet; UNO ist ebenfalls geeignet.
- 1× Turbinen-Durchflussmesser YF-S201C Wasser-Durchflusssensor mit Impulsausgang, 5 V.
- 1× 0,91 Zoll OLED-Display mit SSD1306 128 × 32 Pixel, I²C, Adresse üblicherweise 0x3C.
- 1× Steckbrett (Breadboard)
- 1× Jumper-Kabel Für die Verbindungen zwischen Arduino, Display und Sensor.
- 1× Messbecher Mit bekanntem Volumen, idealerweise 500 ml oder 1 Liter.
Affiliate-Links: Kaufst du über diese Links, erhält Pollux Labs eine kleine Provision – für dich ohne Mehrkosten.
Zwei Dinge vorab: Sensoren wie der YF-S201 sind Bastelbauteile – sie sind nicht für Trinkwasser zertifiziert und gehören nicht dauerhaft in deine Hausleitung eingebaut. Verwende sie für Versuchsaufbauten mit einem Schlauch, und halte deine Elektronik trocken bzw. weit genug vom Wasser entfernt.
Außerdem hat der Sensor einen Mindestdurchfluss von rund 1 Liter pro Minute. Ein dünnes Rinnsal oder einzelne Tropfen bringen das Turbinenrad nicht in Bewegung – für deine Messungen brauchst du also einen ordentlichen Wasserstrahl.
Aufbau des Wasserzählers
Der Aufbau ist kompakt und nutzt die I²C-Pins des OLED-Displays und deines Arduinos, um die Verkabelung zu vereinfachen. Der Durchflussmesser benötigt (neben 5V und Erde) nur eine Verbindung. Auf der folgenden Skizze verwende ich einen Arduino Nano – ein UNO funktioniert genauso, die Pins sind dieselben.
Hier noch einmal die Verbindungen im Detail:
Verbindung des Durchflussmessers:
- VCC: An den 5V-Pin des Arduinos
- GND: An einen GND-Pin des Arduinos
- Signal: An den digitalen Pin D2 des Arduinos. Dieser Pin ist für Interrupts geeignet, was eine präzise Messung der Impulse ermöglicht. Einen zusätzlichen Widerstand brauchst du nicht – der interne Pull-up des Arduinos übernimmt das im Sketch.
Verbindung des OLED-Displays:
- VCC: An den 3.3V-Pin des Arduinos.
- GND: An einen GND-Pin des Arduinos
- SDA (Daten): An den SDA-Pin des Arduino. Beim Nano und UNO ist das Pin A4.
- SCL (Takt): An den SCL-Pin des Arduinos. Beim Nano und UNO ist das Pin A5.
___STEADY_PAYWALL___
Die benötigten Bibliotheken
Für dieses Projekt benötigst du zwei Bibliotheken für das OLED-Display. Suche hierfür im Bibliotheksverwalter nach Adafruit SSD1306. Bei der Installation wirst du gefragt, ob du auch die Bibliothek Adafruit GFX Library installieren möchtest – bestätige das bitte mit Ja.
Für den Durchflussmesser benötigst du in der Regel keine Bibliothek – seine Impulse verarbeitest du selbst im Sketch.
Der Sketch für den Wasserzähler
Der folgende Code liest die Impulse des Durchflussmessers und zeigt die gemessene Wassermenge auf dem OLED-Display an. Im Folgenden gehe ich von einer Wassermenge von 2,25ml pro Impuls aus – dieser Wert leitet sich aus dem Datenblatt des YF-S201 ab, das rund 450 Impulse pro Liter angibt. Das ist allerdings nur ein Startwert: Wie genau dein Exemplar tatsächlich misst, findest du erst durch eine eigene Kalibrierung heraus. Ich empfehle dir daher, den Sensor nach dem ersten Test selbst zu kalibrieren.
//Wasserverbrauch messen mit einem Durchflussmesser
//polluxlabs.net
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Deklariere die Display-Parameter für ein 0,91'' Display
#define SCREEN_WIDTH 128 // OLED-Breite in Pixel
#define SCREEN_HEIGHT 32 // OLED-Höhe in Pixel
// Deklariere ein OLED-Display Objekt
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Pin für den Durchflussmesser
const int sensorPin = 2;
// Variable für die Impulszählung. `volatile` ist wichtig für Interrupts.
volatile unsigned long impulse_count = 0;
// Die kalibrierte Konstante für deinen Sensor (2.25 ml/impuls)
const float ML_PER_IMPULSE = 2.25;
void setup() {
// Startet die serielle Kommunikation zur Fehlersuche
Serial.begin(115200);
// Initialisiert das OLED-Display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306-Zuweisung fehlgeschlagen"));
for (;;); // Endlosschleife, wenn das Display nicht gefunden wird
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// Initialisiert den Sensor-Pin als Eingang mit internem Pull-up-Widerstand
pinMode(sensorPin, INPUT_PULLUP);
// Richtet einen Interrupt ein, der die Funktion count_impulses bei jedem Impuls aufruft
attachInterrupt(digitalPinToInterrupt(sensorPin), count_impulses, RISING);
}
void loop() {
// Eine Kopie des Zählers erstellen, während die Interrupts kurz pausieren
noInterrupts();
unsigned long current_impulses = impulse_count;
interrupts();
// Berechnung des Wasserverbrauchs
float water_volume_ml = current_impulses * ML_PER_IMPULSE;
// Löscht den Display-Inhalt
display.clearDisplay();
// Zeigt den Titel an
display.setTextSize(1);
display.setCursor(0, 0);
display.println("Wasserverbrauch:");
// Zeigt den Wert an – ab 1000 ml wechselt die Anzeige auf Liter
display.setTextSize(2);
display.setCursor(0, 16);
if (water_volume_ml < 1000) {
display.print(water_volume_ml, 0);
display.print(" ml");
} else {
display.print(water_volume_ml / 1000.0, 2);
display.print(" l");
}
// Aktualisiert das Display
display.display();
// Gibt die Impulse im Seriellen Monitor aus – praktisch für die Kalibrierung
Serial.print(current_impulses);
Serial.print(F(" Impulse = "));
Serial.print(water_volume_ml, 0);
Serial.println(F(" ml"));
// Kleine Pause, damit die Anzeige ruhig bleibt
delay(100);
}
// Interrupt-Service-Routine (ISR)
// Diese Funktion wird automatisch bei jedem Impuls aufgerufen
void count_impulses() {
impulse_count++;
}
Kalibrierung des Sensors
Der Wert in der Zeile const float ML_PER_IMPULSE = 2.25; ist entscheidend für die Genauigkeit deiner Messung. Je nachdem, welches Modell du verwendest oder auch, wenn dein Sensor nicht präzise misst, kann dieser Wert abweichen. Um die richtige Wassermenge pro Impuls zu ermitteln, gehe wie folgt vor:
- Vorbereitung: Halte einen Messbecher mit bekanntem Volumen (z. B. 500 ml) bereit. Lade den obenstehenden Sketch auf deinen Arduino.
- Nullstellung: Starte den Sketch neu, indem du den Reset-Knopf auf deinem Arduino drückst. Das Display sollte 0 ml anzeigen.
- Messung: Lasse genau 500 ml Wasser konstant schnell durch den Sensor in den Messbecher laufen.
- Wert ablesen: Warte, bis die Messung auf dem Display nicht mehr steigt. Notiere den angezeigten Wert (z.B. 650 ml). Alternativ kannst du auch den Seriellen Monitor öffnen (115200 Baud) – dort siehst du zusätzlich die reine Impulszahl.
- Neue Menge: Berechne die korrekte Menge Wasser pro Impuls: 2,25ml x (500ml / 650ml) = 1,73ml/Impuls
- Code anpassen: Ändere die Zeile im Sketch zu const float ML_PER_IMPULSE = 1.73;.
- Testen: Lade den Sketch erneut hoch und wiederhole den Test mit 500 ml Wasser. Der angezeigte Wert sollte nun sehr nah an 500 ml liegen.
Um das Ergebnis genauer zu machen, kannst du auch mehrere Messungen durchführen und einen Mittelwert finden. Es kann durchaus sein, dass z.B. die Fließgeschwindigkeit des Wassers Auswirkungen auf die Zahl der gemessenen Impulse hat. In diesem Tutorial lernst du, wie du Sensoren auf verschiedene Arten kalibrierst.
So funktioniert der Sketch
Lass uns schnell einen Blick auf die wichtigsten Teile des Sketchs werfen:
1. Die Setup-Funktion
Zunächst richtest du mit pinMode(sensorPin, INPUT_PULLUP); den Sensor-Pin ein. Das INPUT_PULLUP ist dabei kein Detail am Rande: Im Durchflussmesser steckt ein Hall-Sensor, der den Pin bei jedem Impuls nur auf Masse ziehen kann – von selbst auf 5V hochziehen kann er ihn nicht. Ohne den internen Pull-up-Widerstand des Arduinos würde der Pin unkontrolliert vor sich hin schweben und dein Zähler entweder gar nichts oder wilde Störimpulse zählen.
Der Befehl attachInterrupt() ist das Herzstück. Anstatt im Loop ständig zu prüfen, ob ein Impuls vom Sensor kommt, richtest du hier einen sogenannten Hardware-Interrupt ein. Das bedeutet, dass der Arduino-Chip sofort seine aktuelle Aufgabe unterbricht und direkt zu der kleinen Funktion count_impulses() springt, sobald er ein Signal am Pin D2 empfängt. Das macht die Messung extrem reaktionsschnell und präzise.
2. Der Loop: Die Hauptlogik
Dieser Teil wird immer wieder wiederholt.
- Impulse auslesen: Hier kopierst du den Wert des Zählers, bevor du mit ihm rechnest. Das Kopieren ist dabei in noInterrupts() und interrupts() eingerahmt – und das ist wichtiger, als es aussieht. Eine unsigned long-Variable ist vier Bytes groß, und der Arduino liest diese Bytes nacheinander einzeln aus. Feuert der Interrupt genau zwischen zwei dieser Schritte, bekommst du einen Wert, der halb alt und halb neu ist – also schlicht falsch. noInterrupts() schaltet die Interrupts für diese wenigen Mikrosekunden ab, interrupts() aktiviert sie sofort wieder. Impulse gehen dabei nicht verloren, sie werden nur minimal verzögert gezählt.
- Berechnung: float water_volume_ml = current_impulses * ML_PER_IMPULSE; ist der Kern des Projekts. Hier wird die Anzahl der Impulse mit dem Kalibrierungsfaktor multipliziert, um das tatsächliche Wasservolumen in Millilitern zu erhalten.
- Display-Update: Die folgenden Befehle, wie display.clearDisplay(), display.setCursor() und display.print(), kümmern sich darum, die berechneten Daten auf dem OLED-Display darzustellen. Der Befehl display.display() sendet am Ende die gesammelten Daten an das Display, um die Anzeige zu aktualisieren. Die if-Abfrage sorgt dafür, dass die Anzeige ab 1000 ml auf Liter umschaltet – sonst würde die Zahl irgendwann rechts aus dem schmalen Display herauslaufen.
- Pause: delay(100); sorgt für eine kurze Pause – ohne sie würde die Anzeige unruhig flackern. Deine Messung leidet darunter übrigens nicht: Der Interrupt zählt auch während des delay() zuverlässig weiter.
3. Die Funktion count_impulses(): Der Interrupt
Dies ist die kleine, aber sehr wichtige Funktion, die durch den Interrupt-Befehl ausgelöst wird. Sie wird nicht vom Loop aufgerufen, sondern springt automatisch in Aktion, sobald der Sensor einen Impuls sendet. Ihre einzige Aufgabe ist es, den Zähler impulse_count um eins zu erhöhen.
Wie geht es weiter?
Du kannst nun den Wasserverbrauch messen und auf einem Display anzeigen. Eine interessante Weiterentwicklung könnte z.B. ein LED-Ring sein, der anzeigt, wie sehr du dich beim Duschen oder beim Befüllen der Gießkanne einem festgelegten Maximalverbrauch näherst.
Auch am Sketch selbst kannst du weiterbauen: Ein Taster zum Nullstellen des Zählers ist schnell ergänzt. Und wenn du den Zählerstand regelmäßig ins EEPROM schreibst, überlebt er sogar einen Stromausfall.