Infrarot Lichtschranke mit Arduino und Raspberry Pi zum Auslesen des Wasserzählers

Martin Kompf

Wasserzähler mit Lichtschranke auf Basis des Arduino Pro Mini

Wasser ist ein kostbares Gut, die permanente Verfügbarkeit von frischem Trinkwasser ist eine Errungenschaft, deren Wert man nicht hoch genug schätzen kann. Das folgende Projekt befasst sich mit der laufenden Messung des Wasserverbrauchs in einem Haushalt, um Einsparungs- und Optimierungsmöglichkeiten zu finden. Ausgangspunkt ist dabei der bereits vorhandene mechanische Wasserzähler. Eine selbstgebaute Infrarot-Reflex-Lichtschranke dient zur Verbrauchserfassung, ein Arduino Pro Mini übernimmt dabei die elektronische Signalverarbeitung. Ein Raspberry Pi steuert das Messgerät an und hilft bei der Auswertung der Messdaten.

Der Wasserzähler

Musteraufbau innen
Abb. 1: Der Wasserzähler.

Der vorhandene Wasserzähler (Abb. 1) dient zur Abrechnung des Wasserverbrauch mit den Stadtwerken. Er arbeitet nach dem Prinzip eines Wasserrads. Das fließende Wasser versetzt ein Flügelrad in Drehung, das seinerseits eine aus mehreren Skalen und einem Zählwerk bestehende Mechanik antreibt. Damit ist prinzipiell die Erfassung bis auf 0,1 Liter Auflösung möglich. Die Abrechnung des Jahresverbrauchs erfolgt aber in m³, wobei 1 m³ = 1000 l gilt.

Interessant ist der Skalenzeiger, der sich einmal pro verbrauchtem Liter dreht (Abb. 1 rechts unten), denn auf ihm ist ein reflektierender Spiegel aufgeklebt. Mittels einer Reflex-Lichtschranke lassen sich die Umdrehungen des Spiegels erfassen und elektronisch weiterverarbeiten.

Arduino Lichtschranke

Die Lichtschranke besteht aus einer Infrarot-Leuchtdiode SFH 409 (alternativ: SFH 4346) und einem Infrarot-Fototransistor SFH 309 FA (Abb. 2). Die Ansteuerung erfolgt mit einem Arduino Pro Mini in der konkreten Ausprägung eines Wattuino Pro Mini 3.3V/8MHz von Watterott. Prinzipiell sind natürlich auch andere Modelle des Arduino geeignet, für den Einsatz des Wattuino sprechen insbesondere seine geringen Abmessungen und Kosten.

Der Wattuino Pro Mini hat keinen aufgelöteten USB-Seriell Wandler. In meinem konkreten Aufbau ist das kein Nachteil, da am Raspberry Pi, der mit dem Arduino kommuniziert, sowieso kein USB Anschluss mehr frei ist. Um die notwendige Software auf den Wattuino aufspielen zu können, benötigt man allerdings einen externen USB-Seriell Wandler, der während des Programmiervorgangs auf den Wattuino aufzustecken ist.

Schaltbild der Lichtschranke mit Anbindung an Arduino und RasPi
Abb. 2: Schaltbild der Infrarot-Reflex-Lichtschranke mit Anbindung an Arduino und RasPi

Gegenüber einem herkömmlichen Aufbau einer Lichtschranke mit analogen Triggerschaltkreisen oder gar diskreten Transistoren bietet die Verwendung eines Mikrocontrollers viele Vorteile. So sind neben den beiden Infrarot-Bauelementen nur zwei weitere passive Bauteile notwendig: Ein Widerstand von 120 Ω dient zur Begrenzung des Stroms durch die Leuchtdiode und der Widerstand 1,8 kΩ wandelt den durch den Fototransistor fließenden Strom in eine Spannung um. Sie gelangt an den Analogeingang A7 des Arduino.

Die Anode der Leuchtdiode ist nicht mit der Betriebsspannung 3V3, sondern mit dem Digitalausgang D2 verbunden. Dadurch lässt sich eine wirksame Unterdrückung von störender Umgebungsstrahlung erreichen. Das Messprogramm schaltet sehr schnell über D2 die Leuchtdiode an und aus und misst jeweils die an A7 entstehende Spannung. Die anschließende Differenzbildung eliminiert das auf den Sensor treffende Umgebungslicht, das Resultat enthält ausschließlich das von der Leuchtdiode erzeugte Licht. Dieses Messprinzip ließe sich ohne einen Mikrocontroller nur sehr aufwändig realisieren.

