HSG

Aktuelle Seite: HSG/Fächer/Informatik/ MSR/Raspberry Pi

Raspberry Pi 3B als Wassermelder

Ausgangsproblem: Wie bekomme ich mit, dass in einem Keller, der keinen Abfluss hat, ein Wasserschaden vorliegt und der Keller evlt. voll Wasser läuft?

Bei einer kurzen Internetsuche fand ich eine schöne Seite: Raspberry Pi als Wassersensor benutzen – E-Mail als Benachrichtung. Dort wird anhand des älteren Modells Raspi 2 mit detaillierten Skizzen und Programmen gezeigt, wie man den Raspi dazu bringt, Wasser zu detektieren und mit relativ kurzem Python-Code eine e-mail zu versenden.

Bild Bild Bild
(Fotos: Matthias Sprau - Lizenz: CreativeCommons BY-NC-SA 3.0)

Und schon kommt die Warnmeldung auf's Telefon ...
Bild

Eine e-mail mit dem Raspi versenden

Tasächlich gelingt das mit Python mit relativ wenigen Zeilen. Jedoch gibt es kleine, entscheidende Details, die man berücksichtigen muss. Vorab: Das Senden von mails gelang mir nur mit gmail.com, bei den anderen Anbietern t-online.de und web.de ist es mir bisher nicht gelungen. Mit Hilfe der genannten Quellen habe ich folgendes Testprogramm melder_status.py für Python3 zusammengestellt:


# Autor: M. Sprau
# Datum: 25.10.2020
# Programm sendet die uptime-Zeit des Raspis.
# Eintrag in crontab kontrollieren.

import smtplib, shlex, subprocess

# uptime-Zeit des Raspis bestimmen
cmd = "uptime -p"
args = shlex.split(cmd)
p = subprocess.Popen(args, stdout=subprocess.PIPE)
output = p.communicate()

# Benutzerdaten z.B. bei gmail.com
user = 'BENUTZER'   # @gmail.com kann hier weggelassen werden
pwd = 'PASSWORT'

# Mail absenden
mail_text = str(output) + '\n ist der Raspi nun online!'
subject = 'Online-Status des Raspis'
MAIL_FROM = 'BENUTZER@gmail.com'
RCPT_TO = 'EMFFÄNGER@ADRESSE'
DATA = 'From:%s\nTo:%s\nSubject:%s\n\n%s' % (MAIL_FROM, RCPT_TO, subject, mail_text)
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
#print ("Logging in ...")
server.login(user, pwd)
#print ("Begin sending ...")
server.sendmail(MAIL_FROM, RCPT_TO, DATA)
#print ("Logging out ...")
server.quit()

Wie im Quelltext zu sehen ist, wird das Passwort im Klartext abgepeichert. Daher sollte man sich aus Sicherheitsgründen ein separates gmail-Konto nur für diesen Zweck anlegen. Folgende Schritte sind zunächst nötig:

Aktueller Hinweis zu App-Passwort

  • Auf gmail.com einen passenden Benutzer anlegen, ggf. muss man das mit einer Mobil-Nummer bestätigen.
  • In gmail anmelden und "Zugriff durch weniger sicher Apps" aktivieren. Das ist sehr wichtig, sonst wird keine mail versendet und es gibt eine Fehlermeldung. Dazu geht man im Konto auf Einstellungen > Sicherheit. Ein Bildschirmfoto der entscheidenen Stelle: gmail
  • Zum Testen bzw. bis die Einrichtung der mails funktioniert, kann man die auskommentierten "print"-Befehle
  • benutzen.
  • Wie im Bild zu sehen ist, ist der Raspi per LAN-Kabel mit einem Netzwerk verbunden, sodass Unsicherheiten im WLAN-Betrieb vermieden werden.

