Regenwassertank Füllstand messen mit Raspberry Pi; Version 2020

Aufgrund der vielen Besucher des Themas, hier neu zusammengefasst, das komplette Thema!

Wer die Entstehung und die Rückschläge sucht, findet die alten Beiträge im Archiv.

Hardware:

Raspberry Pi
DYP-ME007Y

 

Software:

Am Ende wurde es: python und mysql

Vorwort zum DYP-ME007Y: hier gibt es wohl verschiedene Versionen, die beste Erklärung dazu habe ich hier gefunden: JSN-SR04T-2.0.pdf

Ich verwende jetzt die Variante mit 120kOhm Widerstand.

Achtung: Ich hatte versucht, den Raspberry und die Platine des Sensors im Keller zu lassen und mit einem langen Kabel den eigentlichen Sensor angeschlossen. Die Signale brechen aber zu oft ab als dass das praktikabel wäre. Das bedeutet, die Platine muss mit raus zum Tank. Damit die Feuchtigkeit die Platine nicht vernichtet, bitte unbedingt gut einpacken, verkleben oder ähnliches!

Mein Tank ist ein liegender Zylinder, was bedeutet, dass 10 cm Unterschied in der Mitte im Wasserstand viel mehr Volumen hat als der selbe Unterschied ganz oben oder ganz unten.

Also mal auf die Suche gemacht nach einer verwendbar einfachen Formel.
Der zweite Treffer in der Online-Suche war

Methode 3 klingt ja schon gut. Erst mal zur Kontrolle und zum Verständnis ein Beispiel gerechnet, klappt.

Raspbian ISO auf die SD-Karte, anstarten.

sudo raspi-config

Passwort und Locale ändern nach Bedarf. SSH aktivieren und Serial Zugriff deaktivieren (das kommt im Hauptteil noch einmal vor).

Danach LAMP installieren, aber ohne den FTP Teil:

https://pchelp.ricmedia.com/setup-lamp-server-raspberry-pi-3-complete-diy-guide/3/

Ftp braucht niemand, sftp funktioniert und SSH haben wir ja schon.

mysql_secure_installation

CREATE USER ‚root2’@’localhost‘ IDENTIFIED BY ‚NewPassword‘; #Hier ein eigenes Passwort vergeben!

GRANT ALL PRIVILEGES ON * . * to ‚root2’@’localhost‘ WITH GRANT OPTION;

Datenbank regenwasser anlegen, Benutzer regenwasseruser, Rechte vergeben, Tabelle Volume mit3 Spalten: id als INT mit auto_increment, datetime mit Format datetime und volume als INT.

apt-get install python-mysqldb
Auch wichtig: apt-get install python-serial
Disable the login shell on the serial port in the interfacing options of „sudo raspi-config“, and reboot. For B+ that may be all that is required.
Endlich das Geheimnis um das 0X55 Kommando mit viel probieren gelöst. Und dann noch mehr probiert, denn nirgends ist dokumentiert, dass man noch einmal ein Kommando senden muss um die Platine wieder zu deaktivieren.
Dann einen Codeblock gebaut, der Werte die zu weit außer dem Mittelwert sind verwirft und noch einmal einen neuen Mittelwert bildet.
Dann ein Codeblock der die komplette Messreihe verwirft wenn das Ergebnis nicht plausiebel ist, also außerhalb der Größe des Tanks.
Dann ein Code der vom Abstand umrechnet auf die Füllmenge des Tanks. Mein Tank sieht wie eine liegende Tonne aus, also sind Füllstand und Volumen nicht linear.
Dann das Ergebnis in eine Datenbank schreiben. Endlich fertig.
Und hier der komplette Code:
</pre>

#!/usr/bin/python
# coding=utf8
import time
import math
import sys
import RPi.GPIO as GPIO

#################### select mode ######################
# mode 1 = analog (R27 empty)
# mode 2 = serial, running continuous (R27 = 47kOhm)
# mode 3 = serial with trigger from script (R27 = 120kOhm)
mode=3 

#################### Tank settings ####################
voll=70 # 70 cm von Sensor bis Wasseroberfläche = voll
leer=230 # 230 cm von Sensor bis Wasseroberfläche= leer
laenge=230 #230 cm Länge des Tanks
radius=90 #90 Radius Tank
korr= 0 # 20 wird zur Entfernung addiert
echo_time = 2 # delay between measurements in seconds ### make sure no echo is left
allowed_dev = 0.1 # filter out wrong measurements

