Ankündigung

Einklappen
Keine Ankündigung bisher.

Zählerabfrage als Wiregate Plugin

Einklappen
Dieses Thema ist geschlossen.
X
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • JuMi2006
    antwortet
    Ja...ganz vergessen, ich wollte nochmal Danke sagen ;-) !!!
    Ich hätte dazu dann gern ein "Counter" rrd ... greentux hatte das mal irgendwo für seinen Pelletkessel, so könnte man schön den Tagesverbrauch sehen. Aber erstmal bin ich froh so weit zu sein.

    Als nächstes dann Umschaltung der Auslesegeschwindigkeit, socat und DB-Anbindung und sicherlich eine Berechnung des aktuellen (Verbrauchs 10/30/60 Minuten).

    Nun muss ich aber erstmal wieder handwerklich tätig werden.

    Also nochmal:
    Danke!

    P.S.: Bis auf die DPT9 Sub hab ich auch alles verstanden ;-)

    Einen Kommentar schreiben:


  • NetFritz
    antwortet
    Hallo
    Glückwunsch es geht doch so wie ich es Dir geschrieben habe.
    Gruß NetFritz

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    So...mein erstes Plugin ;-)

    Zählerabfrage für einen EMH-ITZ mit IR-Schreib-/Lesekopf. Hardware habe ich von volkszähler.org und das ganze läuft als Perl-Plugin via cron alle 5 Minuten.

    Der Zählerstand wird mit timestamp geloggt, in ein rrd geschrieben und auf den Bus gesendet.

    2 kleine Probleme/Fragen noch:

    1. Das rrd sieht komisch aus / stimmt nicht -> falsch angelegt !?
    2. Der Wert aus dem Log/Konsole (1774,9) stimmt nicht 100% mit dem Bus überein 1774,08) !?

    Code:
    #!/usr/bin/perl
    
    # (m)ein Stromzaehler mit IR-Schnittstelle blubbert nach einem "Anforderung-
    # telegramm" Daten raus. 
    # Das Telegramm ist mit 300 Baud, 7 Bit, 1 Stoppbit
    # und gerader Parit0t zu senden. Das ist der Initialmodus von Ger0ten,
    # die das Protokoll IEC 62056-21 implementieren.
    # Dies betrifft folgende bekannte Zähler:
    # Das Script ist angepasst auf EMH ITZ. Weitere Zähler wie z.B. Elster AS1440, Siemens TD-3511 müssen
    # angepasst werden (Position Zählerstand finden).
    # !!! Wiederholung nur alle 3 Minuten da der Zähler nach Ende des Scriptes weiter sendet !!!
    # Basis des Scripts von volkszähler.org
    # Autor: Andreas Schulze
    # Bugfix: Eric Schanze
    # Erweiterung um RRD und KNX-Teil auf Wiregate: JuMi2006
    # www.knx-user-forum.de
    # Version: 0.1
    # Datum: 02.04.2012
    
    ### KONFIGURATION ###
    my $PORT='/dev/ttyUSB-1-4';						#Schnittstelle
    my $anforderungstelegramm = "/?!\r\n";			#Anforderungstelegramm \r\n entspricht CR-LF
    my $rrd = "/var/www/rrd/Zaehler_WP_abs.rrd";	#rrd-file - muss vorher angelegt werden !
    my $file = '/var/tmp/log.txt';					#Logdatei - muss vorher angelegt werden !
    my $position = "539";							#Endposition des Zählerstandes im Seneprotokoll
    my $ga = "0/0/4";								#Gruppenadresse DPT9
    ### ENDE KONFIGURATION ###
    
    use warnings;
    use strict;
    use utf8;
    use Device::SerialPort;
    use RRDs;
    
    ### Seriellen Port initialisieren
    my $tty = new Device::SerialPort($PORT) || die "can't open $PORT: $!";
    $tty->baudrate(300)      || die 'fail setting baudrate';
    $tty->databits(7)        || die 'fail setting databits';
    $tty->stopbits(1)        || die 'fail setting stopbits';
    $tty->parity("even")     || die 'fail setting parity';
    $tty->write_settings     || die 'fail write settings';
    $tty->rts_active(1);
    $tty->dtr_active(1);
    $tty->read_char_time(500);     # 0.5 seconds for each character
    $tty->read_const_time(3000);   # 3 second per unfulfilled "read" call
    #$tty->debug(1);
    
    ### Anforderungstelegramm senden
    my $num_out = $tty->write($anforderungstelegramm);
    die "write failed\n" unless ($num_out);
    die "write inclomplete\n" unless ($num_out == length($anforderungstelegramm));
    print "$num_out Bytes written\n";		
    
    ### Daten auslesen
    
    my ($num_read, $s);						### Lesebefehl - Daten werden automatisch gesendet
    
    $s = $tty->read($position);     		### EMH ITZ: 539 Zeichen bis Wertende
    
    my $value = substr $s, -8; 				### hier wird z.B. "001702.2" als Zählerstand ausgegeben
    print ($value, "\n") ;					### Ausgabe des Zählerstandes in der Konsole
    
    my $val1 = substr $value, 0, 6;			### separiert die ersten 6 Zahlen im "String 001702"
    my $val2 = substr $value, 7;			### spariert die letzte Zahl im String (bei 8 Stellen)
    
    #print ("val1: ",$val1,"\n");			### Zur Kontrolle in der Konsole
    #print ("val2: ",$val2,"\n");			### Zur Kontrolle in der Konsole
    
    ### Wert in DPT9 umwandeln und in Konsole ausgeben
    my @hexdec = encode_dpt9($value);				
    my $hexval = sprintf("%x", $hexdec[0]) . " " . sprintf("%x", $hexdec[1]);
    print ($hexval,"\n");
    
    ### Wert an Gruppenadresse und RRD senden
    system("groupwrite ip:localhost $ga $hexval");
    RRDs::update("$rrd", "N: $value"); 
    
    ### Log der Zählerausgabe
    
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
    my $timestamp = printf "%4d-%02d-%02d %02d:%02d:%02d\n",$year+1900,$mon+1,$mday,$hour,$min,$sec;
    
    open(LOG,">>$file") || die "Fehler $!";
    print LOG ("\n",$year+1900,"-",$mon+1,"-",$mday," ",$hour,":",$min,":",$sec," ; ",$value);
    close LOG;
    
    $tty->close || die "can't close $PORT: $!";
    
    ### SUBS
    sub encode_dpt9 { # 2byte signed float
        my $state = shift;
        my $data;
    
    	my $sign = ($state <0 ? 0x8000 : 0);
        my $exp  = 0;
        my $mant = 0;
    
        $mant = int($state * 100.0);
        while (abs($mant) > 2047) {
            $mant /= 2;
            $exp++;
        }
        $data = $sign | ($exp << 11) | ($mant & 0x07ff);
        return $data >> 8, $data & 0xff;
    }
    Angehängte Dateien

    Einen Kommentar schreiben:


  • makki
    antwortet
    Zitat von JuMi2006 Beitrag anzeigen
    Gern das ganze Plugin (wegen Timeout nicht als Wirgate-Plugin). Ich wollte euch den Thread hier bloss nicht mit meinem ganzen Code zumüllen ;-).
    Zumüllen ist nur, wenns nichts mit dem Thread zu tun hat - hier wohl nicht der Fall
    Obs nun ein WG-Plugin (das ist nicht die richtige Antwort auf jede Frage, manchmal gehts anders eben besser/einfacher, ist ja auch Ok und kann im OpenAutomation-SVN unter Tools seinen Platz für die Nachwelt finden..

    Nun zur Frage: nimm Dir doch einfach die "sub encode_dpt9 .." aus dem wiregated.pl, die liefert die beiden Bytes von einem Float.
    Oder eben den ganzen Sums (encode*, knx_write), ein
    knx_write("10/1/2","001732.9",9);
    tuts dann.. (statt ".." eben deine Variablen mit GA/Wert einsetzen und oben ein "use EIBConnection; - ein bis zwei Zeilen wird man aus knx_write noch rauswerfen müssen)

    Makki

    P.S.: Ich habe mir angewöhnt zwischen Seriellem Port und Perl immer einen socat (localhost, UDP) zu packen, das spart einem das ganze serielle gefrickel mit reconnect, restart usw..)

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Gern das ganze Plugin (wegen Timeout nicht als Wirgate-Plugin). Ich wollte euch den Thread hier bloss nicht mit meinem ganzen Code zumüllen ;-).

    Code:
    #!/usr/bin/perl
    
    # Basis von volkszähler.org
    # (m)ein Stromz0hler mit IR-Schnittstelle blubbert nach einem "Anforderung-
    # telegramm" Daten raus. Das Telegramm ist mit 300 Baud, 7 Bit, 1 Stoppbit
    # und gerader Parit0t zu senden. Das ist der Initialmodus von Ger0ten,
    # die das Protokoll IEC 62056-21 implementieren.
    #
    # Autor: Andreas Schulze
    # Bugfix: Eric Schanze
    # Datum: 20120302
    #
    
    my $PORT='/dev/ttyUSB-1-4';				#Schnittstelle
    my $anforderungstelegramm = "/?!\r\n";		#Anforderungstelegramm
    my $rrd_path='var/www/rrd/';			#Pfad zum rrd
    my $rrd_name='Stand';				#Name des rrd
    my $file = '/var/tmp/log.txt';			#Logdatei
    
    
    use warnings;
    use strict;
    use utf8;
    use Device::SerialPort;
    use RRDs;
    
    
    my $tty = new Device::SerialPort($PORT) || die "can't open $PORT: $!";
    $tty->baudrate(300)      || die 'fail setting baudrate';
    $tty->databits(7)        || die 'fail setting databits';
    $tty->stopbits(1)        || die 'fail setting stopbits';
    $tty->parity("even")     || die 'fail setting parity';
    $tty->write_settings     || die 'fail write settings';
    $tty->rts_active(1);
    $tty->dtr_active(1);
    $tty->read_char_time(500);     # 0.5 seconds for each character
    $tty->read_const_time(3000);   # 3 second per unfulfilled "read" call
    #$tty->debug(1);
    
    my $num_out = $tty->write($anforderungstelegramm);
    die "write failed\n" unless ($num_out);
    die "write inclomplete\n" unless ($num_out == length($anforderungstelegramm));
    print "$num_out Bytes written\n";
    
    my ($num_read, $s);
    
    $s = $tty->read(539);     		### 539 Zeichen bis Wertende
    
    my $value = substr $s, -8; 		### hier wird z.B. "001702.2" als Zählerstand ausgegeben
    print ($value, "\n") ;			### Ausgabe des Zählerstandes in der Konsole
    
    my $val1 = substr $value, 0, 5;					### separiert die ersten 6 Zahlen im "String 001702"
    my $val2 = substr($value,length($value)-9,1);	### spariert die letzte Zahl "2" im String mit 8 Zeichen k.A. warum "9"
    
    my @array = ("$val1","$val2");					### Hier hab ich jetzt also die Zahl vor und die Zahl nach dem Komma - nötig ???
    #print @array;									### o.k. Array angelegt ;-)
    
    #####################################
    #HIER KOMME ICH JETZT INS TAL DER AHNUNGSLOSEN 
    #Als Werte stehen hier also schon "001702.2","00172" und "2" zur Verfügung
    
    my $x= print uc(sprintf("%x", $value)), "\n";
    my $wert = hexstr_to_signed32int($x);       # von hex nach 32bit integer
    #
    # ---  Sub Konvertiert hex string to 32 bit signed integer ----------
    sub hexstr_to_signed32int {
        my $hexstr = @_;
        die "Invalid hex string: $hexstr"
            if $hexstr !~ /^[0-9A-Fa-f]{1,8}$/;
        my $num = hex($hexstr);
        return $num >> 31 ? $num - 2 ** 32 : $num;
    }
    
    #print uc(sprintf("%x", $value)), "\n";
    #print uc(sprintf("%x", $value)), "\n";
    #print ($wert, "\n"); 							###bringt die Vorkommalzahl als HEX
    
    my $hexval = sprintf("%x", $val1) . " " . sprintf("%x", $val2);
    print ($hexval, "\n");
    
    #ENDE DES TALS
    #####################################
    
    #####################################
    # Log der Zählerausgabe
    open(LOG,">$file") || die "Fehler $!";
    print LOG $s;
    close LOG;
    
    ### RRD schreiben und Wert senden
    
    #system("groupwrite ip:localhost 0/0/4 $val1");
    
    $tty->close || die "can't close $PORT: $!";

    Einen Kommentar schreiben:


  • makki
    antwortet
    Also alles auf dieser Welt tendiert z.B. dazu 0yyyy als Octal zu interpretieren, sag uns doch mal die ganze Wahrheit, also das ganze,komplette Plugin - ist unter uns - eh implizit GPL

    Makki

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    @makki:

    Mit der Mittelgruppe ist natürlich doof ;-) - senden auf den Bus klappt schonmal - nur das Format stimmt noch nicht.
    RRD wird sich dann auch lösen - URRD war nen Tippfehler hier im script war es richtig.

    Ich muss trotzdem nochmal ganz explizit nachfragen:

    der Wert "001732.9" ist das was mir den Zählerstand angibt.

    Kann der so ins RRD geschoben werden ?
    Was muss ich damit machen um ihn auf den Bus zu senden ?

    Zielformat ist ja DPT9 soviel hab ich verstanden. Ich bekomme es auch hin dass z.B. 1732 als dreistellige HEX (6C4) ausgegeben wird, aber das ist mehr try&error.

    Also "001732.9" --> Hex --> DPT9 oder welche Reihenfolge ?

    Dank und Gruß
    Mirko

    Einen Kommentar schreiben:


  • makki
    antwortet
    Zitat von JuMi2006 Beitrag anzeigen
    Code:
    URRDs::update
    das in der Konsole ?
    Code:
    Undefined subroutine &main::update called at /var/tmp/ehz.pl line 53
    vermutlich weil es RRDs::.. nicht URRDs:: heisst,
    was sprich dagegen das vereinfachte update_rrd(..) zu verwenden?

    2. Warum sendet groupswrite auf die 10/2/10 statt auf die 10/10/10
    Weil es 10/10/10 nicht geben kann?!
    Zwei Möglichkeiten: den Bullshit falsch umwandeln oder ignoriren, der gewählte Weg ist "falsch" umwandeln..
    Edit: Mittelgruppen gehen mal so von 0-7

    Makki

    Einen Kommentar schreiben:


  • NetFritz
    antwortet
    Hallo
    @JuMi2006
    Ich bekomme ja einen String ($value) im Format 1234.5 (kWh) ausgegeben.
    Was kannst Du den an deinem Zählerdisplay ablesen, zeigt dein Zähler diesen Wert auch an dann ist ja bis da alles o.K.

    In die RRD-DB kannst Du diesen Wert ohne eine Umstellung schreiben.
    Allerdings muss man erst eine RRD-DB erzeugen.

    Um diesen Wert auf den Bus zubringen muss er umgestellt werden auf 2byte. Dafür ist "encode_dpt9($value);".

    Schau mal hier bei den Tapko: Tools, da kannst Du unter DPT9(EIS5) den Wert deines Zählerstandes eingeben und bekommst dann 2byte hex zurück.
    Bei einem Zählerstand von 1234.5 sind das 3788.

    Gruß NetFritz

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Ich hab hier nochmal ein Verständnisproblem.
    Ich bekomme ja einen String ($value) im Format 1234.5 (kWh) ausgegeben.
    Warum kann ich diese Zahl (oder ist es gar keine?) nicht einfach weiter verwerten?
    Was muss damit getan werden.

    Hier nochmal der letzte Teil des Codes:

    Code:
    my $value = substr $s, -8; 		### hier wird z.B. "1702.2" als Zählerstand ausgegeben
    #print $value;				### Ausgabe des Zählerstandes in der Konsole
    
    my $val1 = substr $value, 0, 5;		### separiert die ersten 6 Zahlen im String
    my $val2 = substr($value,length($value)-9,1);	### spariert die letzte Zahl im String mit 8 Zeichen k.A. warum "9"
    
    my @array = ("$val1","$val2");	         ### Hier hab ich jetzt also die Zahl vor und die Zahl nach dem Komma - nötig ???
    
    #print @array;                                        ### o.k. Array angelegt ;-)
    
    my $wert = sprintf("%x", $array[0]);	         ### Was muss ich in was umwandeln? Ziel: RRD und Bus
    
    print $wert;			         ### Kontrolle in der Konsole
    Danke !
    Falls jemand mag auch per PN - Ergebnis gibts dann natürlich hier ;-)

    Einen Kommentar schreiben:


  • NetFritz
    antwortet
    Hallo

    groupswrite ist für 1-bit Operationen
    groupwrite für alle anderen Datentypen > 1bit

    Also musst Du groupwrite benutzen.

    $value musst Du noch umwandeln.
    Schau mal in Post #79 im Code unten bei sub encode_dpt9 .

    Die Sub habe ich so augerufen
    "@hexdec = "encode_dpt9($value);"

    Anschließend muss das Array noch zusammengefügt werden
    "$hexval = sprintf("%x", $hexdec[0]) . " " . sprintf("%x", $hexdec[1]);"

    Dann $hexval mit groupwrite auf den BUS schreiben.
    "system("groupwrite ip:127.0.0.1 5/0/14 $hexval"); "

    Gruß NetFritz

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    3 kurze Fragen:

    1. Warum bringt mir
    Code:
    URRDs::update
    das in der Konsole ?
    Code:
    Undefined subroutine &main::update called at /var/tmp/ehz.pl line 53
    2. Warum sendet groupswrite auf die 10/2/10 statt auf die 10/10/10

    3. Ich müsste $value noch irgendwie umwandeln oder?

    Hier mein Code:

    Code:
    #!/usr/bin/perl
    
    #
    # (m)ein Stromz0hler mit IR-Schnittstelle blubbert nach einem "Anforderung-
    # telegramm" Daten raus. Das Telegramm ist mit 300 Baud, 7 Bit, 1 Stoppbit
    # und gerader Parit0t zu senden. Das ist der Initialmodus von Ger0ten,
    # die das Protokoll IEC 62056-21 implementieren.
    #
    # Autor: Andreas Schulze
    # Bugfix: Eric Schanze
    # Datum: 20120302
    #
    
    my $PORT='/dev/ttyUSB-1-4';				#Schnittstelle
    my $anforderungstelegramm = "/?!\r\n";		#Anforderungstelegramm
    my $rrd_path='var/www/rrd/';			#Pfad zum rrd
    my $rrd_name='Stand';				#Name des rrd
    my $file = '/var/tmp/log.txt';			#Logdatei
    
    
    use warnings;
    use strict;
    use utf8;
    use Device::SerialPort;
    use RRDs;
    
    
    my $tty = new Device::SerialPort($PORT) || die "can't open $PORT: $!";
    $tty->baudrate(300)      || die 'fail setting baudrate';
    $tty->databits(7)        || die 'fail setting databits';
    $tty->stopbits(1)        || die 'fail setting stopbits';
    $tty->parity("even")     || die 'fail setting parity';
    $tty->write_settings     || die 'fail write settings';
    $tty->rts_active(1);
    $tty->dtr_active(1);
    $tty->read_char_time(500);     # 0.5 seconds for each character
    $tty->read_const_time(3000);   # 3 second per unfulfilled "read" call
    #$tty->debug(1);
    
    my $num_out = $tty->write($anforderungstelegramm);
    die "write failed\n" unless ($num_out);
    die "write inclomplete\n" unless ($num_out == length($anforderungstelegramm));
    print "$num_out Bytes written\n";
    
    my ($num_read, $s);
    
    $s = $tty->read(539);     		### 539 Zeichen bis Wertende
    
    my $value = substr $s, -8; 		### hier wird z.B. "1702.2" als Zählerstand ausgegeben
    print $value;				### Ausgabe des Zählerstandes in der Konsole
    
    system("groupswrite ip:localhost 10/10/10 test");    # Auf Bus senden
    #RRDs::update                                                  # Ausgabe in rrd schreiben
    
    # Log der Zählerausgabe
    open(LOG,">$file") || die "Fehler $!";
    print LOG $s;
    close LOG;
    
    $tty->close || die "can't close $PORT: $!";
    Das ganze aus der Konsole gestartet, so würde cron sich ja dann auch verhalten.

    Danke und Gruß
    Mirko

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Danke !!! Damit werd ich weitermachen ... soll ja auch was fürs Hirn bei rauskommen ;-)

    Einige Zähler kann man wohl nach der Initialisierung auf höheren Geschwindigkeiten weiter auslesen. Leider Fehlt mir bei EMH eine ordentlich Doku - mehr als das Datenblatt geben die nicht raus. Dort ist eine Geschwindigkeit von 4800 Baud angegeben ... wird wohl irgendwie gehen.

    Ich werde dann wieder berichten und nachfragen.

    Gruß Mirko

    Einen Kommentar schreiben:


  • 2ndsky
    antwortet
    Zitat von JuMi2006 Beitrag anzeigen
    Wie bekomme ich jetzt sauber die 1.8.0 (Zählerstand in kWh) separiert? Den Rest brauch ich eigentlich nicht wirklich. Trotz aller Bemühungen hab ich das mit Arrays immer noch nicht drauf, aber das wäre hier wohl von Vorteil.
    Mit regulären Ausdrücken (regular expressions) kannst du einen Stringvergleich machen. Dein Zählerstand hat ja folgendes Format "1.8.0(0001715.0*kWh)", also machst du eine IF in deiner Schleife. Wenn $s den String "1.8.0(" enthält, dann muss es dein Zählerstand sein. Innerhalb der IF dann noch einen substr machen um nur die Zahl zu extrahieren und schon hast du deinen Zählerstand.

    Hier mal etwas Lektüre für den Stringvergleich mit regulären Ausdrücken: Comparing values in perl

    ALTERNATIV: Wenn der Zählerstand immer an der selben Stelle im Array steht kannst du auch gleich direkt auf den Index zugreifen, dann sparst du dir die Suche nach dem Textanfang. Wie man mit Arrays umgeht siehst du hier: Arrays in Perl - Besonderheiten

    Da du aber immer schrittweise liest, wird das so nicht funktionieren. Du könntest dir aber eine Zählervariable einbauen. Sprich eine Variable die du vor der Schleife mit 0 initialisierst und in der Schleife bei jedem Durchgang um eins hochzählst. Bis sie den Wert hat, an dem dein Zählerstand stehen müsste (einfach abzählen in deiner Ausgabe). Hat sie den Wert erreicht hast du deinen Zählerstand in $s, diesen Speichern und den Zähler wieder zurückstellen (auf 0). Bei der nächsten Schleifenwiederholung wird dieser wieder hochgezählt bis er wieder den Wert erreicht hat usw.

    Das sollten jetzt zumindestens mal einige Stichworte für eine Google Suche gewesen sein

    Einen Kommentar schreiben:


  • makki
    antwortet
    Zitat von JuMi2006 Beitrag anzeigen
    I
    300 Baud
    Puh, wem da wohl mal wieder jemand das Hirn amputiert hat (vermutlich der BWL-Student wegen 20ct dem Techniker), ich mein jeder 8-Bit uC (1€) schafft auch ohne externen Quarz (30ct extra) 115kbit

    300 Baud ist halt schon seeeeeeeeeeeeeeeehr laaaaaaaaaaaaaaaaaaaaaangsam, alte Handy surfen mit der 1000fachen Geschwindigkeit, aktuelle mit ein paar tausenderstellen mehr..

    Egal.. wo liegt das Problem? Für ein Perl-Plugin sind solche schnarchzapfen nicht optimal aber geht (wenige davon), cronjob: besser, den Wert halt iwo hinschreiben und dann damit machen was man anstrebt..

    Makki

    Einen Kommentar schreiben:

Lädt...
X