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

  • mikeeyy
    antwortet
    Hi JuMi,

    warum wandelst Du die Ausgabe vor der Weiterverarbeitung in HEX um? Bei mir tut es leider nix.. keine Ausgabe..

    Grüße,
    Mike

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    So ... jetzt auch für SML. Läuft derzeit bei Lio ... er wird berichten.
    Das ganze ist KEIN WireGate Plugin sondern ein simples Perl-Script dass über crontab aufgerufen wird.

    Grüße

    Code:
    #!/usr/bin/perl
    # Autor: JuMi2006 / www.knx-user-forum.de
    # knx_write sub: makki / www.knx-user-forum.de
    # Version: 0.1
    # Datum: 19.02.2013
    
    use warnings;
    use strict;
    use Device::SerialPort;
    use feature "switch";
    use EIBConnection;
    use RRDs;
    
    #0701000F0700FF = 7.0       FIXME !!! [0F]0700
    #070100010801FF = 1.8.1
    
    my $eib_url = "local:/tmp/eib";     #for local eibd "local:/tmp/eib" for eibd in LAN: "ip:192.168.2.220:6720"
    my $device = "/dev/ttyUSB-1-4";
    my $rrdpath = "/var/www/rrd";
    
    my @obis;
    push @obis,{obis=>"1.8.0",  fact=>10000, ga =>"14/8/1", dpt => 14, rrd_name => "Zaehler_Verbrauch", rrd => "c"   }; #rrd: c=counter ; g=gauge
    push @obis,{obis=>"7.0",    fact=>10,    ga =>"14/8/0", dpt => 9 , rrd_name => "Zaehler_Leistung",  rrd => "g" };
    my @countermodes = (5,15,60,1440);    #Aufloesungen fuer COUNTER RRDs in Minuten (1440 = Tagesverbrauch)
    
    
    my $debug = "";
    my $port = Device::SerialPort->new($device) || die $!;
    $port->databits(8);
    $port->baudrate(9600);
    $port->parity("none");
    $port->stopbits(1);
    $port->handshake("none");
    $port->write_settings;
    $port->dtr_active(1);
    $port->purge_all();
    $port->read_char_time(0);     # don't wait for each character
    $port->read_const_time(2000); # 1 second per unfulfilled "read" call
    
    
    my ($x,$data);
    my $start = 0;
    while ($start < 2) { # wait for second 1B1B1B1B01010101
           my ($count,$saw)=$port->read(512);   # will read 512 chars
                if ($count == 512) {       # wurden 512 chars gelesen ?       
                $x = uc(unpack('H*',$saw)); # nach hex wandeln
                $data .= $x;
                if ($data =~ /1B1B1B1B01010101/){$start ++};
           }
    }
    
    $data =~ m/1B1B1B1B01010101(.*?)B1B1B1/;
    my $sml = $1;
    #print $1."\n";
    
    foreach my $obis (@obis)
    {
    $obis->{obis} =~ s/\./0/g;
    $obis->{obis} .= "FF";
    if ($debug==1){print $obis->{obis}." Obis\n";}
    $sml =~ m/$obis->{obis}(.*?)0177/;
    if ($debug==1){print $1." contains hex \n";}
    my $sml_val = $1;
    #extract value
    $sml_val =~ s/^.*52FF//;
    $sml_val = substr($sml_val,2);
    print $sml_val." hex \n";
    my $value = $sml_val;
    my $dec_value = sprintf("%d", hex($value));
    $dec_value /= $obis->{fact};
    print $dec_value."<<<<---- Wert\n";
    
    if ($obis->{rrd} eq "c")
                {
                    &rrd_counter ($obis->{rrd_name},$dec_value)
                }
    if ($obis->{rrd} eq "g")
                {
                    &rrd_gauge ($obis->{rrd_name},$dec_value)
                }
    
    &knx_write ($obis->{ga},$dec_value,$obis->{dpt});
    print "GA:".$obis->{ga}." Wert:".$dec_value." DPT:".$obis->{dpt}."\n";
    }
    
    
    ### SUBS ###
    
    sub rrd_counter
    {
        if ($debug==1){print ("COUNTER","\n")};
        foreach (@countermodes)
        {
            my $obisname = $_[0];
            if ($debug==1){print $obisname." obisname \n";}
            my $value = $_[1];
            if ($debug==1){print $value." value \n";}
            my $rrdname = $obisname."_".$_."\.rrd";
            if ($debug==1){print ($rrdname,"\n")};
            my $rrdfile = $rrdpath."\/".$rrdname;
            unless (-e $rrdfile)
            {
                RRDs::create ($rrdfile,"DS:value:COUNTER:".(($_*60)+600).":0:10000000000","RRA:AVERAGE:0.5:1:365","RRA:AVERAGE:0.5:7:300","-s ".($_*60));
            }
            my $countervalue = int($value*$_*60);
            RRDs::update("$rrdfile", "N:$countervalue");
        }
    }
    
    sub rrd_gauge
    {
        if ($debug==1){print ("GAUGE","\n")};
        my $obisname = $_[0];
        if ($debug==1){print $obisname." obisname \n";}
        my $value = $_[1];
        if ($debug==1){print $value." value \n";}
        my $rrdname = $obisname."\.rrd";
        if ($debug==1){print ($rrdname,"\n")};
        my $rrdfile = $rrdpath."\/".$rrdname;
        unless (-e $rrdfile)
        {
            RRDs::create ($rrdfile,"DS:value:GAUGE:900:0:10000000000","RRA:AVERAGE:0.5:1:2160","RRA:AVERAGE:0.5:5:2016","RRA:AVERAGE:0.5:15:2880","RRA:AVERAGE:0.5:60:8760");
        }
        RRDs::update("$rrdfile", "N:$value");
    }
    
    
    sub knx_write {
        my ($dst,$value,$dpt,$response,$dbgmsg) = @_;
        my $bytes;
        my $apci = ($response) ? 0x40 : 0x80; # 0x40=response, 0x80=write
        #     DPT 1 (1 bit) = EIS 1/7 (move=DPT 1.8, step=DPT 1.7)
        #     DPT 2 (1 bit controlled) = EIS 8
        #     DPT 3 (3 bit controlled) = EIS 2
        #     DPT 4 (Character) = EIS 13
        #     DPT 5 (8 bit unsigned value) = EIS 6 (DPT 5.1) oder EIS 14.001 (DPT 5.10)
        #     DPT 6 (8 bit signed value) = EIS 14.000
        #     DPT 7 (2 byte unsigned value) = EIS 10.000
        #     DPT 8 (2 byte signed value) = EIS 10.001
        #     DPT 9 (2 byte float value) = EIS 5
        #     DPT 10 (Time) = EIS 3
        #     DPT 11 (Date) = EIS 4
        #     DPT 12 (4 byte unsigned value) = EIS 11.000
        #     DPT 13 (4 byte signed value) = EIS 11.001
        #     DPT 14 (4 byte float value) = EIS 9
        #     DPT 15 (Entrance access) = EIS 12
        #     DPT 16 (Character string) = EIS 15
        # $dpt = $eibgaconf{$dst}{'DPTSubId'} unless $dpt; # read dpt from eibgaconf if existing
        given ($dpt) {
            when (/^12/)             { $bytes = pack ("CCL>", 0, $apci, $value); }  #EIS11.000/DPT12 (4 byte unsigned)
            when (/^13/)             { $bytes = pack ("CCl>", 0, $apci, $value); }
            when (/^14/)             { $bytes = pack ("CCf>", 0, $apci, $value); }
            when (/^16/)             { $bytes = pack ("CCa14", 0, $apci, $value); }
            when (/^17/)             { $bytes = pack ("CCC", 0, $apci, $value & 0x3F); }
            when (/^20/)             { $bytes = pack ("CCC", 0, $apci, $value); }
            when (/^\d\d/)           { return; } # other DPT XX 15 are unhandled
            when (/^[1,2,3]/)        { $bytes = pack ("CC", 0, $apci | ($value & 0x3f)); } #send 6bit small
            when (/^4/)              { $bytes = pack ("CCc", 0, $apci, ord($value)); }
            when ([5,5.001])         { $bytes = pack ("CCC", 0, $apci, encode_dpt5($value)); } #EIS 6/DPT5.001 1byte
            when ([5.004,5.005,5.010]) { $bytes = pack ("CCC", 0, $apci, $value); }
            when (/^5/)              { $bytes = pack ("CCC", 0, $apci, $value); }
            when (/^6/)              { $bytes = pack ("CCc", 0, $apci, $value); }
            when (/^7/)              { $bytes = pack ("CCS>", 0, $apci, $value); }
            when (/^8/)              { $bytes = pack ("CCs>", 0, $apci, $value); }
            when (/^9/)              { $bytes = pack ("CCCC", 0, $apci, encode_dpt9($value)); } #EIS5/DPT9 2byte float
            default                  { LOGGER('WARN',"None or unsupported DPT: $dpt sent to $dst value $value"); return; }
        }
        my $leibcon = EIBConnection->EIBSocketURL($eib_url) or return("Error opening con: $!");
        if ($leibcon->EIBOpenT_Group(str2addr($dst),1) == -1) { return("Error opening group: $!"); }
        my $res=$leibcon->EIBSendAPDU($bytes);
        $leibcon->EIBClose();
        return $res;
        
        # str2addr: Convert an EIB address string in the form "1/2/3" or "1.2.3" to an integer
        sub str2addr {
            my $str = $_[0];
            if ($str =~ /(\d+)\/(\d+)\/(\d+)/) { # logical address
                return ($1 << 11) | ($2 << 8) | $3;
            } elsif ($str =~ /(\d+)\.(\d+)\.(\d+)/) { # physical address
                return ($1 << 12) | ($2 << 8) | $3;
            } else {
                #bad
                return;
            }
        }
        
    }
    
    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;
    }

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Zitat von makki Beitrag anzeigen
    Wenns wirklich als float rauskommt plädiere ich für int($wert*1000) -> ins RRD /1000
    Passiert ja so ähnlich:
    Code:
    int($wert*rrd_step) -> ins RRD /rrd_step
    Na ich beobachte das mal .

    Grüße!

    Einen Kommentar schreiben:


  • makki
    antwortet
    Also meine ABB bieten mir nur 2 oder 4Byte signed/unsigned int an (DPT 7/8/12/13)
    Das Problem mit dem Float im allgemeinen und dem DPT9/14 (IEE754) im speziellen ist das der hoppeln kann, weil er etwas verdichtet (was in der Natur der Sache liegt, weil auch modernste CPUs eigentlich nur INT kennen, FLOAT ist nur eine spielform der Auflösung und Rechen-Methode)
    Das muss nicht schlecht sein, ist es auch nicht, aber bei einem Counter mit geringem increment in der dritten Nachkommastelle kann das ziemlich blöd ausgehen.. (Chris M. hatte das mal sehr anschaulich erklärt aber den Beitrag finde ich gerade nicht mehr; Kern der Sache, wenn man den Float als 16/32bit/DPT9/iEEE754, Perl/Python/C 4x rumrechnet können sechs verschiedene Werte rauskommen..)

    Wenns wirklich als float rauskommt plädiere ich für int($wert*1000) -> ins RRD /1000

    Makki

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Nochmal .

    Warum? Wenn der Zählerstand nunmal kein integer ist sondern float - dann behandle ich den auch als float.
    Im RRD-tool wird er ja spätestens nach der Multiplikation mit step wieder ein integer. Falls Du das Problem mit dem COUNTER-RRD meinst. Ich wills ja verstehen, aber ich sehe da wirklich keinen Grund.

    Btw. selbst ABB nutzt DPT14 für den Zählerstand.

    Grüße

    Einen Kommentar schreiben:


  • makki
    antwortet
    Nochmal, mit Verlaub, DPT9/14 ist für nen Zählerstand ganz einfach falsch..
    Das sollte ein integer sein..

    Makki

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Im modernen eHz ist der Zählerstand im besten Fall ein float mit der Auflösung von 0.001 kWh. Im schlechtesten Fall hat er eine Auflösung von 0.1 kWh.
    Damit bleibt das float und eigentlich kommt da sinnig nur DPT14 raus.
    Das DPT9 Müll ist haben wir ja gemerkt .

    Grüße

    EDIT
    Da Frank das neuere script mit den erweiterten DPTs verheiratet hat, kann man sich den DPT ja aussuchen .
    Die Version liegt seit gestern im SVN.

    Einen Kommentar schreiben:


  • makki
    antwortet
    Nur so am Rande, die verwendeten DPTs sind ungeeignet..

    Zitat von JuMi2006 Beitrag anzeigen
    ...
    ja das liegt am DPT9 ... der ist für sowas zu ungenau. Die RRDs werden glücklicherweise direkt im Plugin erstellt.
    Ich hab mein Script auf DPT14 umgestellt, ist allerdings noch nicht im SVN
    Hmm, das liegt aber primär daran, das DPT9 (ein [recht stranger] !float!) für Zähler-Werte schlicht völlig ungeeignet ist!

    Das wäre wohl lieber ein DPT7 (unsigned 2Byte/16Bit, Überlauf bei 65.535 beachten) oder DPT12 geworden (unsigned 4Byte/32Bit, Überlauf bei 4.294.967.295 beachten) , oder?

    DPT14 (4byte IEEE float ist im Kern genauso ungeeignet, da fällt es nur nicht so schnell auf aber es wird über kurz oder lang zu komischen Phänomenen kommen weil der Zählerstand eine Ganzzahl ist und bleibt..

    Makki

    Einen Kommentar schreiben:


  • mikeeyy
    antwortet
    hat doch hier schon ganz gut funktioniert

    Code:
    my $obisvalue=substr($buffer,($pos+length($key)+1),$obis{$key});
    wie gesagt.. perl ist echt nicht mein thema.. ich taste mich hier nur vorsichtig durch den dschungel..

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Nur mal kurz zwischendrin:

    Code:
    1-1:1.8.0*255(001216.858*kWh)
    Müssten wir erstmal per RegEx zerlegen:

    1-1
    1.8.0
    001216.858
    kWh

    Einen Kommentar schreiben:


  • mikeeyy
    antwortet
    Zitat von JuMi2006 Beitrag anzeigen

    jetzt muss ich gestehen dass ich Deinen Beitrag mit Frank verwechselt habe ... ich erinnere mich wieder ... Sorry
    Aber Du hattest zusätzlich noch die "beiden" Zähler in einem oder wie war das?
    Hallo JuMi,

    kein Problem ;-) Bei der Vielzahl von Anfragen hier kann man schon mal den Überblick verlieren.

    Ich habe tatsächlich zwei Zähler die gekoppelt sind. Wobei der Zähler mit dem seriellen Anschluss aber auch die Infos vom zweiten Zähler mitliefert.

    Siehe auch der Output vom alten Script hier:

    0-0:96.1.0*255(050551428442)
    1-0:1.8.0*255(003253.186*kWh) -> Zähler 1
    1-0:1.8.1*255(000615.455*kWh)
    1-0:1.8.2*255(002637.732*kWh)
    1-0:2.8.0*255(000000.226*kWh)
    1-0:2.8.1*255(000000.226*kWh)
    1-0:2.8.2*255(000000.000*kWh)
    0-0:96.14.0*255(02)
    1-0:15.7.0*255(001360*W)
    0-0:17.0.0*255(0085*A)
    0-0:96.3.10*255(1)
    0-1:96.1.0*255(05055142844)
    1-1:1.8.0*255(001216.858*kWh)(13-02-13 09:00:53) -> Zähler 2
    1-1:1.8.1*255(001216.859*kWh)(13-02-13 09:00:53)
    1-1:1.8.2*255(000000.000*kWh)(13-02-13 09:00:53)
    1-1:1.8.3*255(000000.000*kWh)(13-02-13 09:00:53)
    1-1:1.8.4*255(000000.000*kWh)(13-02-13 09:00:53)
    1-1:2.8.0*255(000000.000*kWh)(13-02-13 09:00:53)
    1-1:2.8.1*255(000000.000*kWh)(13-02-13 09:00:53)
    1-1:2.8.2*255(000000.000*kWh)(13-02-13 09:00:53)
    1-1:2.8.3*255(000000.000*kWh)(13-02-13 09:00:53)
    1-1:2.8.4*255(000000.000*kWh)(13-02-13 09:00:53)
    0-1:96.14.0*255(01)(13-02-13 09:00:53)
    1-1:15.7.0*255(000000*W)(13-02-13 09:00:53)
    0-1:17.0.0*255(0000*A)
    0-1:96.3.10*255(1)

    mit Deinem alten Script bekomme ich bereits alle Daten von beiden Zähler in den $buffer rein. Beispielhaft für Zähler 1 lautet das Ergebnis dann:

    Obiskey: 1-0:1.8.1*255(
    Position: 83
    14
    1-0:1.8.1*255: 000617.072 und landet im rrd. Im alten Script gibt es aber die GA Implementierung noch nicht und du hast dort mit $buffer Variablen gearbeitet. Im aktuellen Script dagegen mit @buffer Variablen. Ich kann den Teil für das Auslesen nicht ohne Fehler vom alten Script in das neue Übernehmen. Hier bräuchte ich mal noch einen Denkanstoß.

    Grüße,
    Mike

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Zitat von mikeeyy Beitrag anzeigen
    na ja. mein Problem ist ja immer noch, dass mein Zähler alle 10 Sekunden selber sendet...
    Hallo Mike,

    jetzt muss ich gestehen dass ich Deinen Beitrag mit Frank verwechselt habe ... ich erinnere mich wieder ... Sorry
    Aber Du hattest zusätzlich noch die "beiden" Zähler in einem oder wie war das?
    Ich hätte da evtl. eine Idee wie das gehen könnte. Man müsste da nur ein paar Zeilen schieben ... Ich versuche das die Tage mal.

    Eigentlich müssten wir einfach nur die while-Schleife 2x durchlaufen so dass wir das
    Code:
    if ($buffer =~ /\Q$endsign\E/)
    zweimal erreichen.
    Damit ist gewährleistet dass wir den vollständigen Datensatz erhalten haben. Die Verarbeitung danach ist dann quasi analog zu den neuesten Scripten.


    @Frank
    Die Probleme waren eher sporadisch, aber gut wenn es geht!
    Schieb das doch mal ins SVN wenn es geht ... ich hab nämlich wegen ebus keine Zeit (Lust)

    Einen Kommentar schreiben:


  • Frank0207
    antwortet
    Jetzt läuft es...

    ...sogar richtig schnell.

    Hallo JuMi,

    habe Dein Script von gestern mit der Abfrage aus der 0.1.6 verheiratet und kann meine Daten mit der schnellen Geschwindigkeit aus dem Elster AS1440 auslesen. Er updatet mir jetzt auch am Gesamtstand sofort die erste Kommastelle. Werde das jetzt ne Weile beobachten!
    Evtl. verhält sich da mein Zähler etwas anders wie Deiner. Ein Problem mit meinem Auslesekopf (ist der USB Lese- Schreibekopf von volkszaehler.org) konnte ich auch nicht feststellen. Besten Dank nochmals für Deine / eure Unterstützung. Werde mich jetzt mal wieder um die WP (eBus) kümmern

    Grüße

    Frank

    Einen Kommentar schreiben:


  • mikeeyy
    antwortet
    AW: Zählerabfrage als Wiregate Plugin

    na ja. mein Problem ist ja immer noch, dass mein Zähler alle 10 Sekunden selber sendet und das funzt mit der socat Abfrage nicht. die while Schleife im gepostet Script macht das aber perfekt. ohne die werde ich es wohl nicht zum laufen bekommen, denke ich.

    Grüße
    Mike

    Gesendet von meinem GT-P1000 mit Tapatalk 2

    Einen Kommentar schreiben:


  • JuMi2006
    antwortet
    Halt, Stop, Langsam

    Ist doch schon alles fertig. Nimm das Script hier:

    HIER KLICKEN:
    https://knx-user-forum.de/292600-post261.html

    Und nur das !!!

    Dort passt Du unter Channels DEINE Werte an die Du auslesen möchtest (Schema liegt ja vor). Und trägst noch die richtige Geschwindigkeit, Schnittstelle etc. ein.
    Also fasse nur den Bereich bis ### ENDE KONFIGURATION ### an.

    Fertig!

    Was ist daran so schwierig?

    Alles andere ist alter Tobak.

    Einen Kommentar schreiben:

Lädt...
X