Obwohl es bei den anderen Anbietern auch geeignete SMTP-Server gibt, wie z.B. smtp.web.de:587, lieferten diese Versuche stets nur Fehlermeldungen zurück. Eine vergleichbare Option, im zugehörigen Online-Konto so etwas wie "unsichere Apps" zuzulassen, konnte ich nicht finden.

Nachdem nun die Benachrichtigung per e-mail funktioniert, möchte ich natürlich sicherstellen, dass der Raspi auch eingeschaltet ist, um einen potenziellen Wassereintritt zu melden. Dazu kann man sich per Eintragung im crontab reglemäßig eine mail zusenden lassen. Für meine Zwecke reicht es aus, zweimal wöchentlich eine Statusmail zu erhalten, die dann gleich die uptime-Zeit des Raspis mitsendet. Dazu kann Folgendes eingetragen werden, nachdem man im Terminal den Befehl crontab -e aufgerufen hat:

  
  # status senden: mittwochs, samstags 14.00 Uhr
  @reboot      python3 /home/pi/wasser_sensor.py
  0 14 * * 3,6 python3 /home/pi/melder_status.py

  #
  
Dabei ist wichtig, dass die letzte Zeile im crontab ein Kommentar oder eine Leerzeile ist. Mit der 2. Zeile wird beim Booten des Raspis das Pythonprogramm wasser_sensor.py gestartet, was im nächsten Abschnitt beschrieben wird.

Der Text einer Status-Mail von melder_status.py sieht z.B. so aus:

  (b'up 18 weeks, 1 day, 21 hours, 49 minutes\n', None)
  ist der Raspi nun online!
  

Wassersensor

Im Programm werden die GPIOs des Raspis benutzt, hier im Programm GPIO 18 und als zweiter Pin +3,3 V. Der Schutzwiderstand ist 100 Ohm, wobei auch Versuche mit 150 Ohm erfolgreich waren. Dazu werden zwei geeignete Steckkabel rausgeführt, wovon ein Kabel außen eine "Buchse" hat. Der Widerstand wird entsprechend abgelängt. Damit er nicht zu leicht rausrutschen kann, wird am eingesteckten Drahtstück etwas Lötzinn aufgebracht. Schließlich werden die beiden Stecker mit einer Heißklebepistole verklebt.
Beim Programmstart wird zunächst eine e-mail gesendet, damit man über den Beginn informiert wird. Im Hauptprogramm wird alle 10 s geprüft, ob der Sensor nass ist. Falls ja, wird dann alle 30 s erneut geprüft und eine entsprechende e-mail versendet. Das benutzte Programm kann hier runtergeladen werden: wasser_sensor.py.


#!/usr/bin/python3
# erweitert von M. Sprau
# 08.11.2020

# Nach einem Neustart wird zuerst eine mail gesendet.

import smtplib
from time import sleep

# warten nach Neustart, sonst kann die mail noch nicht abgesendet werden, weil die Dienste noch nicht verfügbar sind.
sleep(15)

# Benutzerdaten z.B. bei gmail.com
user = 'BENUTZER'   # @gmail.com kann hier weggelassen werden
pwd = 'PASSWORT'

subject = 'Neustart des Rapis'
mail_text ='''
Hallo,
der Raspi ist gerade neu gestartet. Wassersensor ist nun aktiv.
'''

MAIL_FROM = 'ABSENDER@ADRESSE'
RCPT_TO = 'ZIEL@ADRESSE'
DATA = 'From:%s\nTo:%s\nSubject:%s\n\n%s' % (MAIL_FROM, RCPT_TO, subject, mail_text)

server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
#print ("Logging in ...")
server.login(user, pwd)
#print ("Begin sending ...")
server.sendmail(MAIL_FROM, RCPT_TO, DATA)
#print ("Logging out ...")
server.quit()

#######################################
# Eigentliches Wasser-Sensor-Programm #
#######################################

#########
# About #
#########
# This script uses a Raspberry Pi to sense for the presense or absense of water.
# If there is water, an email is sent and a buzzer goes off.
# When it's dry again, another email is sent, and the buzzer turns off.

