Wassermelder am GPIO Port des RPi

Begonnen von Torxgewinde, 15 August 2016, 21:58:19

Vorheriges Thema - Nächstes Thema

Torxgewinde

Hallo,
Der Raspberry Pi hat keinen analogen Eingang (ADC), trotzdem kann man einen Wassersensor damit realisieren, sogar einen recht empfindlichen.

Inspiration: http://fritzing.org/projects/raspberry-pi-water-sensor/

Vorsicht: Das Schaltbild von Fritzing hat keine sinvollen Widerstände drin, die GPIO können das wohl eine Weile haben, aber da würde ich mich nicht drauf verlassen. Maximal sollte man den GPIOs nicht mehr als 16 mA pro Pin zumuten, in Summe über alle GPIO nicht mehr als 50 mA.

Meine Hardware:
- Als Sensor wird eine Streifenrasterplatine (vergoldet) so verlötet, dass immer abwechselnd eine Bahn Pol1, eine Bahn Pol2 ergibt.
- Am Raspberry wird ein GPIO ausgesucht, der keine festen Pullups oder Pulldowns hat. Pin7 ist zum Beispiel geeignet.
- Es wird zum Schutz des GPIOs ein kleiner Widerstand Rgpio zum GPIO eingebaut. Der Wert sollte mindestens 200 Ohm betragen (=maximaler Strom ist dann ~17 mA in den GPIO, selbst wenn 3,3 V anliegen). Ich habe 1k verbaut.
- Es wird ein RC Glied aufgebaut, als Kondensator habe ich 10 uF von Rgpio zu GND eingebaut, von 3,3 zu Rgpio habe ich 1M verbaut.
- Parallel zum 1M Widerstand kommt der Sensor.

https://forum.fhem.de/index.php?action=dlattach;topic=56701.0;attach=56547;image
https://forum.fhem.de/index.php?action=dlattach;topic=56701.0;attach=56549;image
https://forum.fhem.de/index.php?action=dlattach;topic=56701.0;attach=56595;image

Software:
Der GPIO wird als Ausgang geschaltet und es wird Low ausgegeben. Der Kondesator entlädt sich über den Rgpio-Widerstand bis auf 0V.
Jetzt wird der Eingang wieder also Eingang umgeschaltet und es wird in einer schnellen Schleife ein Counter hochgezählt, bis man High am GPIO messen kann.
Ist der Sensor trocken lädt sich der Kondensator über den 1M Widerstand langsam auf. Das dauert eine Weile. Mit dem GPIO misst man ob der Eingang (=der Knotenpunkt zwischen R und C) gerade als High oder Low gilt. Man misst mit dem RPi wie lange es dauert von Low nach High aufzuladen.
Ist der Kondensator naß, lädt sich der Kondesator viel schneller auf und der Loop-Counter bleibt bei kleineren Zahlen stehen.

Die Messung ist mit einem Python Skript realisiert. Eine kleine Besonderheit ist die Verwendung der internen Pulldown Widerstände zum Entladen im ersten Moment:

import RPi.GPIO as GPIO
import string
import time
import signal
import sys

import pycurl

# cleanup in STRG-C or SIGINT
def signal_handler(signal, frame):
GPIO.cleanup()
sys.exit(0)

# Measure loops-cycles it takes to charge a capacitor attached to GPIO
# RCpin is not a GPIO number, but a pin-number
def RCtime(RCpin, maxloops):
reading = 0
GPIO.setmode(GPIO.BOARD)

# discharge capacitor attached to pin, first by using the pulldown resistor, then by pulling it low
GPIO.setup(RCpin, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
time.sleep(1)
GPIO.setup(RCpin, GPIO.OUT)
GPIO.output(RCpin, GPIO.LOW)
time.sleep(1)

# switch to input, now charging starts
GPIO.setup(RCpin, GPIO.IN, pull_up_down = GPIO.PUD_OFF)
while True:
if (GPIO.input(RCpin) == GPIO.LOW):
reading += 1
if reading >= maxloops:
# print("takes too long or loop runs too fast")
return reading
if (GPIO.input(RCpin) != GPIO.LOW):
return reading

# send a HTTP Request
def sendToServer(url):
c = pycurl.Curl()
c.setopt(c.URL, url)
c.perform()

#print 'Watersensor running'
signal.signal(signal.SIGINT, signal_handler)

while True:
sensor = RCtime(7, 10000000)
print "Sensor reading: " + str(sensor)
sendToServer('http://admin:secret@localhost:8083/fhem?cmd=setreading+Wassersensor+raw+' + str(sensor))
time.sleep(10)

# run measurement once
#sensor = RCtime(7, 10000000)
#print str(sensor)


Dann sollte das Skript immer laufen. Damit es immer neu gestartet wird habe ich folgendes in die Datei /etc/rc.local eingetragen:
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

# starte Wassersensor
sudo -u pi /home/pi/watersensor.sh 2>&1 >/dev/null &

exit 0


Und den Python Code in einem Wrapper verpackt. Falls FHEM mal nicht läuft, müsste der Python Code sonst die Exception abfangen und Bash kann ich besser als Python:
#!/bin/bash

while true ; do
   python /home/pi/watersensor.py
   sleep 10
done


In Fhem habe ich dann einfach ein Dummy angelegt, dass den Namen "Wassersensor" hat:
define Wassersensor dummy
attr Wassersensor alias Wassersensor
attr Wassersensor devStateIcon Trocken:10px-kreis-gruen Wasser:10px-kreis-rot
attr Wassersensor event-on-change-reading state
attr Wassersensor group Wassersensor
attr Wassersensor icon humidity
attr Wassersensor userReadings percent {\
use Math::Round;;\
my $raw = ReadingsVal("Wassersensor","raw",-1);;;;\
my $max = 1550000;;;;\
my $min = 500;;;;\
100 - round((($raw-$min)/($max-$min))*100);;;;\
},\
state {\
if (ReadingsVal("Wassersensor","percent",-1) > 95) {"Wasser"} else {"Trocken"}\
}


Ich hatte für das Math::Round noch via apt-get ein fehlendes Paket installiert. Ich komme nur gerade nicht mehr auf den genauen Befehl...

Ich hoffe der ein oder andere kann diese Hinweise gebrauchen. Der Sensor ist so empfindlich, dass ein anhauchen schon leicht messbar ist.