#################### serial settings ####################
if (mode == 3) or (mode == 2):
	import serial
	#seriellen Port einstellen
	ser = serial.Serial("/dev/ttyAMA0", baudrate=9600) #, timeout=3.0
	ser = serial.Serial(
               port='/dev/ttyAMA0',
               baudrate = 9600,
               parity=serial.PARITY_NONE,
               stopbits=serial.STOPBITS_ONE,
               bytesize=serial.EIGHTBITS,
               timeout=1
           )
#################### analog settings ####################
elif (mode == 1):
	GPIO.setmode(GPIO.BOARD)
	trig=13
	echo=15
	GPIO.setup(echo,GPIO.IN)
	GPIO.setup(trig,GPIO.OUT)
else:
	print "Mode falsch."
	print >> sys.stderr, "Mode falsch."
	GPIO.cleanup()
	sys.exit(1)

###### main ###############
entfernung=0
gesamt=0
entf_array = {}
for i in range(0,10):
	if (mode == 1): ### analog
		GPIO.output(trig,True)
		time.sleep(0.00001)
		GPIO.output(trig,False)
		j = 0
		while GPIO.input(echo) == 0:
			pass
		start=time.time();
		while GPIO.input(echo) == 1:
			pass
		ende = time.time();
		entfernung=(((ende - start) * 34300) / 2)+korr
	elif (mode == 2): ### serial cont.
		ser.flushInput()
		#import pdb
		#pdb.set_trace()
		#ser.write(bytes([0x55]))
		#startbyte = ord(ser.read(1))
		startbyte = 1
		while startbyte != 255:
			read = ser.read(1)
			print read
			if len(read) == 0:
				continue
			else:
				startbyte = ord(read)
			hbyte = ord(ser.read(1))
			lbyte = ord(ser.read(1))
			sbyte = ord(ser.read(1))
		entfernung = (hbyte * 256 + lbyte)/10 + korr
		summe = startbyte + hbyte + lbyte
		summe = summe - (int(summe/256) * 256)
		if summe != sbyte:
			entfernung = 0
		print(startbyte, hbyte, lbyte, sbyte, entfernung)
	elif (mode == 3): ### serial standby
		ser.flushInput()
		ser.write(chr(0X55))
		#startbyte = ord(ser.read(1))
		#import pdb
		#pdb.set_trace()
		startbyte = None
		while startbyte != 255:
			read = ser.read(1)
			if len(read) == 0:
				ser.flushInput()
				continue
			else:
				startbyte = ord(read)
				hbyte = ord(ser.read(1))
				lbyte = ord(ser.read(1))
				sbyte = ord(ser.read(1))
				ser.flushInput()
			ser.write(chr(0X55))
		ser.write(chr(0X55))
		entfernung = (hbyte * 256 + lbyte)/10 + korr
		summe = startbyte + hbyte + lbyte
		summe = summe - (int(summe/256) * 256)
		if summe != sbyte:
			entfernung = 0
		#print (i)
		#print(startbyte, hbyte, lbyte, sbyte, entfernung)
	print entfernung
	entf_array[i] = entfernung
	gesamt=gesamt+entfernung
	time.sleep(echo_time)
mittelwert=gesamt/(i+1)

##### filter mismatching measurements
i = 0
gesamt = 0
for entf in entf_array:
	if entf_array[entf] < mittelwert*(1+allowed_dev) and entf_array[entf] > mittelwert*(1-allowed_dev):
		gesamt=gesamt+entf_array[entf]
		i=i+1
mittelwert=gesamt/(i)
##### discard if median is outside full or empty
if (mittelwert < voll or mittelwert > leer):
	print "Messung ausserhalb Bereich: " + str(mittelwert)
	print >> sys.stderr, "Messung ausserhalb Bereich: " + str(mittelwert)
	mittelwert = None
	volumen = None
	#if (mode == 1):
	#	GPIO.cleanup()
	#sys.exit(1)