Die beiden weiteren Bauelemente LED grün (oder gelb oder rot) und Widerstand 560 Ω sind für die eigentliche Funktion der Lichtschranke nicht erforderlich. Sie dienen zur visuellen Rückmeldung der korrekten Funktion des Gerätes. Abgleichwiderstände oder Potentiometer sucht man in der Schaltung vergeblich. Die Anpassung der Lichtschranke an unterschiedliche Umgebungen übernimmt im fertig montierten Zustand die Software.

Die Kommunikation mit dem Raspberry Pi läuft über die serielle Schnittstelle mit den Signalen TXD und RXD, die über Kreuz verbunden sind. Da der Arduino Pro Mini mit einer Betriebsspannung von 3,3 Volt arbeitet, übernimmt der RasPi auch die Spannungsversorgung des Arduino und die Verschaltung von TXD und RXD kann ohne weitere Pegelanpassung erfolgen. Die beiden in Reihe geschalteten Widerstände 1,8 kΩ haben lediglich eine Schutzfunktion, falls man bei der Verdrahtung einen Fehler macht.

Aufbau

Musteraufbau innen
Abb. 3: Der Arduino Pro Mini und die wenigen externen Bauelemente kommen in kleines Plastikgehäuse.

Die Bauteile inklusive des Arduino sind auf einer Universal-Lochrasterplatte verlötet. Ihre Größe ist so gewählt, dass sie ohne weitere Befestigungselemente «saugend» in ein Plastikgehäuse STRAPUBOX 2043 SW (54 x 37 x 21 mm) passt (Abb. 3).

Infrarot-Leuchtdiode und -Fototransistor sind von der Unterseite der Platine bestückt, damit das Infrarotlicht durch zwei in die untere Gehäuseschale gebohrte Löcher nach außen gelangen kann. Die Oberseite der Bauelemente schließt dabei exakt mit der Gehäuseoberfläche ab (Abb. 4).

Die farbige Leuchtdiode ist normal von oben bestückt, sodass sie später auf der vom Zähler abgewandten Seite sichtbar ist. Ihre Länge ist so gewählt, dass sie mit der Gehäuseoberseite abschließt.

Die Verbindung mit dem Raspberry Pi erfordert ein vieradriges Kabel. Ich habe hierfür einfach vier Stück verschiedenfarbige Litze von je 2 m Länge mit der Bohrmaschine verdrillt. Das Kabel wird am RasPi direkt auf den GPIO Header gesteckt, auf der Leiterplatte der Lichtschranke ist dazu ein Pfostenstecker bestückt (Abb. 9).

Schnittzeichnung
Abb. 4: Schnittansicht der Reflex-Lichtschranke (schematisch)

Software

Der Arduino eignet sich zwar hervorragend zur Ansteuerung der Lichtschranke und Echtzeitdatenerfassung, hat jedoch seine Grenzen, wenn es an die Langzeitspeicherung und Visualisierung der Daten geht. Aus diesem Grund besteht die Software aus zwei Teilen. Ein Teil läuft auf dem Arduino und kümmert sich um die Datenerfassung. Der zweite Teil läuft auf einem Raspberry Pi. Da sich in meinem Keller schon ein RasPi zur Messung des Gasverbrauchs und zum Auslesen des Stromzählers befindet, habe ich gleich diesen verwendet. Da allerdings schon alle seine USB-Ports belegt sind, erfolgt die Verbindung mit dem Arduino nicht über USB, sondern über das beschriebene vieradrige Kabel zur Stromversorgung und seriellen Kommunikation.

Die komplette Software steht auf Github im Repository water-counter zur Verfügung. Eine Kopie gelangt per

git clone https://github.com/skaringa/water-counter.git

in das lokale Arbeitsverzeichnis.

Die Datei ReflectorLightBarrier.ino enthält den Arduino-Sketch. Er ist mittels der Arduino-IDE zu kompilieren und in den Programmspeicher des Arduino zu laden.

Einmalig ist die serielle Schnittstelle des Raspberry Pi zu konfigurieren. Dazu ruft man

