hardware:channels:meters:water:wasserzaehler_ohne_s0
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
hardware:channels:meters:water:wasserzaehler_ohne_s0 [2015/12/31 16:54] – mikemiller | hardware:channels:meters:water:wasserzaehler_ohne_s0 [2024/04/23 18:03] (aktuell) – Fehler beim REST Aufruf jau | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
+ | ====== Mechanischen Wasserzähler auslesen ====== | ||
+ | ===== Ansatz 1: Text-/ | ||
- | Idee: Das kleine Sternrad des Wasserzählers mit einem Laser anvisieren. | + | **Idee:** Die Ziffern und Rädchen des Wasserzählers über eine Text-/ |
+ | **Vorteil: | ||
+ | **Nachteil: | ||
+ | {{https:// | ||
+ | ==== Hardware ==== | ||
+ | |||
+ | Folgende Hardware (Gesamtkosten ~15€) wird benötigt: | ||
+ | * ESP32-CAM WiFi | ||
+ | * USB zu TTL UART Serial Converter zum Download des ESP32 | ||
+ | * Stromversorgung des ESP32, z.B. über USB Breakout-Modul und USB-Netzteil | ||
+ | |||
+ | ==== Software ==== | ||
+ | |||
+ | Die Idee und Software stammt von Jomjol - die Software und eine Anleitung sind hier hinterlegt: https:// | ||
+ | * Für den ersten Download empfehle ich folgende Schaltung: https:// | ||
+ | * Bei Nichterkennung des Geräts: Gerätemanager -> Benötigte Treiber ggf. über optionale Windows Updates installieren | ||
+ | * Bei Verbindungsproblemen -> USB-TTL/ | ||
+ | |||
+ | ==== vzlogger-Integration ==== | ||
+ | |||
+ | Hier der Code für die vzlogger.conf zum Abholen und Verknüpfen der Daten: | ||
+ | < | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }], | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Folgende Parameter müssen im Code ersetzt werden: | ||
+ | * UUID_FRONTEND: | ||
+ | * IP-ADDRESS_ESP: | ||
+ | |||
+ | Der Parameter ' | ||
+ | |||
+ | |||
+ | ===== Ansatz 2: Laser ===== | ||
+ | |||
+ | Idee: Das kleine Sternrad des Wasserzählers mit einem Laser anvisieren | ||
+ | |||
+ | ==== Hardware ==== | ||
+ | |||
+ | <note important> | ||
+ | \\ | ||
{{: | {{: | ||
Zeile 20: | Zeile 71: | ||
{{: | {{: | ||
- | Neben dem Wasserzähler greife ich auch den Gaszähler ab. Auf dem letzten Rädchen ist hier ein silber beschichtetets Stück, das hervoragend reflektiert. Hier musste ich nicht den Aufwand mit dem Laser machen, sondern nutze hier dieses Bauteil [[http:// | + | Neben dem Wasserzähler greife ich auch den Gaszähler ab. Auf dem letzten Rädchen ist hier ein silber beschichtetets Stück, das hervoragend reflektiert. Hier musste ich nicht den Aufwand mit dem Laser machen, sondern nutze hier dieses Bauteil [[http:// |
{{: | {{: | ||
Zeile 26: | Zeile 77: | ||
Beide Zähler gehen an einen Analog in eines Arduino V3 (Ebay, deutlich unter 10 Euro) [[http:// | Beide Zähler gehen an einen Analog in eines Arduino V3 (Ebay, deutlich unter 10 Euro) [[http:// | ||
+ | Es empfiehlt sich, das Analogsignal, | ||
+ | |||
+ | <note important> | ||
+ | |||
+ | ==== Über Script in die Datenbank ==== | ||
+ | |||
+ | Für Gas- und Wasserzähler, | ||
+ | |||
+ | === Arduino === | ||
- | Das ist der Code auf dem Arduino. Die Idee ist die Differenz zwischen dem Tal und dem Berg bei dem analogen | + | Das ist der Code auf dem Arduino. Die Idee ist die Differenz zwischen dem Tal und dem Berg bei dem analogen |
<code c> | <code c> | ||
Zeile 40: | Zeile 100: | ||
long wasser_counter = 0; | long wasser_counter = 0; | ||
long gas_counter = 0; | long gas_counter = 0; | ||
+ | long loops = 0; | ||
int wasser_max = 0; | int wasser_max = 0; | ||
int gas_max = 0; | int gas_max = 0; | ||
- | int wasser = 0; | + | long wasser = 0; |
- | int gas = 0; | + | long gas = 0; |
int wasser_z = 0; | int wasser_z = 0; | ||
int gas_z = 0; | int gas_z = 0; | ||
unsigned long lastlog = 0; | unsigned long lastlog = 0; | ||
+ | unsigned long last_gas = 0; | ||
+ | unsigned long last_wasser = 0; | ||
unsigned long time; | unsigned long time; | ||
void loop() | void loop() | ||
{ | { | ||
- | int gas = analogRead(1); | + | // Gleitender Durchschnitt anwenden. |
- | int wasser = analogRead(0); | + | gas = ((gas *9) + analogRead(1)) /10; |
- | + | wasser = ((wasser *3) + analogRead(0))/4; | |
+ | time = millis(); | ||
+ | if (time < lastlog ){ //time ist übergelaufen | ||
+ | lastlog = 0; | ||
+ | last_gas= 0; | ||
+ | last_wasser = 0; | ||
+ | } | ||
wasser_max = max(wasser_max, | wasser_max = max(wasser_max, | ||
gas_max = max(gas_max, | gas_max = max(gas_max, | ||
- | + | loops++; | |
if ((wasser_max - 400) > wasser | if ((wasser_max - 400) > wasser | ||
{ | { | ||
Zeile 64: | Zeile 133: | ||
} | } | ||
- | if ((gas_max - 110) > gas ) | + | if ((gas_max - 100) > gas ) |
{ | { | ||
gas_z++; | gas_z++; | ||
Zeile 71: | Zeile 140: | ||
} | } | ||
- | if (gas_z > 100){ | + | if (gas_z > 100 && time - last_gas > 500){ |
gas_counter++; | gas_counter++; | ||
gas_max= 0; | gas_max= 0; | ||
+ | last_gas = time; | ||
} | } | ||
- | + | //Qn 2.5 =2500 Liter die Stunde = 60000 Markierungen am Raedchen 0,06 Sekunden (=60 ms für einen Durchgang) | |
- | if (wasser_z > 100){ | + | if (wasser_z > 40 && time - last_wasser > 30){ |
wasser_counter++; | wasser_counter++; | ||
wasser_max= 0; | wasser_max= 0; | ||
+ | last_wasser = time; | ||
} | } | ||
- | time = millis(); | ||
- | if (time - lastlog > 1000){ | + | |
+ | if (time - lastlog > 50){ | ||
// Serial.print(" | // Serial.print(" | ||
lastlog = time; | lastlog = time; | ||
long checksum = time + gas + gas_counter + wasser + wasser_counter; | long checksum = time + gas + gas_counter + wasser + wasser_counter; | ||
- | + | ||
- | + | ||
Serial.print(time); | Serial.print(time); | ||
Serial.print(" | Serial.print(" | ||
Zeile 102: | Zeile 172: | ||
Serial.print(gas_max); | Serial.print(gas_max); | ||
Serial.print(" | Serial.print(" | ||
- | Serial.println(wasser_max); | + | Serial.print(wasser_max); |
+ | Serial.print(" | ||
+ | Serial.println(loops); | ||
+ | loops=0; | ||
} | } | ||
Zeile 108: | Zeile 181: | ||
} | } | ||
</ | </ | ||
+ | |||
+ | === Script zur Datenübernahme === | ||
Der Code auf der PC/ | Der Code auf der PC/ | ||
+ | (Aufruf mit Cron regelmäßig, | ||
+ | |||
<code perl> | <code perl> | ||
# | # | ||
# Set up the serial port | # Set up the serial port | ||
+ | use Proc:: | ||
+ | die " | ||
+ | | ||
use DBI; | use DBI; | ||
#use LWP:: | #use LWP:: | ||
use Time::HiRes qw(usleep nanosleep time); | use Time::HiRes qw(usleep nanosleep time); | ||
- | my $dbh2 = DBI-> | + | my $dbh2 = DBI-> |
my $sth3 = $dbh2-> | my $sth3 = $dbh2-> | ||
#my $Gas_offset | #my $Gas_offset | ||
Zeile 123: | Zeile 203: | ||
system(" | system(" | ||
- | my $dbh = DBI-> | + | my $dbh = DBI-> |
my $sth = $dbh-> | my $sth = $dbh-> | ||
- | my $sth2 = $dbh-> | + | my $sth2 = $dbh-> |
- | $sth2-> | + | $sth2-> |
#1014 | #1014 | ||
Zeile 132: | Zeile 212: | ||
open(LOGGING, | open(LOGGING, | ||
open(SERIAL, | open(SERIAL, | ||
+ | $time_offset = 0; | ||
while (1) { | while (1) { | ||
# Poll to see if any data is coming in | # Poll to see if any data is coming in | ||
Zeile 160: | Zeile 241: | ||
if ( $test =~ /([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*)/ && $1 + $2 + $3 + $4 + $5 == $6){ | if ( $test =~ /([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*) - ([0-9]*)/ && $1 + $2 + $3 + $4 + $5 == $6){ | ||
+ | $time = $1; | ||
+ | $time_offset2 = int(time * 1000) - $time; | ||
+ | if (abs($time_offset-$time_offset2) >=5000){ | ||
+ | $time_offset = $time_offset2; | ||
+ | } | ||
$gas = $2; | $gas = $2; | ||
$gas_counter = $3; | $gas_counter = $3; | ||
$wasser = $4; | $wasser = $4; | ||
$wasser_counter = $5; | $wasser_counter = $5; | ||
- | $sth-> | + | # $sth-> |
if ($wasser_counter > $wasser_counter_old){ | if ($wasser_counter > $wasser_counter_old){ | ||
- | $sth3-> | + | $sth3-> |
} | } | ||
$wasser_counter_old = $wasser_counter; | $wasser_counter_old = $wasser_counter; | ||
if ($gas_counter > $gas_counter_old){ | if ($gas_counter > $gas_counter_old){ | ||
- | $sth3-> | + | $sth3-> |
} | } | ||
Zeile 178: | Zeile 264: | ||
chop($test); | chop($test); | ||
chop($test); | chop($test); | ||
- | print LOGGING $test.`date`; | + | print LOGGING $test.gmtime()." |
- | print $test.`date`; | + | print $test.gmtime()." |
} | } | ||
Zeile 188: | Zeile 274: | ||
</ | </ | ||
+ | < | ||
+ | Die Kanäle (channel_id) sind hart codiert: 12 und 9.</ | ||
+ | |||
Was noch justiert werden muss, ist die Differenz, die zwischen Tal und Berg nötig ist: | Was noch justiert werden muss, ist die Differenz, die zwischen Tal und Berg nötig ist: | ||
Zeile 242: | Zeile 331: | ||
?> | ?> | ||
- | |||
</ | </ | ||
- | |||
So sieht so eine Grafik aus: | So sieht so eine Grafik aus: | ||
- | |||
{{: | {{: | ||
{{: | {{: | ||
+ | <note tip>Oder den " | ||
+ | |||
+ | ==== Über vzlogger an die Middleware ==== | ||
+ | |||
+ | Für **nur einen** Wasserzähler, | ||
+ | |||
+ | === Arduino === | ||
+ | |||
+ | Hier der Code für den Arduino. Verkürzte Version von oben, weil nur eine Diode ausgewertet wird und die Impulse im Arduino nicht gezählt werden. | ||
+ | |||
+ | <code c> | ||
+ | |||
+ | // Original von mikemiller 2015-2016 | ||
+ | // Reduziert auf nur 1 Wasserzähler mit Ausgabe | ||
+ | // des Impulses auf die USB-Schnittstelle | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | Serial.begin(300); | ||
+ | // Open serial communications and wait for port to open: | ||
+ | // A baud rate of 115200 is used instead of 9600 for a faster data rate | ||
+ | // on non-native USB ports | ||
+ | // | ||
+ | while (!Serial) { | ||
+ | ; // wait for serial port to connect. Needed for native USB port only | ||
+ | } | ||
+ | | ||
+ | // | ||
+ | pinMode(A0, INPUT); | ||
+ | digitalWrite(A1, | ||
+ | } | ||
+ | int wasser_max = 0; | ||
+ | long wasser = 0; | ||
+ | int wasser_z = 0; | ||
+ | unsigned long lastlog = 0; | ||
+ | unsigned long last_wasser = 0; | ||
+ | unsigned long time; | ||
+ | |||
+ | void loop() | ||
+ | { | ||
+ | // Gleitender Durchschnitt anwenden. | ||
+ | wasser = ((wasser *3) + analogRead(0))/ | ||
+ | time = millis(); | ||
+ | if (time < lastlog ){ //time ist übergelaufen | ||
+ | lastlog = 0; | ||
+ | last_wasser = 0; | ||
+ | } | ||
+ | wasser_max = max(wasser_max, | ||
+ | |||
+ | if ((wasser_max - 200) > wasser) | ||
+ | { | ||
+ | wasser_z++; | ||
+ | } else { | ||
+ | wasser_z = 0; | ||
+ | } | ||
+ | |||
+ | // Qn 2,5 m³/h = 2.500 Liter pro Stunde | ||
+ | // Das Raedchen hat 6 Fluegel und dreht sich 1x je 0,1 Liter --> 60 Impulse je Liter | ||
+ | // Bei Maximaldurchfluss 2.500 l/h * 60 Imp/l = 2.500*60 Imp/h = 2.500/60 Imp/s = 41,7 Imp/s | ||
+ | // --> Mindestens 24 ms zwischen zwei Impulsen (" | ||
+ | |||
+ | if (wasser_z > 40 && time - last_wasser > 30) { | ||
+ | wasser_max= 0; | ||
+ | lastlog = time; | ||
+ | last_wasser = time; | ||
+ | Serial.print(0x00); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | Die Differenz zwischen Hell und Dunkel muss für dieses Code-Beispiel mindestens 200 betragen, sonst wird kein Impuls erzeugt. | ||
+ | |||
+ | === Frontend === | ||
+ | |||
+ | Kanal erstellen als Wassermengenzähler mit einer Auflösung von 60 (Impulse/l, siehe Berechnung im Code) und Stil=steps. UUID notieren. | ||
+ | |||
+ | === Beispielkonfiguration === | ||
+ | Exemplarische / | ||
+ | <code base vzlogger.conf> | ||
+ | { | ||
+ | // Water as S0 meter | ||
+ | |||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | |||
+ | " | ||
+ | " | ||
+ | |||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | // " | ||
+ | // " | ||
+ | // " | ||
+ | " | ||
+ | } | ||
+ | } // meter | ||
+ | </ | ||
+ | |||
+ | ===== Ansatz 3: grüne LED und Fototransistor ===== | ||
+ | |||
+ | Im [[https:// | ||
+ | |||
+ | Beim Nachbau hat es sich gezeigt, dass es u.a. auf die Helligkeit der verwendeten LED ankommt. Die zuerst beschafft LED war nicht hell genug, um den Fototransistor selbst bei direkter Belichtung durchzusteuern. Mit der nachbeschafften Nichia NSPG300D gelang der Betrieb dann auf Anhieb. | ||
+ | |||
+ | Wie in der Original-Anleitung beschrieben, | ||
+ | |||
+ | Dieser Impulsgenerator ließ sich dann einfach aufbauen und funktioniert in der Tat viel besser als die einfache Schaltung. Ob das an der Verwendung eines Mikrocontrollers oder an dem mit Linse fokussierten Lichtstrahl der LED liegt, ist unklar. Die Montage auf dem Wasserzähler und die Justage war jedenfalls in wenigen Minuten erledigt. Zum Schutz von Fremdlicht habe ich ein HT-Rohr mit Deckel über den Wasserzähler gestülpt. | ||
+ | |||
+ | Nach Lektüre des Artikels über [[: | ||
+ | - Einen USB-to-Serial-Adapter mit extra herausgeführtem Vcc-Pin aus der Schublade geholt | ||
+ | - diesen gleich zur Spannungsversorgung des STALL-Impulsgebers verwendet | ||
+ | |||
+ | {{: | ||
+ | |||
+ | Die Idee ist wie bei der Schaltung aus [[: | ||
+ | |||
+ | < | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }] | ||
+ | } | ||
+ | ]} | ||
+ | </ | ||
+ | |||
+ | {{: | ||
+ | |||
+ | So besonders gut scheint die Middleware mit den groben Impulsen (1 Impuls pro Liter) und dem oft stundenlangen Ruhen des Messwertes nicht klarzukommen; | ||
+ | |||
+ | {{: | ||
+ | |||
+ | {{: | ||
+ | |||
+ | ===== Ansatz 4: 3D Druck Optik und OPT101 ===== | ||
+ | Ich hatte den Laser-Ansatz für Jahre in Gebrauch. Abgesehen von den Sicherheitsproblemen hatte ich nach ein paar Jahren Algen im Wasserzähler. | ||
+ | Daher die Idee auch mit grüner LED zu arbeiten. Da 3D-Drucker vorhanden habe ich eine Halterung für eine Linse entworfen, deren Fokuspunkt direkt auf dem Rad liegt. | ||
+ | https:// | ||
+ | Impulse werden mit einem OPT101 Chip (intgrierter Verstärker) abgenommen. | ||
+ | {{: |
hardware/channels/meters/water/wasserzaehler_ohne_s0.1451577290.txt.gz · Zuletzt geändert: 2015/12/31 16:54 von mikemiller