else:
	##### convert distance to round tank volume
	print "Entfernung: ",  mittelwert, " cm"
	hoehe = (leer - mittelwert)
	acosval = (radius - hoehe)/float(radius) # float needed else python rounds to int
	alpha = 2*math.acos(acosval)
	volumen = int(round(laenge * radius*radius/2*(alpha -math.sin(alpha))))
	check = False
	if check:
		print "leer: ",  leer, " cm"
		print "radius: ",  radius, " cm"
		print "laenge: ",  laenge, " cm"
		print "hoehe: ",  hoehe, " cm"
		print "acosval: ",  acosval, ""
		print "alpha: ",  alpha, " rad"
		print "Volumen: ", volumen, " cm³"
	print "Volumen: ", volumen/1000, " Liter"
	print "- - - - - - - - - - - - - - - - - - - - - "
#f1=open('/var/scripts/regentank_check/entfernung.txt','w')
#print >> f1, '%d' % (mittelwert)
#f1.close()

#f2=open('/var/scripts/regentank_check/volumen.txt','w')
#print >> f2, '%d' %(volumen)
#f2.close()

#f1l=open('/var/scripts/regentank_check/entfernung.txt','r')
#f2l=open('/var/scripts/regentank_check/volumen.txt','r')

#serverftp = ftplib.FTP('ftp.server.de', 'user', 'pass')
#serverftp.storbinary('Stor entfernung.txt', f1l)
#serverftp.storbinary('Stor volumen.txt', f2l)
#serverftp.quit()

import MySQLdb
db = MySQLdb.connect(host="localhost",    # your host, usually localhost
                     user="regenwasseruser",         # your username
                     passwd="password",  # your password
                     db="regenwasser")        # name of the data base

# you must create a Cursor object. It will let
#  you execute all the queries you need
cur = db.cursor()

# Use all the SQL you like
if volumen:
	vol_string = str(int(round(volumen/1000,0)))
else:
	vol_string = "Null"
cur.execute("INSERT INTO volumen (`datetime`,`volume`) VALUES (now()," + vol_string + ")")

db.commit()
db.close()

#### clean up after using GPIO mode
if (mode == 1):
	GPIO.cleanup()
<pre>

Und zu guter letzt, noch meine Skizzen von der Verkabelung, für Modus 1, 2 und 3:

Modus 1, analog, kein Widerstand:

Modus 2, seriell Dauer, 47kOhm Widerstand:

Modus 3, seriell Standby, 120kOhm Widerstand:

 

Und noch eine Warnung: ich hatte einen Schlauch quer durch den Tank liegen von der Wasserpumpe. Der Sensor hat, als der Wassertand fiel, den Schlauch erfasst. Unbedingt darauf achten, dass die Richtung in die der Sensor misst auch wirklich frei ist.

Ich lasse das script jetzt einmal pro Tag per crontab laufen.

Hier der Code für eine Anzeige des Füllstands.

Wir lesen den letzten Wert aus der Datenbank aus.

 

 <?php #ini_set('display_errors', 1); #ini_set('display_startup_errors', 1); #error_reporting(E_ALL); $servername = "localhost"; $username = "regenwasseruser"; $password = "Passwort_Hier"; $dbname = "regenwasser"; $max_volume = 5853; // Create connection $conn = new mysqli($servername, $username, $password, $dbname); // Check connection if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

$sql = "SELECT volume, datetime FROM volumen order by id DESC limit 1";
$result = $conn->query($sql);
$row = $result->fetch_assoc();
$volume = $row["volume"];
$datetime = substr($row["datetime"],0,-3);
#$datetime = strtotime($row["datetime"]);
$datetime = date("d.m.Y H:i", strtotime($row["datetime"]));
$percent = round($volume / $max_volume * 100 ,2);
//if ($result->num_rows > 0) {
    // output data of each row
 //   while($row = $result->fetch_assoc()) {
 //       echo "id: " . $row["id"]. " - Name: " . $row["firstname"]. " " . $row["lastname"]. "
";
 //   }
//} else {
 //   echo "0 results";
//}
$conn->close();
?> 
<label for="fuel">Regenwassertank: </label>

<meter id="fuel" name="fuel"
       min="0" max="<?php echo $max_volume?>"
       low="2000" high="<?php echo $max_volume?>" optimum="<?php echo $max_volume?>"
       value="<?php print "$volume";?>">
    </meter>

    <?php print "$datetime <b>$percent</b>"?>%

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.