sudo raspi-config

auf und wählt Advanced Options - Serial aus. Die Frage Would you like a login shell to be accessible over serial beantwortet man mit No, die nächste Frage Would you like the serial port hardware to be enabled? mit Yes. Nach einem Reboot steht die serielle Schnittstelle als Device /dev/serial0 zur Verfügung.

Nach dem Start ist die Arduino Software im Trigger data mode. Für einen ersten Funktionstest besser geeignet ist der Raw data mode, in dem die Software ununterbrochen die am Fototransistor gemessene Differenzspannung auf der seriellen Schnittstelle ausgibt. Die Umschaltung der verschiedenen Modi erfolgt durch das Senden von Kommandos vom RasPi zum Arduino. Dafür lässt sich zum Beispiel das Terminalprogramm minicom verwenden, das auf dem RasPi läuft:

minicom -b 9600 -o -D /dev/serial0

Das Senden des Zeichens C zum Arduino veranlasst diesen, in den Kommandomodus zu gehen. Nun reagiert er auf verschiedene Steuerbefehle:

D - Raw data mode: Die Software sendet fortlaufend Messwerte.
T - Trigger data mode: Nur bei Umschaltung des Triggers wird eine 1 oder 0 gesendet.
S low high - Setzen der Triggerpegel. Parameter sind zwei Integer-Werte von 0 bis 1023.
C - Aus den beiden Datenmodi gelangt man stets mittels C wieder in den Kommandomodus.

Im Raw data mode gibt der Arduino ständig Messwerte auf der seriellen Schnittstelle aus. Dabei handelt es sich um den vom Umgebungslicht bereinigten und gefilterten Differenzwert. So kann man testen, ob beim Aufbau der Hardware alles glatt gegangen ist: Hält man ein Stück Papier vor die Reflex-Lichtschranke, dann sollte die Veränderung seines Abstands signifikante Unterschiede in den Messwerten bewirken.

Mit der Tastenkombination Control-A gelangt man in das Menü von minicom, hier stehen dann diverse nützliche Befehle, wie zum Beispiel x zum Beenden des Terminalprogramms zur Verfügung.

Der für die Messwerterfassung relevante Arduino-Programmausschnitt ist:

    // perform measurement
    // turn IR LED off
    digitalWrite(irOutPin, LOW);
    // wait 10 milliseconds
    delay(10);
    // read the analog value:
    sensorValueOff = analogRead(analogInPin);
    // turn IR LED on
    digitalWrite(irOutPin, HIGH);
    delay(10);
    // read the analog value:
    sensorValueOn = analogRead(analogInPin);
    sensorValue = sensorValueOn - sensorValueOff;
    filteredValue = lowpass(sensorValue);

    switch (dataOutput) {
      case 'R':
        // print the raw data to the serial monitor
        Serial.println(filteredValue);
        break;
      case 'T':
        // detect and output trigger
        detectTrigger(filteredValue);
        break;
    }

Montage und Abgleich

Wasserzähler mit Lichtschranke auf Basis des Arduino Pro Mini
Abb. 5: Montage der Lichtschranke auf dem Wasserzähler

Das Gehäuse ist mit einem Streifen doppelseitigen Klebebands (tesa Powerstrips «spurlos wieder ablösbar») auf dem Gehäuse des Wasserzählers befestigt (Abb. 5). Dabei erfolgt die Positionierung der Lichtschranke mittig über dem Skalenzeiger mit dem Spiegel. Zuerst habe ich den Fehler begangen, die Lichtschranke unmittelbar auf die Oberfläche des Zählers aufzukleben. Dann ist der Abstand zwischen Lichtschranke und Spiegel jedoch zu gering. Da der Abstrahlwinkel der LED nur ±20° beträgt und der Spiegel stark gerichtet und kaum diffus reflektiert, gelangt bei zu geringem Abstand kein reflektiertes Licht zurück auf den parallel angebrachten Fototransistor. Optimal ist bei meinem Zähler ein Abstand von ca. 7 mm zwischen Zähleroberfläche und Lichtschranke. Als Abstandshalter habe ich ein Stück Moosgummi auf die Unterseite des Gehäuses geklebt (siehe Abb. 4).