# To run this script at boot, edit /etc/rc.local to include (no quotes) 'sudo python .py'
# bzw.: crontab -e anpassen

###########
# License #
###########
# Released under the WTFPL.
#Full text and more information here: http://en.wikipedia.org/wiki/WTFPL

########################################
# Gmail login credentials to send email#
########################################
user = 'BENUTZER'   # @gmail.com kann hier weggelassen werden
pwd = 'PASSWORT'

############################
# General Email Parameters #
############################
MAIL_FROM = "ABSENDER@gmail.com"
RCPT_TO =  ['ZIEL@ADRESSE']  # max. nur 1 Adresse erlaubt, sonst Fehler im gmail-Konto!


#######################################
# Email Parameters when sensor is Wet #
#######################################
subject_wet = "Raspi Wasser-Sensor ist nass!"
body_wet = "Achtung,\n der Raspi hat einen Wasseraustritt im Keller gemeldet!\n Bitte kontrollieren!\n"

#######################################
# Email Parameters when semsor is Dry #
#######################################
subject_dry = "Raspi Wasser-Sensor ist trocken!"
body_dry = "Entwarnung, der Sensor ist trocken."

import RPi.GPIO as GPIO

# Function Definitions
#takes either "wet" or "dry" as the condition.
def email(condition):
    if condition == 'wet':
        DATA = 'From:%s\nTo:%s\nSubject:%s\n\n%s' % (MAIL_FROM, RCPT_TO, subject_wet, body_wet)

    if condition == 'dry':
        DATA = 'From:%s\nTo:%s\nSubject:%s\n\n%s' % (MAIL_FROM, RCPT_TO, subject_dry, body_dry)

    # The actual mail send
    server = smtplib.SMTP('smtp.gmail.com:587')
    server.starttls()
    server.login(user, pwd)
    server.sendmail(MAIL_FROM, RCPT_TO, DATA)
    server.quit()

# Tests whether water is present.
# returns 0 for dry
# returns 1 for wet
# tested to work on pin 18
def RCtime (RCpin):
    reading = 0
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(RCpin, GPIO.OUT)
    GPIO.output(RCpin, GPIO.LOW)
    sleep(1)
    GPIO.setup(RCpin, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)    # This takes about 1 millisecond per loop cycle
    while True:
        if (GPIO.input(RCpin) == GPIO.LOW):
            reading += 1
        if reading >= 1000:
            return 0
        if (GPIO.input(RCpin) != GPIO.LOW):
            return 1

# Main Loop
sleep(25) # damit mail aus melder_neustart.py fertig gesendet wurde.
print('Waiting for wetness...')
while True:
    sleep(10)
    if RCtime(18) == 1:
        print("Sensor is wet")
        email('wet')
        while True:
            sleep(30)
            if RCtime(18) == 1:
                print("Sensor is still wet...")
                email('wet')
                continue
            if RCtime(18) == 0:
                print("Sensor is dry again")
                email('dry')
                print("Waiting for wetness...")
                break

App-Passwort (ab Juni 2022 zwingend notwendig!)

Damit ein Raspberry Pi weiterhin mails senden darf, verlangt Google nun, dass man ein Passwort für eine Anwendung anlegt. Das zeigen die folgenden Bilder, mit denen man sich ein Passwort erzeugt und im Python-Code ablegt. Dabei wurde auch eine Bestätigung per SMS an eine Mobilfunk-Nummer für das Gmail-Konto gesendet, was sich problemlos bestätigen ließ. 1 2 3
Falls eine mail vom Raspberry Pi nicht abgesendet und blockiert wurde, muss man sich auf seinem Google-Account anmelden. Dort ggf. im Reiter Sicherheit kontrollieren und die App "Linux", so wurde der Anmeldeversuch meines Raspis genannt, entsprechend freigeben.

Links