Hallo an alle Interessierte,
ich habe "Meine Lösung" gefunden, gebaut und sie funktioniert.
Zuerst habe ich mir einen neuen RasPi B+ mit Zubehör (SD-Karte, Netzteil, etc.) gekauft. Einen WLAN Stick von Hama hatte ich noch Zuhause rumliegen. Über Amazon habe ich mir dieses DYP-ME007Y [Anzeige] Ultraschall Sensormodul gekauft.
Nach dieser Anleitung: http://www.gtkdb.de/index_36_2272.html habe ich den Ultraschallsensor an den RasPi angeschlossen. In der Anleitung wird ein anderer Ultraschallsensor (HC-SR04 Ultraschallmodul) verwendet, der DYP-ME007Y ist jedoch Anschlussgleich, hat aber einige Vorteile, z.B. Waterproof Design.
Den Ultraschallsensor habe ich in einen Deckel des Tanks eingebaut. Das lange Anschlusskabel reicht durch die Wand bis in den Nachbarkeller wo an einer Stromsteckdose der Raspberry Pi (Name: HeizPi) mit dem WLAN-Stick angeschlossen ist.
Bei Verbindungsunterbrüchen im WLAN verbindet der Stick mit diesem Skript als Cronjob immer wieder neu. Somit kann der HeizPi nicht ins Nirwana abgleiten.
Das Python-Skript (ultrasonic.py) zum messen des Abstandes vom Deckel bis auf die Öloberfläche habe ich mit geringen Änderungen (Mittelwert aus 20 Messungen, keine Endlos Schleife) übernommen.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# import required modules
import time
import datetime
import RPi.GPIO as GPIO
# define GPIO pins
GPIOTrigger = 18
GPIOEcho = 17
# function to measure the distance
def MeasureDistance():
# set trigger to high
time.sleep(0.2)
GPIO.output(GPIOTrigger, True)
# set trigger after 10µs to low
time.sleep(0.0001)
GPIO.output(GPIOTrigger, False)
# store initial start time
StartTime = time.time()
# store start time
while GPIO.input(GPIOEcho) == 0:
StartTime = time.time()
# store stop time
while GPIO.input(GPIOEcho) == 1:
StopTime = time.time()
# calculate distance
TimeElapsed = StopTime - StartTime
Distance = (TimeElapsed * 34400) / 2
return Distance
# main function
def main():
try:
# while True:
Distance0 = MeasureDistance()
Distance01 = MeasureDistance()
Distance02 = MeasureDistance()
Distance03 = MeasureDistance()
Distance04 = MeasureDistance()
Distance05 = MeasureDistance()
Distance06 = MeasureDistance()
Distance07 = MeasureDistance()
Distance08 = MeasureDistance()
Distance09 = MeasureDistance()
Distance10 = MeasureDistance()
Distance11 = MeasureDistance()
Distance12 = MeasureDistance()
Distance13 = MeasureDistance()
Distance14 = MeasureDistance()
Distance15 = MeasureDistance()
Distance16 = MeasureDistance()
Distance17 = MeasureDistance()
Distance18 = MeasureDistance()
Distance19 = MeasureDistance()
Distance20 = MeasureDistance()
Distance_sum = Distance01 + Distance02 + Distance03 + Distance04 + Distance05 + Distance06 + Distance07 + Distance08 + Distance09 + Distance10 + Distance11 + Distance12 + Distance13 + Distance14 + Distance15 + Distance16 + Distance17 + Distance18 + Distance19 + Distance20
Distance = round(Distance_sum / 20,1)
# Meine Tanks haben Maximal 4.500 Liter bei 150 cm Füllhöhe
# Zusätzlich 4 cm Offset vom Einbauort des Sensors
Fuelstand = 154 - Distance
Liter = 4500 / 150 * Fuelstand
Zeit = time.time()
ZeitStempel = datetime.datetime.fromtimestamp(Zeit).strftime('%Y-%m-%d_%H:%M:%S')
print (ZeitStempel),("Entfernung: %.1f cm" % Distance),(" Fuelhoehe: %.1f cm" % Fuelstand),(" Liter: %.0f l" % Liter)
time.sleep(2)
# reset GPIO settings if user pressed Ctrl+C
except KeyboardInterrupt:
print("Measurement stopped by user")
GPIO.cleanup()
if __name__ == '__main__':
# use GPIO pin numbering convention
GPIO.setmode(GPIO.BCM)
# set up GPIO pins
GPIO.setup(GPIOTrigger, GPIO.OUT)
GPIO.setup(GPIOEcho, GPIO.IN)
# set trigger to false
GPIO.output(GPIOTrigger, False)
# call main function
main()
Alles anzeigen
Ein Cronjob führt das Skript täglich viermal durch und hängt die Messung sowie den daraus errechneten Literwert an eine bestehende Logdatei an.
Beispiel Logdatei: Heizoel.log
2014-11-01_18:10:06 Entfernung: 68.6 cm Fuelhoehe: 85.4 cm Liter: 2562 l
2014-11-02_00:10:05 Entfernung: 68.8 cm Fuelhoehe: 85.2 cm Liter: 2556 l
2014-11-02_06:10:06 Entfernung: 68.8 cm Fuelhoehe: 85.2 cm Liter: 2556 l
Beispiel Cronjob: (mit: sudo crontab -e)
Die Logdatei wird von einem anderen Cronjob mit SFTP kurz nach der Messung von dem RasPi im Keller über WLAN an meinen zweiten RasPi im Büro gesandt und dort gespeichert.
Beispiel der /home/HeizPi/LogAnPiSenden.tcl:
#!/usr/bin/expect --
# Script: LogAnPiSenden.tcl
# Description: Utility to SFTP a file to a specified host
# usage: LogAnPiSenden.tcl target_server_port target_server user pass target_directory source_directory file_name_to_ftp rename_file
# Set named parameters
set ssh_port "22"
set sftp_server "192.168.0.20"
set sftp_user "PiBueroUser"
set sftp_pass "MeinPasswordAufPiBuero"
set target_directory "/opt/fhem/log/"
set source_directory "/home/HeizPi/"
set file_name "Heizoel.log"
set rename_file "N"
set sftp_prompt "sftp>?"
set timeout 300
# Default variable(rename_file) to "N" if not supplied by caller.
if { $rename_file eq "" } {
set rename_file N
}
# Procedure to connect to SFTP server
proc connect { sftp_pass } {
variable sftp_prompt
variable sftp_server
expect -re "Connecting to.*" { exp_continue } \
-re "Are you sure you want to continue.*" { exp_send "YES\r"; exp_continue } \
-re "(Password|password).*:.*" { exp_send "$sftp_pass\r"; exp_continue } \
-re $sftp_prompt { return 0 } \
eof { puts "***Error connecting to($sftp_server)."; return 1 }
timeout { puts "***Error connecting to($sftp_server)."; return 1 }
}
# Procedure to send meesage denoting error, then quit SFTP, then exit with status denoting error.
proc abort { msg } {
puts "$msg";
exp_send "quit\r";
exit 1;
}
# Connect to the SFTP server
spawn sftp -oPort=$ssh_port $sftp_user@$sftp_server
set connect_results [connect $sftp_pass]
# If successful connection, continue, else exit denoting error occured.
if { $connect_results == 0 } {
# Change to target directory on target server.
exp_send "cd $target_directory\r"
expect "No such file or directory" { abort "\n**Error changing to directory($target_directory) on target server." } \
"Failure" { abort "\n**Error changing to directory($target_directory) on target server." } \
-re $sftp_prompt {} \
timeout { abort "\n**Error changing to directory($target_directory) on target server." }
# Change local directory.
exp_send "lcd $source_directory\r"
expect "No such file or directory" { abort "\n**Error changing to directory($source_directory) on local machine." } \
"Failure" { abort "\n**Error changing to directory($source_directory) on local machine." } \
-re $sftp_prompt {} \
timeout { abort "\n**Error changing to directory($source_directory) on local machine." }
# If rename_file = "Y" transfer file with '.xferring' as extension.
if { $rename_file == "Y" } {
set file_name_to_xfer $file_name
append file_name_to_xfer .xferring
} else {
set file_name_to_xfer $file_name
}
# Put file to target host.
set timeout 1800
exp_send "put $file_name $file_name_to_xfer\r"
expect "not found." { abort "\n***Error transfering file($file_name) to: $sftp_server)." } \
"No such file or directory" { abort "\n***Error transfering file($file_name) to: $sftp_server)." } \
"Failure" { abort "\n***Error transfering file($file_name) to: $sftp_server)." } \
-re $sftp_prompt {} \
timeout { abort "\n***Error transfering file($file_name) to: $sftp_server)." }
# If rename_file = "Y" remove extension '.xferring' from file name after successful transfer.
if { $rename_file == "Y" } {
exp_send "rename $file_name_to_xfer $file_name\r"
expect "No such file or directory" { abort "\n***Error renaming file($file_name_to_xfer) to($file_name) on $sftp_server)." } \
"Failure" { abort "\n***Error renaming file($file_name_to_xfer) to($file_name) on $sftp_server)." } \
-re $sftp_prompt {} \
timeout { abort "\n***Error renaming file($file_name_to_xfer) to($file_name) on $sftp_server)." }
}
# QUIT!!
exp_send "quit\r"
# Successful SFTP session so exit with zero status
exit 0
}
# Error connecting to SFTP server so exit with non-zero status
exit 1
Alles anzeigen
Beispiel des Cronjob:
Die so auf meinen Büro-RasPi angekommene Log-Datei wird von FHEM ausgewertet.
Ausschnitt aus der fhem.cfg:
# #################################################################
# Definition für Heizöl Externe-Datei einzulesen
# Fake FileLog entry, to access the Heizoel.log from FHEMWEB
# #################################################################
#
define HeizoelFileLog FileLog ./log/Heizoel.log fakelog
attr HeizoelFileLog room Heizung
attr HeizoelFileLog group Heizoelstand
attr HeizoelFileLog logtype heizoel:Window,text
define PlotHeizoel SVG HeizoelFileLog:heizoel:CURRENT
attr PlotHeizoel label "Liter (l)"::"Liter (l)"::"Heizoel-Stand: Max=$data{max1} Liter, Letzter=$data{currval1} Liter"
attr PlotHeizoel group Heizoelstand
attr PlotHeizoel room Heizung
# #################################################################
Alles anzeigen
Beispiel der /opt/fhem/www/gplot/heizoel.gplot Datei:
############################
# Display the tank level.
# Bracew 2014-10-31 Heizoel
# <L1> .. <L3> Uebergabewerte aus fhem.cfg
############################
#
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel "Heizoel"
set ytics
set y2tics
set title <L3>
set grid xtics y2tics
set ylabel <L1>
set y2label <L2>
set yrange [0:4500]
set y2range [0:4500]
#FileLog 9::0:
plot "<IN>" using 1:2 title 'Tankfuellung' with lines
############################
Alles anzeigen
und vom Web-Frontend des FHEM-Servers auf jedem Browser als Plot (Beispiel im Attachment als jpg-Datei) grafisch dargestellt.
Anregungen und Verbesserungen nehme ich gerne entgegen.
Ich danke allen, die hier in diesem und im FHEM-Forum mir weitergeholfen und mich mit Ideen unterstützt haben.
Gutes Gelingen wünscht
Bracew
P.S.(Nachtrag vom 07.11.2014): Mit einem verbessertem Python-Skript (ultrasonic.py) zum messen des Abstandes habe ich nun eine Genauigkeit erreicht, welche den Messwert nur noch um 0,3 mm streuen lässt, sodass der HeizPi bei meinen 4.500 Liter Tank auf ± einen Liter genau messen kann. Die Genauigkeit ließe sich sogar noch steigern, ist für meine Zwecke nun aber ausreichend genau.
P.S.(Nachtrag vom 05.04.2015): Den Plot haben wir im FHEM-Forum nochmals verbessert, siehe: [url=http://forum.fhem.de/index.php/topi….html#msg228208]http://forum.fhem.de/index.php/topi….html#msg228208[/url]
Für den Sensor habe ich ein Loch in die Abluftleitung gebohrt und den Sensor mit Kabelverschraubungen im Rohr befestigt. Dies hat den Vorteil, dass der Mindest-Abstand zur Öloberfläche bei vollem Tank ungefähr passt.