Messwerte mit Triggerschwelle
Abb. 6: Die gemessenen Sensorwerte (blau) beim mehrmaligen Durchlauf des verspiegelten Zeigers und die daraus bestimmten Triggerschwellen.

Im montierten Zustand sollte nun die Bestimmung der Triggerschwellen erfolgen. Dazu erfasst man bei laufendem Wasserzähler die Messdaten im Raw data mode. Das bereits erwähnte Terminalprogramm minicom ist in der Lage, die über die serielle Schnittstelle laufenden Daten in eine Textdatei zu schreiben (per Control-A L). Mit dem Tabellenkalkulationsprogramm LibreOffice Calc habe ich daraus die Grafik Abb. 6 erzeugt, die bei der Ermittlung der Triggerschwellen hilft: Im zeitlichen Verlauf der Messdaten (blau) sind eindeutig die Durchläufe des Spiegel zu erkennen, da das von ihm reflektierte Licht zu einer deutlichen Zunahme der am Fototransistor gemessenen Spannung führt. Hieraus ergab sich dann für meinen Aufbau die Festlegung der beiden Triggerschwellen auf 70 (low, rot in Abb. 6) und 90 (high, gelb).

Das Kommando S 70 90 schreibt die Werte für die Triggerschwellen in den EEPROM des Arduino. Sie überleben damit das Abtrennen der Stromversorgung und das Aufspielen eines neuen Sketches.

Von nun an betreibt man die Arduino-Software im Trigger data mode. Hier erfolgt eine Ausgabe auf der seriellen Schnittstelle nur noch bei einem Triggerereignis: Bei Unterschreitung des low Triggerlevels gibt das Programm das Zeichen 0 und bei Überschreitung des high Levels eine 1 aus. Außerdem wird die Leuchtdiode entsprechend geschaltet, was für eine visuelle Kontrolle der Funktion hilfreich ist:

/**
 * Detect and print a trigger event
 */
void detectTrigger(float val) {
  boolean nextState = triggerState;
  if (val > triggerLevelHigh) {
    nextState = true;
  } else if (val < triggerLevelLow) {
    nextState = false;
  }
  if (nextState != triggerState) {
    triggerState = nextState;
    Serial.println(triggerState? 1 : 0);
    // control LED
    digitalWrite(ledOutPin, triggerState);
  }
}

Aufzeichnung von Zählerstand und Verbrauch

Auf dem Raspberry Pi läuft das Python-Skript wairc.py, das über die serielle Schnittstelle /dev/ttyAMA0 die vom Arduino gelieferten Daten empfängt. Im Trigger data mode sind das nur Nullen und Einsen, deren Umschaltung den Durchlauf der Zählmarkierung signalisiert. Da das Verhältnis von Umdrehungen der Zählscheibe zum Wasserverbrauch bekannt ist, kann man somit direkt auf die verbrauchte Wassermenge schließen:

# Serial port of arduino
# port = '/dev/ttyAMA0'
port = '/dev/serial0'

# counter unit: 1 revolution = 1 l = 0.001 m^3
trigger_step = 0.001

# ...

  # Open serial line
  ser = serial.Serial(port, 9600)

  trigger_state = 0
  counter = last_rrd_count()

  while(1==1):
    # Read line from arduino and convert to trigger value
    line = ser.readline()
    line = line.strip()

    old_state = trigger_state
    if line == '1':
      trigger_state = 1
    elif line == '0':
      trigger_state = 0
    if old_state == 1 and trigger_state == 0:
      # trigger active -> update count rrd
      counter += trigger_step
      update = "N:%.3f:%.3f" % (counter, trigger_step)
      rrdtool.update(count_rrd, update)

Das Programm schreibt bei einer Auslösung des Triggers den neuen Zählerstand und die seit dem letzten Trigger verbrauchte Wassermenge trigger_step = 1 Liter = 0.001 m³ in eine Round Robin Datenbank. Dieses Prinzip gleicht dem in Gaszähler auslesen mit Magnetometer HMC5883 und Raspberry Pi beschriebenen Vorgehen.

Das Programm übernimmt auch das Anlegen der Datenbank water.rrd, die im gleichen Verzeichnis wie das Python-Skript liegt. Hierzu muss man es einmalig mit der Option -c starten:

./wairc.py -c

Die Definition der Datenbank sieht folgendermaßen aus (siehe hierzu auch rrdcreate):

rrdtool.create(count_rrd,
  '--no-overwrite',
  '--step', '60',
  'DS:counter:GAUGE:86400:0:100000',
  'DS:consum:ABSOLUTE:86400:0:1',
  'RRA:LAST:0.5:1:4320',
  'RRA:AVERAGE:0.5:1:4320',
  'RRA:LAST:0.5:1440:30',
  'RRA:AVERAGE:0.5:1440:30',
  'RRA:LAST:0.5:10080:520',
  'RRA:AVERAGE:0.5:10080:520')

Die Datenbank enthält zwei Datenquellen (DS): counter für den aktuellen Zählerstand und consum für den Verbrauch pro Zeiteinheit. Als grundlegendes Datenerfassungsintervall sind 60 Sekunden (eine Minute) festgelegt. Allerdings hebt man diese Werte nicht für alle Ewigkeiten auf, sondern nur für drei Tage. Das erledigt die Definition der ersten beiden Round Robin Archives (RRA), die eine Anzahl von 4320 Werten (entspricht 3 Tagen * 24 Stunden * 60 Minuten) aufnehmen.

Die nächsten beiden RRAs konsolidieren jeweils 1440 Werte - das ist genau ein Tag - zu einem neuen Wert, der dann 30 Tage aufgehoben wird. Die letzten beiden RRAs übernehmen die Berechnung und Speicherung von Wochenwerten (7 * 24 * 60 = 10080) über knapp 10 Jahre (520 Wochen).

Sie können durch Veränderung der Parameter andere Festlegungen für Ihre Datenhaltung treffen. Das muss allerdings vor dem Anlegen der Datenbank passieren, spätere Änderungen sind nicht mehr möglich!

Eine neu angelegte Datenbank startet logischerweise mit dem Zählerstand 0. Um ihn mit dem Wasserzähler zu synchronisieren, notiert man sich seinen Zählerstand (zum Beispiel 175.448) und schreibt ihn möglichst zeitnah in die Datenbank:

rrdtool update water.rrd N:175.448:0

Danach ist wairc.py neu zu starten. Es liest dabei den gespeicherten Zählerwert aus der Datenbank und setzt damit die Zählung fort. Für den Dauerbetrieb sollte wairc.py als Hintergrunddienst installiert sein, damit es Ausloggen und Systemneustarts überlebt. Möglichkeiten dafür zeigt der Blog Dienst im Hintergrund.

Das Programm rrdtool erzeugt aus der Round Robin Datenbank ansprechende Grafiken. Die in Abb. 7 gezeigten Grafiken des Zählerstands und Verbrauchs der letzten drei Tage erzeugt man zum Beispiel per:

# Erzeuge Zählerstandsgrafik in counter.gif
rrdtool graph counter.gif \
  -s 'now -3 day' -e 'now' \
  -Y -A \
  DEF:counter=water.rrd:counter:LAST \
  LINE2:counter#000000:"Zählerstand [m³]"
# Erzeuge Verbrauchsgrafik in consum.gif
rrdtool graph consum.gif \
  -s 'now -3 day' -e 'now' \
  DEF:consum=water.rrd:consum:AVERAGE \
  CDEF:consumltr=consum,60000,* \
  LINE2:consumltr#00FF00:"Verbrauch [l/min]"
Zählerstand Verbrauch
Abb. 7: Mit rrdtool erzeugte Grafiken von Zählerstand und Wasserverbrauch über drei Tage

Verlängert man den Zeitraum auf eine Woche, dann kann man keine minutengenaue Grafik mehr erzeugen, sondern muss sich auf Tagesdurchschnittswerte beschränken. Das liegt am beschriebenen Konsolidierungsverhalten der Round Robin Datenbank, die eine erste Datenverdichtung nach drei Tagen vornimmt:

rrdtool graph consumpd.gif \
  -s 'now -1 week' -e 'now' \
  DEF:consum=water.rrd:consum:AVERAGE \
  CDEF:consumpd=consum,3600000,*,24,* \
  LINE2:consumpd#0000FF:"Verbrauch [l/d]"
Verbrauch über eine Woche
Abb. 8: Wasserverbrauch über eine Woche - die Werte sind zum Tagesdurchschnitt konsolidiert

Ein kleiner Webserver wie lighttpd «lighty» zusammen mit ein paar HTML-Seiten und Perl-Skripts stellt die Verbrauchsgrafiken komfortabel im Intranet zur Verfügung. Die Skripte befinden sich im Unterverzeichnis www, das Prinzip habe ich in Erfassung des Gasverbrauchs und Temperaturmessung beschrieben.

Leckerkennung

Die aufgezeichneten Zählerstände lassen sich zur Erkennung von Lecks in den Frischwasserleitungen verwenden. Solche Lecks können sehr hohe Folgekosten nach sich ziehen, wenn zum Beispiel Wasser ständig aus einer undichten Leitung in eine Wand fließt. Aber auch eine vor sich hin tröpfelnde Toilettenspülung ist ärgerlich und verursacht Kosten.

Die Idee zur Leckerkennung enstammt dem Projekt eWatermeteralert auf elektorlabs. Das Prinzip beruht auf der Annahme, dass es in einem privaten Haushalt pro Tag mindestens eine Ruhephase von zwei oder drei Stunden geben sollte, während der keinerlei Wasserverbrauch stattfindet. Falls man eine solche Pause nicht findet, liegt die Vermutung nahe, dass permanent Wasser durch ein Leck abfließt (oder man wohnt in einer WG mit Schichtarbeitern).

Die Umsetzung des Algorithmus ist im Skript leakdetect.py implementiert. Zunächst liest es die Zählerwerte der vergangenen 24 Stunden aus der Round Robin Datenbank:

((start, stop, step), head, data) = rrd.fetch(count_rrd, 'LAST', '-s', 'now - 1 day', '-e', 'now')

In einer Schleife ermittelt man dann alle zusammenhängenden Zeiträume, in denen sich der Zählerstand nicht ändert. Falls die Länge des Zeitraums den Schwellwert min_pause überschreitet, gelangt er in die Pausenliste pauses:

  if duration > min_pause:
    pauses.append({'start' : start_time, 'duration' : duration})

Am Schluss überprüft das Programm, ob die Pausenliste leer ist. Falls ja, dann erfolgt die Ausgabe einer Warnmeldung:

  if len(pauses) == 0:
    print("Possible leak detected! There is no break of at least {0} hours."
      .format(min_pause/3600.0))

Das Skript ist in der crontab hinterlegt, damit es einmal pro Tag läuft und seine Ausgabe per E-Mail verschickt:

30 7 * * * $HOME/water-counter/leakdetect.py | mail -E -s 'Leak detection' $LOGNAME

Fazit

Die Lösung ähnelt sehr der Infrarot Lichtschranke mit Arduino zum Auslesen des Stromzählers. Im Prinzip können Sie die dort beschriebene Hardware auf Basis des Arduino Nano auch ohne Änderungen für den Wasserzähler verwenden. Ich habe mich nur aufgrund der Tatsache, dass alle USB Port an meinem Raspberry Pi belegt waren, für die vorliegende Variante mit dem Arduino Pro Mini entschieden. Auch softwareseitig sind die beiden Lösungen nahezu identisch - sie unterscheiden nur sich in den verwendeten Maßeinheiten (m³ statt kWh) und den Zählerkonstanten (Verbrauch pro Zählimpuls).

Die Lichtschranke am Stromzähler verrichtet jetzt seit über einem Jahr fehlerfrei ihren Dienst. Es war keine Nachkalibrierung des Triggers erforderlich und die Differenz der Zählerstände ist Null. Das bestätigt das zugrundeliegende Konzept und lässt hoffen, dass die vor kurzem in Betrieb genommene Wasserverbrauchsmessung genauso akkurat funktioniert. Welche Schlüsse man aus den gewonnenen Erkenntnissen über den persönlichen, minutiös erfassten Wasserverbrauch zieht, bleibt natürlich jedem selbst überlassen. Sie sollten nur sehr genau abwägen, wem Sie diese Messwerte in Echtzeit über längere Zeit zur Verfügung stellen, da sich aus ihnen sensitive Informationen, zum Beispiel über Abwesenheitszeiten, gewinnen lassen.

RasPi und Arduino
Abb. 9: Arduino-Lichtschranke und Raspberry Pi. Das vieradrige verdrillte Kabel übernimmt den Datentransfer und die Stromversorgung des Arduino.