So ... da isses: https://knx-user-forum.de/352354-post88.html
Ankündigung
Einklappen
Keine Ankündigung bisher.
Zählerabfrage als Wiregate Plugin
Einklappen
Dieses Thema ist geschlossen.
X
X
-
Zitat von JuMi2006 Beitrag anzeigenVersuchs mal hiermit....klingt nach SML-Protokoll, oder den Thread mal nach SML durchsuchen.
Open Automation / Code / [r1702] /tools/sml-meter/sml_meter.pl
Danke
Kommentar
-
Normalerweise bekommst du per SML Protokoll den aktuellen Zäherstand. Da muss nichts hochgezählt werden.
Ich hänge mal meine aktuelle Version dran. Ich hatte seinerzeit das PlugIn erweitert, so dass weniger Konfiguration nötig ist.
Ich muss mir doch mal meinen Account bei sourceforge freischalten lassen, damit der ganze Kram nicht verloren geht
Außerdem benutze ich kein COUNTER RRD sonder DERIVE. Mit den COUNTER RRDs hatte ich Probleme mit Wertüberläufen.
Ach ja, das Ganze läuft als Crontab, nicht als wiregate PlugIn.
Gruß
Code:#!/usr/bin/perl # # Autor: coolrunnings / www.knx-user-forum.de # Based on the PlugIn sml-meter by JuMi2006 / www.knx-user-forum.de # knx_write sub: makki / www.knx-user-forum.de # Version: 0.2 # Datum: 03.11.2013 use warnings; use strict; use Device::SerialPort; use feature "switch"; use EIBConnection; use RRDs; use Scalar::Util qw(looks_like_number); ##################################################################### # define everything here 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/ttyUSB0"; my $rrdpath = "/var/www/rrd"; my @obis; push @obis,{obis=>"1.8.0", ga =>"9/0/0", dpt => 14, rrd_name => "zaehler_verbrauch"}; push @obis,{obis=>"16.7.0", ga =>"9/0/1", dpt => 9, rrd_name => "zaehler_leistung" }; push @obis,{obis=>"36.7.0", ga =>"9/0/2", dpt => 9, rrd_name => "zaehler_leistung_L1"}; push @obis,{obis=>"56.7.0", ga =>"9/0/3", dpt => 9, rrd_name => "zaehler_leistung_L2"}; push @obis,{obis=>"76.7.0", ga =>"9/0/4", dpt => 9, rrd_name => "zaehler_leistung_L3"}; my @countermodes = (5,15,60,1440); #Aufloesungen fuer COUNTER RRDs in Minuten (1440 = Tagesverbrauch) my @derivemodes = (5,15,60,1440); #Aufloesungen fuer DERIVE RRDs in Minuten (1440 = Tagesverbrauch) my $debug = "1"; my $logging = "1"; 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 ##################################################################### # don't touch anything after this point my $filename = "/tmp/sml_meter.log"; if ( $logging == "1" ) {open (LOG, ">>$filename") or $logging = "0";} if ( $logging == "1" ) {print LOG "===============================================\n";} if ( $logging == "1" ) {print LOG (localtime)." Start new run\n";} my ($x,$rawdata) = 0 ; my $count = 0; my $saw = 0; my $start = 0; if ($debug == 1) {print "Step 1 - Collect data \n";} if ($logging == 1) {print LOG "Step 1 - Collect data \n";} while ($start < 2) { # wait for second 1B1B1B1B01010101 ($count,$saw)=$port->read(512); # will read 512 chars if ($count == 512) { # 512 chars read? $x = uc(unpack('H*',$saw)); # nach hex wandeln $rawdata .= $x; if ($rawdata =~ /1B1B1B1B01010101/) {$start ++}; } # if } # while if ($debug==1) {print "Step 2 - Reg Exp 1 build dataset \n";} if ($logging==1) {print LOG "Step 2 - Reg Exp 1 build dataset \n";} $rawdata =~ m/1B1B1B1B01010101(.*?)B1B1B1/; #print $rawdata ."\n"; if ($debug==1) {print "Step 3 - Analyze data \n";} if ($logging==1) {print LOG "Step 3 - Analyze data \n";} # find OBIS values in raw data foreach my $obiscnt (@obis) { if ( $debug == 1 ) {print "\n";} if ( $logging == 1 ) {print LOG "-----------------------------------------------\n";} my $obissearch = &obis2search($obiscnt->{obis}); $rawdata =~ m/$obissearch(.*?)017707/; my $obisdata = $1."01"; print "found: ".$obisdata."\n"; my $smlStatus = ""; my $smlValTime = ""; my $smlUnit = ""; my $smlScaler = ""; my $smlValue = ""; my $smlValueS = ""; ## check status if ( $debug == 1 ) {print "check sml status: ";} if ( $logging == 1 ) {print LOG "check sml status: ";} $smlStatus = &parseOBIS(\$obisdata); ## check value time if ( $debug == 1 ) {print "check sml value time: ";} if ( $logging == 1 ) {print LOG "check sml value time: ";} $smlValTime = &parseOBIS(\$obisdata); ## check unit if ( $debug == 1 ) {print "check sml unit: ";} if ( $logging == 1 ) {print LOG "check sml unit: ";} $smlUnit = &parseOBIS(\$obisdata); ## check scaler if ( $debug == 1 ) {print "check sml scaler: ";} if ( $logging == 1 ) {print LOG "check sml scaler: ";} $smlScaler = &parseOBIS(\$obisdata); ## check value if ( $debug == 1 ) {print "check sml value: ";} if ( $logging == 1 ) {print LOG "check sml value: ";} $smlValue = &parseOBIS(\$obisdata); ## check value signature if ( $debug == 1 ) {print "check sml value signature: ";} if ( $logging == 1 ) {print LOG "check sml value signature: ";} $smlValueS = &parseOBIS(\$obisdata); #print "done: ".$obisdata."\n"; #print $smlStatus; # Calculate value if ( $smlValue ne "" ) { my $calcvalue = ($smlValue * (10**$smlScaler)); # Modify value depening on unit my $smlUnitCfg_ref = &getSMLUnitStr($smlUnit); my @smlUnitCfg = @{$smlUnitCfg_ref}; if ( (@smlUnitCfg) == 2 ) { my $tUnitStr = $smlUnitCfg[0]; my $tUnitRRD = $smlUnitCfg[1]; # Wh to kWh if ( $smlUnit == 30 ) { $calcvalue = $calcvalue * 0.001; $tUnitStr = "kWh"; } if ( $debug == 1 ) {print "Unit config: [".$tUnitStr."][".$tUnitRRD."]\n";} if ( $logging == 1 ) {print LOG "Unit config: [".$tUnitStr."][".$tUnitRRD."]\n";} if ( $debug == 1 ) {print "Final value: [".$calcvalue." ".$tUnitStr."]\n";} if ( $logging == 1 ) {print LOG "Final value: [".$calcvalue." ".$tUnitStr."]\n";} # write rrd if ($tUnitRRD =~ m/c/) { &rrd_counter ($obiscnt->{rrd_name},$calcvalue) } if ($tUnitRRD =~ m/d/) { &rrd_derive ($obiscnt->{rrd_name},$calcvalue) } if ($tUnitRRD =~ m/g/) { &rrd_gauge ($obiscnt->{rrd_name},$calcvalue) } # send value to bus &knx_write ($obiscnt->{ga},$calcvalue,$obiscnt->{dpt}); if ($debug == 1) {print "GA:".$obiscnt->{ga}." value:".$calcvalue." DPT:".$obiscnt->{dpt}."\n";} if ($logging == 1) {print LOG "GA:".$obiscnt->{ga}." value:".$calcvalue." DPT:".$obiscnt->{dpt}."\n";} } else { if ( $debug == 1 ) {print "No unit found. No data will be written.\n";} if ( $logging == 1 ) {print LOG "No unit found. No data will be written.\n";} } } } $port->close() || warn "Serial port did not close proper!\n"; undef $port; if ( $logging == "1" ) {close LOG;} ## subs ## ##################################################################### # convert OBIS to search parameter sub obis2search { my ($obisid) = @_; my $res = "77070100"; if ($debug==1) { print "OBIS ID: ".$obisid."\n";} if ($logging==1) { print LOG "OBIS ID: ".$obisid."\n";} foreach my $c ( split(/\./,$obisid) ) { $res .= sprintf("%02X",$c); } $res .= "FF"; if ($debug==1) { print "OBIS search: ".$res."\n";} if ($logging==1) { print LOG "OBIS search: ".$res."\n";} return $res; } ##################################################################### # parse OBIS data sub parseOBIS { #my ($obisdata) = @_; my $r_obisdata = shift; my $res = ""; if ( length($$r_obisdata) < 2 ) { print "error --> data too short\n"; return $res; } my $smlDataTypeID = substr($$r_obisdata,0,2); if ( $smlDataTypeID == "01" ) { ## no data if ( $debug == 1 ) {print "[empty]\n";} if ( $logging == 1 ) {print LOG "[empty]\n";} $$r_obisdata =~ s/^..//; # remove first 2 characters return $res; } else { #if ( $debug == 1 ) {print "found something ";} #if ( $logging == 1 ) {print LOG "found something ";} my $smlDataType = substr($smlDataTypeID,0,1); my $smlIntBitCode = substr($smlDataTypeID,1,1); # only signed and unsigned int possible if ( $smlDataType != "5" && $smlDataType != "6") { if ( $debug == 1 ) {print "Invalid datatype [".$smlDataType."]\n"; }; if ( $logging == 1 ) {print LOG "Invalid datatype [".$smlDataType."]\n"; }; return $res; } $$r_obisdata =~ s/^..//; # remove first 2 characters my $smlIntBitCnt = 0; #switch ( $smlIntBitCode ) { # case 2 { $smlIntBitCnt = 8 } # case 3 { $smlIntBitCnt = 16 } # case 5 { $smlIntBitCnt = 32 } # case 9 { $smlIntBitCnt = 64 } # else { if ( $debug == 1 ) {print "Invalid integer bit count [".$smlIntBitCode."]\n"; }; # return $res; # } #} if ( $smlIntBitCode == 2 ) { $smlIntBitCnt = 8 } elsif ( $smlIntBitCode == 3 ) { $smlIntBitCnt = 16 } elsif ( $smlIntBitCode == 5 ) { $smlIntBitCnt = 32 } elsif ( $smlIntBitCode == 9 ) { $smlIntBitCnt = 64 } else { if ( $debug == 1 ) {print "Invalid integer bit count [".$smlIntBitCode."]\n"; }; if ( $logging == 1 ) {print LOG "Invalid integer bit count [".$smlIntBitCode."]\n"; }; return $res; } my $smlIntCharCnt = ($smlIntBitCnt/4); # check that the rest of the string is long enought for the bit count if ( (length($$r_obisdata)) < $smlIntCharCnt ) { if ( $debug == 1 ) {print "String not long enough for for detected bit count [".$smlIntCharCnt."]\n"; }; if ( $logging == 1 ) {print LOG "String not long enough for for detected bit count [".$smlIntCharCnt."]\n"; }; return $res; } # get the hex values my $hexval = substr($$r_obisdata,0,$smlIntCharCnt); $$r_obisdata = substr($$r_obisdata,$smlIntCharCnt); # if signed, get 2' complement if ( $smlDataType == 5 ) { my $hexbin = sprintf("%0${smlIntBitCnt}b",hex($hexval)); if ( substr($hexbin,0,1) == 1 ) { #if ( $debug == 1 ) { print "2's complement \n"; } #if ( $logging == 1 ) { print LOG "2's complement \n"; } my $hexinv = sprintf("%08x",~hex($hexval)); $res = (substr($hexinv,6,2)+1)*(-1); } else { $res = hex($hexval); } } else { $res = hex($hexval); } if ( $debug == 1 ) { print "[".$res."]\n";} if ( $logging == 1 ) { print LOG "[".$res."]\n";} return $res; } return $res; } ##################################################################### # get SML unit string depending on value # DLMS Units as specified in ISO EN 62056-62 and used by SML # original values from unit.h in vzlogger project # The volkszaehler.org project sub getSMLUnitStr { my ($smlUnitCode) = @_; my %unitmap = ( # code # unit #rrd # quantity #name #SI definition 1 => [ "a", "g" ], # time year 52*7*24*60*60 s 2 => [ "mo", "g" ], # time month 31*24*60*60 s 3 => [ "wk", "g" ], # time week 7*24*60*60 s 4 => [ "d", "g" ], # time day 24*60*60 s 5 => [ "h", "g" ], # time hour 60*60 s 6 => [ "min.", "g" ], # time min 60 s 7 => [ "s", "g" ], # time (t) second s 8 => [ "°", "g" ], # (phase) angle degree rad*180/π 9 => [ "°C", "g" ], # temperature (T) degree celsius K-273.15 10 => [ "currency", "g" ], # (local) currency 11 => [ "m", "g" ], # length (l) metre m 12 => [ "m/s", "g" ], # speed (v) metre per second m/s 13 => [ "m³", "g" ], # volume (V) cubic metre m³ 14 => [ "m³", "g" ], # corrected volume cubic metre m³ 15 => [ "m³/h", "g" ], # volume flux cubic metre per hour m³/(60*60s) 16 => [ "m³/h", "g" ], # corrected volume flux cubic metre per hour m³/(60*60s) 17 => [ "m³/d", "g" ], # volume flux m³/(24*60*60s) 18 => [ "m³/d", "g" ], # corrected volume flux m³/(24*60*60s) 19 => [ "l", "g" ], # volume litre 10-3 m³ 20 => [ "kg", "g" ], # mass (m) kilogram 21 => [ "N", "g" ], # force (F) newton 22 => [ "Nm", "g" ], # energy newtonmeter J = Nm = Ws 23 => [ "Pa", "g" ], # pressure (p) pascal N/m² 24 => [ "bar", "g" ], # pressure (p) bar 10⁵ N/m² 25 => [ "J", "g" ], # energy joule J = Nm = Ws 26 => [ "J/h", "g" ], # thermal power joule per hour J/(60*60s) 27 => [ "W", "g" ], # active power (P) watt W = J/s 28 => [ "VA", "g" ], # apparent power (S) volt-ampere 29 => [ "var", "g" ], # reactive power (Q) var 30 => [ "Wh", "cdg" ], # active energy watt-hour W*(60*60s) 31 => [ "VAh", "g" ], # apparent energy volt-ampere-hour VA*(60*60s) 32 => [ "varh", "g" ], # reactive energy var-hour var*(60*60s) 33 => [ "A", "g" ], # current (I) ampere A 34 => [ "C", "g" ], # electrical charge (Q) coulomb C = As 35 => [ "V", "g" ], # voltage (U) volt V 36 => [ "V/m", "g" ], # electr. field strength (E) volt per metre 37 => [ "F", "g" ], # capacitance (C) farad C/V = As/V 38 => [ "Ω", "g" ], # resistance (R) ohm Ω = V/A 39 => [ "Ωm²/m", "g" ], # resistivity (ρ) Ωm 40 => [ "Wb", "g" ], # magnetic flux (Φ) weber Wb = Vs 41 => [ "T", "g" ], # magnetic flux density (B) tesla Wb/m2 42 => [ "A/m", "g" ], # magnetic field strength (H) ampere per metre A/m 43 => [ "H", "g" ], # inductance (L) henry H = Wb/A 44 => [ "Hz", "g" ], # frequency (f => ω) hertz 1/s 45 => [ "1/(Wh)", "g" ], # R_W (Active energy meter constant or pulse value) 46 => [ "1/(varh)", "g" ], # R_B (reactive energy meter constant or pulse value) 47 => [ "1/(VAh)", "g" ], # R_S (apparent energy meter constant or pulse value) 48 => [ "V²h", "g" ], # volt-squared hour ´ volt-squaredhours V²(60*60s) 49 => [ "A²h", "g" ], # ampere-squared hour ampere-squaredhours A²(60*60s) 50 => [ "kg/s", "g" ], # mass flux kilogram per second kg/s 51 => [ "S => mho", "g" ], # conductance siemens 1/Ω 52 => [ "K", "g" ], # temperature (T) kelvin 53 => [ "1/(V²h)", "g" ], # R_U²h (Volt-squared hour meter constant or pulse value) 54 => [ "1/(A²h)", "g" ], # R_I²h (Ampere-squared hour meter constant or pulse value) 55 => [ "1/m³", "g" ], # R_V => meter constant or pulse value (volume) 56 => [ "%", "g" ], # percentage % 57 => [ "Ah", "g" ], # ampere-hours ampere-hour 60 => [ "Wh/m³", "g" ], # energy per volume 3,6*103 J/m³ 61 => [ "J/m³", "g" ], # calorific value, wobbe 62 => [ "Mol %", "g" ], # molar fraction of mole percent (Basic gas composition unit) # gas composition 63 => [ "g/m³", "g" ], # mass density, quantity of material (Gas analysis => accompanying elements) 64 => [ "Pa s", "g" ], # dynamic viscosity pascal second (Characteristic of gas stream) 253 => [ "(reserved)", "g"], # reserved 254 => [ "(other)", "g" ], # other unit 255 => [ "(unitless)", "g"] # no unit, unitless, count ); #print "-------- unit ".$unitmap{$smlUnitCode}[0]."\n"; #print "-------- unit ".$unitmap{$smlUnitCode}."\n"; my $unitarray_ref = $unitmap{$smlUnitCode}; return $unitarray_ref; } ##################################################################### 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; } ##################################################################### sub rrd_counter { if ($debug==1){print ("COUNTER","\n")}; if ($logging==1){print LOG ("-- COUNTER ---","\n")}; my $obisname = $_[0]; if ($debug==1){print "obisname ".$obisname."\n";} if ($logging==1){print LOG "obisname ".$obisname."\n";} my $value = $_[1]; if ($debug==1){print "value ".$value."\n";} if ($logging==1){print LOG "value ".$value."\n";} foreach (@countermodes) { my $rrdname = $obisname."_".$_."_c\.rrd"; if ($debug==1){print "rrdname ".$rrdname."\n"}; if ($logging==1){print LOG "----------\n"}; if ($logging==1){print LOG "rrdname ".$rrdname."\n"}; my $rrdfile = $rrdpath."\/".$rrdname; unless (-e $rrdfile) { RRDs::create ($rrdfile,"DS:value:COUNTER:".(($_*60)+600).":0:U", "RRA:AVERAGE:0.5:1:366", "RRA:AVERAGE:0.5:7:1308", "-s ".($_*60)); # step } my $countervalue = int($value*$_*60); if ($debug==1){print "[".$value."]*[".$_."]*[60] = ".$countervalue." countervalue \n";} if ($logging==1){print LOG "[".$value."]*[".$_."]*[60] = ".$countervalue." countervalue \n";} RRDs::update("$rrdfile", "N:$countervalue"); } } sub rrd_derive { if ($debug==1){print ("DERIVE","\n")}; if ($logging==1){print LOG ("-- DERIVE ---","\n")}; my $obisname = $_[0]; if ($debug==1){print "obisname ".$obisname."\n";} if ($logging==1){print LOG "obisname ".$obisname."\n";} my $value = $_[1]; if ($debug==1){print "value ".$value."\n";} if ($logging==1){print LOG "value ".$value."\n";} foreach (@derivemodes) { my $rrdname = $obisname."_".$_."_d\.rrd"; if ($debug==1){print "rrdname ".$rrdname."\n"}; if ($logging==1){print LOG "----------\n"}; if ($logging==1){print LOG "rrdname ".$rrdname."\n"}; my $rrdfile = $rrdpath."\/".$rrdname; unless (-e $rrdfile) { RRDs::create ($rrdfile,"DS:value:DERIVE:".(($_*60)+600).":0:U", "RRA:AVERAGE:0.5:1:366", "RRA:AVERAGE:0.5:7:1308", "-s ".($_*60)); # step } my $derivevalue = int($value*$_*60); if ($debug==1){print "[".$value."]*[".$_."]*[60] = ".$derivevalue." derivevalue \n";} if ($logging==1){print LOG "[".$value."]*[".$_."]*[60] = ".$derivevalue." derivevalue \n";} RRDs::update("$rrdfile", "N:$derivevalue"); } } ##################################################################### sub rrd_gauge { if ($debug==1){print ("GAUGE","\n")}; if ($logging==1){print LOG ("-- GAUGE --","\n")}; my $obisname = $_[0]; if ($debug==1){print "obisname ".$obisname."\n";} if ($logging==1){print LOG "obisname ".$obisname."\n";} my $value = $_[1]; if ($debug==1){print "value ".$value."\n";} if ($logging==1){print LOG "value ".$value."\n";} my $rrdname = $obisname."_g\.rrd"; if ($debug==1){print "rrdname ".$rrdname."\n"}; if ($logging==1){print LOG "rrdname ".$rrdname."\n"}; my $rrdfile = $rrdpath."\/".$rrdname; unless (-e $rrdfile) { RRDs::create ($rrdfile,"DS:value:GAUGE:900:0:U", "RRA:AVERAGE:0.5:1:2016", # 5 minutes for 7 days "RRA:AVERAGE:0.5:6:1488", # 30 minutes for 31 days "RRA:AVERAGE:0.5:12:4392", # 1 hour for 5 month "RRA:AVERAGE:0.5:72:7320", # 6 hours for 5 years "RRA:AVERAGE:0.5:2016:1308"); # 1 day for 25 years } RRDs::update("$rrdfile", "N:$value"); }
Kommentar
-
danke für den Code. Beim ausführen bekomme ich folgendes:
Code:wiregate:/etc/wiregate/plugin/generic/backup# perl zahler2 Step 1 - Collect data Step 2 - Reg Exp 1 build dataset Step 3 - Analyze data OBIS ID: 1.8.0 OBIS search: 77070100010800FF found: 6400018201621E52FF56000156AE9301 check sml status: Invalid integer bit count [4] check sml value time: Invalid datatype [0] check sml unit: Invalid datatype [0] check sml scaler: Invalid datatype [0] check sml value: Invalid datatype [0] check sml value signature: Invalid datatype [0] OBIS ID: 16.7.0 OBIS search: 77070100100700FF found: 0101621B52FF55000055A701 check sml status: [empty] check sml value time: [empty] check sml unit: [27] check sml scaler: [-1] check sml value: [21927] check sml value signature: [empty] Unit config: [W][g] Final value: [2192.7 W] GAUGE obisname zaehler_leistung value 2192.7 rrdname zaehler_leistung_g.rrd GA:9/0/1 value:2192.7 DPT:9 OBIS ID: 36.7.0 OBIS search: 77070100240700FF Use of uninitialized value $1 in concatenation (.) or string at zahler2 line 91. found: 01 check sml status: [empty] check sml value time: error --> data too short check sml unit: error --> data too short check sml scaler: error --> data too short check sml value: error --> data too short check sml value signature: error --> data too short OBIS ID: 56.7.0 OBIS search: 77070100380700FF Use of uninitialized value $1 in concatenation (.) or string at zahler2 line 91. found: 01 check sml status: [empty] check sml value time: error --> data too short check sml unit: error --> data too short check sml scaler: error --> data too short check sml value: error --> data too short check sml value signature: error --> data too short OBIS ID: 76.7.0 OBIS search: 770701004C0700FF Use of uninitialized value $1 in concatenation (.) or string at zahler2 line 91. found: 01 check sml status: [empty] check sml value time: error --> data too short check sml unit: error --> data too short check sml scaler: error --> data too short check sml value: error --> data too short check sml value signature: error --> data too short wiregate:/etc/wiregate/plugin/generic/backup#
Kommentar
-
Anbei:
Code:wiregate:/etc/wiregate/plugin/generic/backup# perl zahler2 Step 1 - Collect data Step 2 - Reg Exp 1 build dataset 51ACECC61C1B26FAEAB8FF0101016351AC00760700090153E827620062007263020171016322D4000000001B1B1B1B1A03DD611B1B1B1B01010101760700090153E8296200620072630101760101070009006F4D630B0901454D480000424ECD010163237C00760700090153E82A620062007263070177010B0901454D480000424ECD070100620AFFFF72620165006FC55D7777078181C78203FF0101010104454D480177070100000009FF010101010B0901454D480000424ECD0177070100010800FF6400018201621E52FF560001583F4A0177070100010801FF0101621E52FF560001583F4A0177070100010802FF0101621E52FF5600000000000177070100100700FF0101621B52FF55000055C50177078181C78205FF0172620165006FC55D01018302000AA1993A626C62D3345F4C95977A5262309183F335956147F25996456B2152F3FE8490C451ACECC61C1B26FAEAB8FF010101632BCD00760700090153E82D6200620072630201710163AAC3000000001B1B1B1B1A0319961B1B1B1B01010101760700090153E82F6200620072630101760101070009006F4D650B0901454D480000424ECD010163134900760700090153E830620062007263070177010B0901454D480000424ECD070100620AFFFF72620165006FC55E7777078181C78203FF0101010104454D480177070100000009FF010101010B0901454D480000424ECD0177070100010800FF6400018201621E52FF560001583F530177070100010801FF0101621E52FF560001583F530177070100010802FF0101621E52FF5600000000000177070100100700FF0101621B52FF55000056B50177078181C78205FF0172620165006FC55F01018302000AA1993A626C62D3345F4C95977A5262309183F335956147F25996456B2152F3FE8490C451ACECC61C1B26FAEAB8FF0101016319EF00760700090153E833620062007263020171016332FB000000001B1B1B1B1A0392FF1B1B1B1B01010101760700090153E8356200620072630101760101070009006F4D670B0901454D480000424ECD01016377B700760700090153E836620062007263070177010B0901454D480000424ECD070100620AFFFF72620165006FC5607777078181C78203FF0101010104454D480177070100000009FF010101010B0901454D480000424ECD0177070100010800FF6400018201621E52FF560001583F5C0177070100010801FF0101621E52FF560001583F5C0177070100010802FF0101621E52FF5600000000000177070100100700FF0101621B52FF55000057280177078181C78205FF0172620165006FC56001018302000AA1993A626C62D3345F4C95977A5262309183F335956147F25996456B2152F3FE8490C451ACECC61C1B26FAEAB8FF0101016390CB00760700090153E8396200 Step 3 - Analyze data OBIS ID: 1.8.0 OBIS search: 77070100010800FF found: 6400018201621E52FF560001583F4A01 check sml status: Invalid integer bit count [4] check sml value time: Invalid datatype [0] check sml unit: Invalid datatype [0] check sml scaler: Invalid datatype [0] check sml value: Invalid datatype [0] check sml value signature: Invalid datatype [0] OBIS ID: 16.7.0 OBIS search: 77070100100700FF found: 0101621B52FF55000055C501 check sml status: [empty] check sml value time: [empty] check sml unit: [27] check sml scaler: [-1] check sml value: [21957] check sml value signature: [empty] Unit config: [W][g] Final value: [2195.7 W] GAUGE obisname zaehler_leistung value 2195.7 rrdname zaehler_leistung_g.rrd GA:9/0/1 value:2195.7 DPT:9 OBIS ID: 36.7.0 OBIS search: 77070100240700FF Use of uninitialized value $1 in concatenation (.) or string at zahler2 line 93. found: 01 check sml status: [empty] check sml value time: error --> data too short check sml unit: error --> data too short check sml scaler: error --> data too short check sml value: error --> data too short check sml value signature: error --> data too short OBIS ID: 56.7.0 OBIS search: 77070100380700FF Use of uninitialized value $1 in concatenation (.) or string at zahler2 line 93. found: 01 check sml status: [empty] check sml value time: error --> data too short check sml unit: error --> data too short check sml scaler: error --> data too short check sml value: error --> data too short check sml value signature: error --> data too short OBIS ID: 76.7.0 OBIS search: 770701004C0700FF Use of uninitialized value $1 in concatenation (.) or string at zahler2 line 93. found: 01 check sml status: [empty] check sml value time: error --> data too short check sml unit: error --> data too short check sml scaler: error --> data too short check sml value: error --> data too short check sml value signature: error --> data too short wiregate:/etc/wiregate/plugin/generic/backup#
Kommentar
-
Was hast du denn für einen Zähler?
Ich habe mir mal die Daten angeschaut. Da kommen ganz komische Sachen rüber, z.B.:
Code:77 07 01 00 01 08 00 FF 64 00 01 82 01 62 1E 52 FF 56 00 01 58 3F 53 01
Die 3. Zeile ist der Status. Die 6 am Anfang besagt, dass es sich um einen unsigned Wert handelt. Die bit Anzahl danach darf aber nur 2 (8 bit), 3 (16 bit), 5 (32 bit) oder 9 (64 bit) sein. So steht es zumindest in der Norm.
Gruß
Kommentar
-
Zitat von coolrunnings Beitrag anzeigenWas hast du denn für einen Zähler?
Ich habe mir mal die Daten angeschaut. Da kommen ganz komische Sachen rüber, z.B.:
Code:77 07 01 00 01 08 00 FF 64 00 01 82 01 62 1E 52 FF 56 00 01 58 3F 53 01
Die 3. Zeile ist der Status. Die 6 am Anfang besagt, dass es sich um einen unsigned Wert handelt. Die bit Anzahl danach darf aber nur 2 (8 bit), 3 (16 bit), 5 (32 bit) oder 9 (64 bit) sein. So steht es zumindest in der Norm.
Gruß
von dem Zähler hab ich ein Foto angehangen.
Danke für die UnterstützungAngehängte Dateien
Kommentar
-
Also ich hab auch nen Zähler von der RWE, aber den ISKRA und nicht den EMH. Damit klappt es einwandfrei.
Ich hab den Code mal angepasst, so dass die 4 für 24 bit steht und 6 für 40 bit. Konnte darüber allerdings nichts in der Spezi finden. Dann sähen die OBIS Daten so aus:
Code:77 07 01 00 01 08 00 FF # ID 1-1.8.0 64 00 01 82 # Status 0x182 --> 386 01 # keine Zeit 62 1E # Unit 0x1E --> 30 --> Wh 52 FF # Scaler -1 --> value+10^-1 56 00 01 58 3F 53 # Value: 0x1583F53 --> 22560595 Wh --> 2256,0595 kWh 01 # keine value Signatur
Hier der Code:
Code:#!/usr/bin/perl # # Autor: coolrunnings / www.knx-user-forum.de # Based on the PlugIn sml-meter by JuMi2006 / www.knx-user-forum.de # knx_write sub: makki / www.knx-user-forum.de # Version: 0.2 # Datum: 03.11.2013 # Licenced under the GPLv3 use warnings; use strict; use Device::SerialPort; use feature "switch"; use EIBConnection; use RRDs; use Scalar::Util qw(looks_like_number); ##################################################################### # define everything here 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/ttyUSB0"; my $rrdpath = "/var/www/rrd"; my @obis; push @obis,{obis=>"1.8.0", ga =>"9/0/0", dpt => 14, rrd_name => "zaehler_verbrauch"}; push @obis,{obis=>"16.7.0", ga =>"9/0/1", dpt => 9, rrd_name => "zaehler_leistung" }; push @obis,{obis=>"36.7.0", ga =>"9/0/2", dpt => 9, rrd_name => "zaehler_leistung_L1"}; push @obis,{obis=>"56.7.0", ga =>"9/0/3", dpt => 9, rrd_name => "zaehler_leistung_L2"}; push @obis,{obis=>"76.7.0", ga =>"9/0/4", dpt => 9, rrd_name => "zaehler_leistung_L3"}; my @countermodes = (5,15,60,1440); #Aufloesungen fuer COUNTER RRDs in Minuten (1440 = Tagesverbrauch) my @derivemodes = (5,15,60,1440); #Aufloesungen fuer DERIVE RRDs in Minuten (1440 = Tagesverbrauch) my $debug = "1"; my $logging = "1"; 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 ##################################################################### # don't touch anything starting here my $filename = "/tmp/sml_meter.log"; if ( $logging == "1" ) {open (LOG, ">>$filename") or $logging = "0";} if ( $logging == "1" ) {print LOG "===============================================\n";} if ( $logging == "1" ) {print LOG (localtime)." Start new run\n";} my ($x,$rawdata) = 0 ; my $count = 0; my $saw = 0; my $start = 0; if ($debug == 1) {print "Step 1 - Collect data \n";} if ($logging == 1) {print LOG "Step 1 - Collect data \n";} while ($start < 2) { # wait for second 1B1B1B1B01010101 ($count,$saw)=$port->read(512); # will read 512 chars if ($count == 512) { # 512 chars read? $x = uc(unpack('H*',$saw)); # nach hex wandeln $rawdata .= $x; if ($rawdata =~ /1B1B1B1B01010101/) {$start ++}; } # if } # while if ($debug==1) {print "Step 2 - Reg Exp 1 build dataset \n";} if ($logging==1) {print LOG "Step 2 - Reg Exp 1 build dataset \n";} $rawdata =~ m/1B1B1B1B01010101(.*?)B1B1B1/; print $rawdata."\n"; if ($debug==1) {print "Step 3 - Analyze data \n";} if ($logging==1) {print LOG "Step 3 - Analyze data \n";} # find OBIS values in raw data foreach my $obiscnt (@obis) { if ( $debug == 1 ) {print "\n";} if ( $logging == 1 ) {print LOG "-----------------------------------------------\n";} my $obissearch = &obis2search($obiscnt->{obis}); $rawdata =~ m/$obissearch(.*?)017707/; my $obisdata = $1."01"; if ( $debug == 1 ) {print "found: ".$obisdata."\n";} if ( $logging == 1 ) {print LOG "found: ".$obisdata."\n";} my $smlStatus = ""; my $smlValTime = ""; my $smlUnit = ""; my $smlScaler = ""; my $smlValue = ""; my $smlValueS = ""; ## check status if ( $debug == 1 ) {print "check sml status: ";} if ( $logging == 1 ) {print LOG "check sml status: ";} $smlStatus = &parseOBIS(\$obisdata); ## check value time if ( $debug == 1 ) {print "check sml value time: ";} if ( $logging == 1 ) {print LOG "check sml value time: ";} $smlValTime = &parseOBIS(\$obisdata); ## check unit if ( $debug == 1 ) {print "check sml unit: ";} if ( $logging == 1 ) {print LOG "check sml unit: ";} $smlUnit = &parseOBIS(\$obisdata); ## check scaler if ( $debug == 1 ) {print "check sml scaler: ";} if ( $logging == 1 ) {print LOG "check sml scaler: ";} $smlScaler = &parseOBIS(\$obisdata); ## check value if ( $debug == 1 ) {print "check sml value: ";} if ( $logging == 1 ) {print LOG "check sml value: ";} $smlValue = &parseOBIS(\$obisdata); ## check value signature if ( $debug == 1 ) {print "check sml value signature: ";} if ( $logging == 1 ) {print LOG "check sml value signature: ";} $smlValueS = &parseOBIS(\$obisdata); #print "done: ".$obisdata."\n"; #print $smlStatus; # Calculate value if ( $smlValue ne "" ) { my $calcvalue = ($smlValue * (10**$smlScaler)); # Modify value depening on unit my $smlUnitCfg_ref = &getSMLUnitStr($smlUnit); my @smlUnitCfg = @{$smlUnitCfg_ref}; if ( (@smlUnitCfg) == 2 ) { my $tUnitStr = $smlUnitCfg[0]; my $tUnitRRD = $smlUnitCfg[1]; # Wh to kWh if ( $smlUnit == 30 ) { $calcvalue = $calcvalue * 0.001; $tUnitStr = "kWh"; } if ( $debug == 1 ) {print "Unit config: [".$tUnitStr."][".$tUnitRRD."]\n";} if ( $logging == 1 ) {print LOG "Unit config: [".$tUnitStr."][".$tUnitRRD."]\n";} if ( $debug == 1 ) {print "Final value: [".$calcvalue." ".$tUnitStr."]\n";} if ( $logging == 1 ) {print LOG "Final value: [".$calcvalue." ".$tUnitStr."]\n";} # write rrd if ($tUnitRRD =~ m/c/) { &rrd_counter ($obiscnt->{rrd_name},$calcvalue) } if ($tUnitRRD =~ m/d/) { &rrd_derive ($obiscnt->{rrd_name},$calcvalue) } if ($tUnitRRD =~ m/g/) { &rrd_gauge ($obiscnt->{rrd_name},$calcvalue) } # send value to bus &knx_write ($obiscnt->{ga},$calcvalue,$obiscnt->{dpt}); if ($debug == 1) {print "GA:".$obiscnt->{ga}." value:".$calcvalue." DPT:".$obiscnt->{dpt}."\n";} if ($logging == 1) {print LOG "GA:".$obiscnt->{ga}." value:".$calcvalue." DPT:".$obiscnt->{dpt}."\n";} } else { if ( $debug == 1 ) {print "No unit found. No data will be written.\n";} if ( $logging == 1 ) {print LOG "No unit found. No data will be written.\n";} } } } $port->close() || warn "Serial port did not close proper!\n"; undef $port; if ( $logging == "1" ) {close LOG;} ## subs ## ##################################################################### # convert OBIS to search parameter sub obis2search { my ($obisid) = @_; my $res = "77070100"; if ($debug==1) { print "OBIS ID: ".$obisid."\n";} if ($logging==1) { print LOG "OBIS ID: ".$obisid."\n";} foreach my $c ( split(/\./,$obisid) ) { $res .= sprintf("%02X",$c); } $res .= "FF"; if ($debug==1) { print "OBIS search: ".$res."\n";} if ($logging==1) { print LOG "OBIS search: ".$res."\n";} return $res; } ##################################################################### # parse OBIS data sub parseOBIS { #my ($obisdata) = @_; my $r_obisdata = shift; my $res = ""; if ( length($$r_obisdata) < 2 ) { print "error --> data too short\n"; return $res; } my $smlDataTypeID = substr($$r_obisdata,0,2); if ( $smlDataTypeID == "01" ) { ## no data if ( $debug == 1 ) {print "[empty]\n";} if ( $logging == 1 ) {print LOG "[empty]\n";} $$r_obisdata =~ s/^..//; # remove first 2 characters return $res; } else { #if ( $debug == 1 ) {print "found something ";} #if ( $logging == 1 ) {print LOG "found something ";} my $smlDataType = substr($smlDataTypeID,0,1); my $smlIntBitCode = substr($smlDataTypeID,1,1); # only signed and unsigned int possible if ( $smlDataType != "5" && $smlDataType != "6") { if ( $debug == 1 ) {print "Invalid datatype [".$smlDataType."]\n"; }; if ( $logging == 1 ) {print LOG "Invalid datatype [".$smlDataType."]\n"; }; return $res; } $$r_obisdata =~ s/^..//; # remove first 2 characters my $smlIntBitCnt = 0; #switch ( $smlIntBitCode ) { # case 2 { $smlIntBitCnt = 8 } # case 3 { $smlIntBitCnt = 16 } # case 5 { $smlIntBitCnt = 32 } # case 9 { $smlIntBitCnt = 64 } # else { if ( $debug == 1 ) {print "Invalid integer bit count [".$smlIntBitCode."]\n"; }; # return $res; # } #} if ( $smlIntBitCode == 2 ) { $smlIntBitCnt = 8 } elsif ( $smlIntBitCode == 3 ) { $smlIntBitCnt = 16 } elsif ( $smlIntBitCode == 4 ) { # not conform with the SML specification! $smlIntBitCnt = 24 } elsif ( $smlIntBitCode == 5 ) { $smlIntBitCnt = 32 } elsif ( $smlIntBitCode == 6 ) { # not conform with the SML specification! $smlIntBitCnt = 40 } elsif ( $smlIntBitCode == 9 ) { $smlIntBitCnt = 64 } else { if ( $debug == 1 ) {print "Invalid integer bit count [".$smlIntBitCode."]\n"; }; if ( $logging == 1 ) {print LOG "Invalid integer bit count [".$smlIntBitCode."]\n"; }; return $res; } my $smlIntCharCnt = ($smlIntBitCnt/4); # check that the rest of the string is long enought for the bit count if ( (length($$r_obisdata)) < $smlIntCharCnt ) { if ( $debug == 1 ) {print "String not long enough for for detected bit count [".$smlIntCharCnt."]\n"; }; if ( $logging == 1 ) {print LOG "String not long enough for for detected bit count [".$smlIntCharCnt."]\n"; }; return $res; } # get the hex values my $hexval = substr($$r_obisdata,0,$smlIntCharCnt); $$r_obisdata = substr($$r_obisdata,$smlIntCharCnt); # if signed, get 2' complement if ( $smlDataType == 5 ) { my $hexbin = sprintf("%0${smlIntBitCnt}b",hex($hexval)); if ( substr($hexbin,0,1) == 1 ) { #if ( $debug == 1 ) { print "2's complement \n"; } #if ( $logging == 1 ) { print LOG "2's complement \n"; } my $hexinv = sprintf("%08x",~hex($hexval)); $res = (substr($hexinv,6,2)+1)*(-1); } else { $res = hex($hexval); } } else { $res = hex($hexval); } if ( $debug == 1 ) { print "[".$res."]\n";} if ( $logging == 1 ) { print LOG "[".$res."]\n";} return $res; } return $res; } ##################################################################### # get SML unit string depending on value # DLMS Units as specified in ISO EN 62056-62 and used by SML # original values from unit.h in vzlogger project # The volkszaehler.org project sub getSMLUnitStr { my ($smlUnitCode) = @_; my %unitmap = ( # code # unit #rrd # quantity #name #SI definition 1 => [ "a", "g" ], # time year 52*7*24*60*60 s 2 => [ "mo", "g" ], # time month 31*24*60*60 s 3 => [ "wk", "g" ], # time week 7*24*60*60 s 4 => [ "d", "g" ], # time day 24*60*60 s 5 => [ "h", "g" ], # time hour 60*60 s 6 => [ "min.", "g" ], # time min 60 s 7 => [ "s", "g" ], # time (t) second s 8 => [ "°", "g" ], # (phase) angle degree rad*180/π 9 => [ "°C", "g" ], # temperature (T) degree celsius K-273.15 10 => [ "currency", "g" ], # (local) currency 11 => [ "m", "g" ], # length (l) metre m 12 => [ "m/s", "g" ], # speed (v) metre per second m/s 13 => [ "m³", "g" ], # volume (V) cubic metre m³ 14 => [ "m³", "g" ], # corrected volume cubic metre m³ 15 => [ "m³/h", "g" ], # volume flux cubic metre per hour m³/(60*60s) 16 => [ "m³/h", "g" ], # corrected volume flux cubic metre per hour m³/(60*60s) 17 => [ "m³/d", "g" ], # volume flux m³/(24*60*60s) 18 => [ "m³/d", "g" ], # corrected volume flux m³/(24*60*60s) 19 => [ "l", "g" ], # volume litre 10-3 m³ 20 => [ "kg", "g" ], # mass (m) kilogram 21 => [ "N", "g" ], # force (F) newton 22 => [ "Nm", "g" ], # energy newtonmeter J = Nm = Ws 23 => [ "Pa", "g" ], # pressure (p) pascal N/m² 24 => [ "bar", "g" ], # pressure (p) bar 10⁵ N/m² 25 => [ "J", "g" ], # energy joule J = Nm = Ws 26 => [ "J/h", "g" ], # thermal power joule per hour J/(60*60s) 27 => [ "W", "g" ], # active power (P) watt W = J/s 28 => [ "VA", "g" ], # apparent power (S) volt-ampere 29 => [ "var", "g" ], # reactive power (Q) var 30 => [ "Wh", "cdg" ], # active energy watt-hour W*(60*60s) 31 => [ "VAh", "g" ], # apparent energy volt-ampere-hour VA*(60*60s) 32 => [ "varh", "g" ], # reactive energy var-hour var*(60*60s) 33 => [ "A", "g" ], # current (I) ampere A 34 => [ "C", "g" ], # electrical charge (Q) coulomb C = As 35 => [ "V", "g" ], # voltage (U) volt V 36 => [ "V/m", "g" ], # electr. field strength (E) volt per metre 37 => [ "F", "g" ], # capacitance (C) farad C/V = As/V 38 => [ "Ω", "g" ], # resistance (R) ohm Ω = V/A 39 => [ "Ωm²/m", "g" ], # resistivity (ρ) Ωm 40 => [ "Wb", "g" ], # magnetic flux (Φ) weber Wb = Vs 41 => [ "T", "g" ], # magnetic flux density (B) tesla Wb/m2 42 => [ "A/m", "g" ], # magnetic field strength (H) ampere per metre A/m 43 => [ "H", "g" ], # inductance (L) henry H = Wb/A 44 => [ "Hz", "g" ], # frequency (f => ω) hertz 1/s 45 => [ "1/(Wh)", "g" ], # R_W (Active energy meter constant or pulse value) 46 => [ "1/(varh)", "g" ], # R_B (reactive energy meter constant or pulse value) 47 => [ "1/(VAh)", "g" ], # R_S (apparent energy meter constant or pulse value) 48 => [ "V²h", "g" ], # volt-squared hour ´ volt-squaredhours V²(60*60s) 49 => [ "A²h", "g" ], # ampere-squared hour ampere-squaredhours A²(60*60s) 50 => [ "kg/s", "g" ], # mass flux kilogram per second kg/s 51 => [ "S => mho", "g" ], # conductance siemens 1/Ω 52 => [ "K", "g" ], # temperature (T) kelvin 53 => [ "1/(V²h)", "g" ], # R_U²h (Volt-squared hour meter constant or pulse value) 54 => [ "1/(A²h)", "g" ], # R_I²h (Ampere-squared hour meter constant or pulse value) 55 => [ "1/m³", "g" ], # R_V => meter constant or pulse value (volume) 56 => [ "%", "g" ], # percentage % 57 => [ "Ah", "g" ], # ampere-hours ampere-hour 60 => [ "Wh/m³", "g" ], # energy per volume 3,6*103 J/m³ 61 => [ "J/m³", "g" ], # calorific value, wobbe 62 => [ "Mol %", "g" ], # molar fraction of mole percent (Basic gas composition unit) # gas composition 63 => [ "g/m³", "g" ], # mass density, quantity of material (Gas analysis => accompanying elements) 64 => [ "Pa s", "g" ], # dynamic viscosity pascal second (Characteristic of gas stream) 253 => [ "(reserved)", "g"], # reserved 254 => [ "(other)", "g" ], # other unit 255 => [ "(unitless)", "g"] # no unit, unitless, count ); #print "-------- unit ".$unitmap{$smlUnitCode}[0]."\n"; #print "-------- unit ".$unitmap{$smlUnitCode}."\n"; my $unitarray_ref = $unitmap{$smlUnitCode}; return $unitarray_ref; } ##################################################################### 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; } ##################################################################### sub rrd_counter { if ($debug==1){print ("COUNTER","\n")}; if ($logging==1){print LOG ("-- COUNTER ---","\n")}; my $obisname = $_[0]; if ($debug==1){print "obisname ".$obisname."\n";} if ($logging==1){print LOG "obisname ".$obisname."\n";} my $value = $_[1]; if ($debug==1){print "value ".$value."\n";} if ($logging==1){print LOG "value ".$value."\n";} foreach (@countermodes) { my $rrdname = $obisname."_".$_."_c\.rrd"; if ($debug==1){print "rrdname ".$rrdname."\n"}; if ($logging==1){print LOG "----------\n"}; if ($logging==1){print LOG "rrdname ".$rrdname."\n"}; my $rrdfile = $rrdpath."\/".$rrdname; unless (-e $rrdfile) { RRDs::create ($rrdfile,"DS:value:COUNTER:".(($_*60)+600).":0:U", "RRA:AVERAGE:0.5:1:366", "RRA:AVERAGE:0.5:7:1308", "-s ".($_*60)); # step } my $countervalue = int($value*$_*60); if ($debug==1){print "[".$value."]*[".$_."]*[60] = ".$countervalue." countervalue \n";} if ($logging==1){print LOG "[".$value."]*[".$_."]*[60] = ".$countervalue." countervalue \n";} RRDs::update("$rrdfile", "N:$countervalue"); } } sub rrd_derive { if ($debug==1){print ("DERIVE","\n")}; if ($logging==1){print LOG ("-- DERIVE ---","\n")}; my $obisname = $_[0]; if ($debug==1){print "obisname ".$obisname."\n";} if ($logging==1){print LOG "obisname ".$obisname."\n";} my $value = $_[1]; if ($debug==1){print "value ".$value."\n";} if ($logging==1){print LOG "value ".$value."\n";} foreach (@derivemodes) { my $rrdname = $obisname."_".$_."_d\.rrd"; if ($debug==1){print "rrdname ".$rrdname."\n"}; if ($logging==1){print LOG "----------\n"}; if ($logging==1){print LOG "rrdname ".$rrdname."\n"}; my $rrdfile = $rrdpath."\/".$rrdname; unless (-e $rrdfile) { RRDs::create ($rrdfile,"DS:value:DERIVE:".(($_*60)+600).":0:U", "RRA:AVERAGE:0.5:1:366", "RRA:AVERAGE:0.5:7:1308", "-s ".($_*60)); # step } my $derivevalue = int($value*$_*60); if ($debug==1){print "[".$value."]*[".$_."]*[60] = ".$derivevalue." derivevalue \n";} if ($logging==1){print LOG "[".$value."]*[".$_."]*[60] = ".$derivevalue." derivevalue \n";} RRDs::update("$rrdfile", "N:$derivevalue"); } } ##################################################################### sub rrd_gauge { if ($debug==1){print ("GAUGE","\n")}; if ($logging==1){print LOG ("-- GAUGE --","\n")}; my $obisname = $_[0]; if ($debug==1){print "obisname ".$obisname."\n";} if ($logging==1){print LOG "obisname ".$obisname."\n";} my $value = $_[1]; if ($debug==1){print "value ".$value."\n";} if ($logging==1){print LOG "value ".$value."\n";} my $rrdname = $obisname."_g\.rrd"; if ($debug==1){print "rrdname ".$rrdname."\n"}; if ($logging==1){print LOG "rrdname ".$rrdname."\n"}; my $rrdfile = $rrdpath."\/".$rrdname; unless (-e $rrdfile) { RRDs::create ($rrdfile,"DS:value:GAUGE:900:0:U", "RRA:AVERAGE:0.5:1:2016", # 5 minutes for 7 days "RRA:AVERAGE:0.5:6:1488", # 30 minutes for 31 days "RRA:AVERAGE:0.5:12:4392", # 1 hour for 5 month "RRA:AVERAGE:0.5:72:7320", # 6 hours for 5 years "RRA:AVERAGE:0.5:2016:1308"); # 1 day for 25 years } RRDs::update("$rrdfile", "N:$value"); }
Kommentar
-
Super, klasse! Vielen Dank!
Kannst du bitte noch sagen wie oft du den Script über crontab ausführen lässt?
Anbei nun der output:
Code:wiregate:/etc/wiregate/plugin/generic/backup# perl zaehler3 Step 1 - Collect data Step 2 - Reg Exp 1 build dataset 1B1B1B1B01010101760700090154886D6200620072630101760101070009007082CF0B0901454D480000424ECD0101630B8900760700090154886E620062007263070177010B0901454D480000424ECD070100620AFFFF7262016500700BE27777078181C78203FF0101010104454D480177070100000009FF010101010B0901454D480000424ECD0177070100010800FF6400018001621E52FF56000158AFEF0177070100010801FF0101621E52FF56000158AFEF0177070100010802FF0101621E52FF5600000000000177070100100700FF0101621B52FF55000000C40177078181C78205FF017262016500700BE201018302000AA1993A626C62D3345F4C95977A5262309183F335956147F25996456B2152F3FE8490C451ACECC61C1B26FAEAB8FF01010163A24C00760700090154886F62006200726302017101631EC1000000001B1B1B1B1A0377C21B1B1B1B0101010176070009015488736200620072630101760101070009007082D10B0901454D480000424ECD010163FB68007607000901548874620062007263070177010B0901454D480000424ECD070100620AFFFF7262016500700BE37777078181C78203FF0101010104454D480177070100000009FF010101010B0901454D480000424ECD0177070100010800FF6400018201621E52FF56000158AFF00177070100010801FF0101621E52FF56000158AF1B1B1B1B0101010176070009015488D36200620072630101760101070009007082F10B0901454D480000424ECD01016370330076070009015488D4620062007263070177010B0901454D480000424ECD070100620AFFFF7262016500700C297777078181C78203FF0101010104454D480177070100000009FF010101010B0901454D480000424ECD0177070100010800FF6400018001621E52FF56000158AFF30177070100010801FF0101621E52FF56000158AFF30177070100010802FF0101621E52FF5600000000000177070100100700FF0101621B52FF55000000C90177078181C78205FF017262016500700C2901018302000AA1993A626C62D3345F4C95977A5262309183F335956147F25996456B2152F3FE8490C451ACECC61C1B26FAEAB8FF0101016395930076070009015488D5620062007263020171016342D7000000001B1B1B1B1A0322521B1B1B1B0101010176070009015488D96200620072630101760101070009007082F30B0901454D480000424ECD01016343830076070009015488DA620062007263070177010B0901454D480000424ECD070100620AFFFF7262016500700C2B7777078181C78203FF0101010104454D480177070100000009FF010101010B0901454D480000424ECD0177070100010800FF6400018201621E52FF56000158AFF40177070100010801FF0101621E52FF56000158AF Step 3 - Analyze data OBIS ID: 1.8.0 OBIS search: 77070100010800FF found: 6400018001621E52FF56000158AFEF01 check sml status: [384] check sml value time: [empty] check sml unit: [30] check sml scaler: [-1] check sml value: [22589423] check sml value signature: [empty] Unit config: [kWh][cdg] Final value: [2258.9423 kWh] COUNTER obisname zaehler_verbrauch value 2258.9423 rrdname zaehler_verbrauch_5_c.rrd [2258.9423]*[5]*[60] = 677682 countervalue rrdname zaehler_verbrauch_15_c.rrd [2258.9423]*[15]*[60] = 2033048 countervalue rrdname zaehler_verbrauch_60_c.rrd [2258.9423]*[60]*[60] = 8132192 countervalue rrdname zaehler_verbrauch_1440_c.rrd [2258.9423]*[1440]*[60] = 195172614 countervalue DERIVE obisname zaehler_verbrauch value 2258.9423 rrdname zaehler_verbrauch_5_d.rrd [2258.9423]*[5]*[60] = 677682 derivevalue rrdname zaehler_verbrauch_15_d.rrd [2258.9423]*[15]*[60] = 2033048 derivevalue rrdname zaehler_verbrauch_60_d.rrd [2258.9423]*[60]*[60] = 8132192 derivevalue rrdname zaehler_verbrauch_1440_d.rrd [2258.9423]*[1440]*[60] = 195172614 derivevalue GAUGE obisname zaehler_verbrauch value 2258.9423 rrdname zaehler_verbrauch_g.rrd GA:9/0/0 value:2258.9423 DPT:14 OBIS ID: 16.7.0 OBIS search: 77070100100700FF found: 0101621B52FF55000000C401 check sml status: [empty] check sml value time: [empty] check sml unit: [27] check sml scaler: [-1] check sml value: [196] check sml value signature: [empty] Unit config: [W][g] Final value: [19.6 W] GAUGE obisname zaehler_leistung value 19.6 rrdname zaehler_leistung_g.rrd GA:9/0/1 value:19.6 DPT:9 OBIS ID: 36.7.0 OBIS search: 77070100240700FF Use of uninitialized value $1 in concatenation (.) or string at zaehler3 line 91. found: 01 check sml status: [empty] check sml value time: error --> data too short check sml unit: error --> data too short check sml scaler: error --> data too short check sml value: error --> data too short check sml value signature: error --> data too short OBIS ID: 56.7.0 OBIS search: 77070100380700FF Use of uninitialized value $1 in concatenation (.) or string at zaehler3 line 91. found: 01 check sml status: [empty] check sml value time: error --> data too short check sml unit: error --> data too short check sml scaler: error --> data too short check sml value: error --> data too short check sml value signature: error --> data too short OBIS ID: 76.7.0 OBIS search: 770701004C0700FF Use of uninitialized value $1 in concatenation (.) or string at zaehler3 line 91. found: 01 check sml status: [empty] check sml value time: error --> data too short check sml unit: error --> data too short check sml scaler: error --> data too short check sml value: error --> data too short check sml value signature: error --> data too short
Kommentar
-
Zitat von mosjka1 Beitrag anzeigenSuper, klasse! Vielen Dank!
Kannst du bitte noch sagen wie oft du den Script über crontab ausführen lässt?
Zitat von mosjka1 Beitrag anzeigenCode:OBIS ID: 1.8.0 OBIS search: 77070100010800FF found: 6400018001621E52FF56000158AFEF01 check sml status: [384] check sml value time: [empty] check sml unit: [30] check sml scaler: [-1] check sml value: [22589423] check sml value signature: [empty] Unit config: [kWh][cdg] Final value: [2258.9423 kWh]
Code:@countermodes =();
Zitat von mosjka1 Beitrag anzeigenCode:OBIS ID: 16.7.0 OBIS search: 77070100100700FF found: 0101621B52FF55000000C401 check sml status: [empty] check sml value time: [empty] check sml unit: [27] check sml scaler: [-1] check sml value: [196] check sml value signature: [empty] Unit config: [W][g] Final value: [19.6 W]
Zitat von mosjka1 Beitrag anzeigenCode:OBIS ID: 36.7.0 OBIS ID: 76.7.0
Kommentar
-
Entschuldigt, dass ich mich hier dran hänge.
Ich habe zwei Leseköpfe von Udo an zwei EMH Zählern, die am Wiregate hängen. Ich habe extra ein 3.5.1-wiregate-1.41 Kernel installiert (installieren lassen), damit die cp210x usb-uart Wandler funktionieren.
Code:Bus 001 Device 009: ID 10c4:ea60 Cygnal Integrated Products, Inc. CP210x Composite Device Bus 001 Device 008: ID 10c4:ea60 Cygnal Integrated Products, Inc. CP210x Composite Device
Code:root@wiregate248:/home/user# socat - /dev/ttyUSBehzEMHHaupt,raw,echo=0,b9600,parenb=0,cs8,cstopb=1 ��oMbbrcv MPNbbrcw;Wc"xv EMH�p;Wb ��rbe���ww��ǂ�EMHw � EMH�p;Ww�d�bR�V��w�bR�V��w�bR�Vw�bR�U���
Code:root@wiregate248:/home/user# socat - /dev/ttyUSBehzEMHHaupt,raw,echo=0,b9600,parenb=0,cs8,cstopb=1 ��oMbbrcv MPNbbrcw;Wc"xv EMH�p;Wb ��rbe���ww��ǂ�EMHw � EMH�p;Ww�d�bR�V��w�bR�V��w�bR�Vw�bR�U���
Danke und Gruß Moritz
Kommentar
-
Zitat von coolrunnings Beitrag anzeigenAlle 5 Minuten.
Das sieht gut aus. Wie gesagt, du kannst die COUNTER RRDs noch deaktivieren. EinfachCode:@countermodes =();
Die würde ich noch aus der Config entfernen.
Ps.: der Lesekopf hängt an dem Zähler von der Luftwärmepumpe. Sie war gerade nicht im Betrieb. Wenn sie mal anläuft, sieht die Sache ganz anders aus, leider!
Wenn ich noch einen zweiten Lesekopf an den Haustromzähler dran machen möchte, erstelle ich dazu eine Kopie von diesem script und dort ändere ich die Schnittstelle ab, oder kann der zweite Zähler über den selben script laufen. Die Zähler sind identisch.
Kommentar
-
Zitat von mosjka1 Beitrag anzeigenPs.: der Lesekopf hängt an dem Zähler von der Luftwärmepumpe. Sie war gerade nicht im Betrieb.
Zitat von mosjka1 Beitrag anzeigenWenn ich noch einen zweiten Lesekopf an den Haustromzähler dran machen möchte, erstelle ich dazu eine Kopie von diesem script und dort ändere ich die Schnittstelle ab, oder kann der zweite Zähler über den selben script laufen. Die Zähler sind identisch.
Kommentar
-
Zitat von kleinklausi Beitrag anzeigenEntschuldigt, dass ich mich hier dran hänge.
Ich habe zwei Leseköpfe von Udo an zwei EMH Zählern, die am Wiregate hängen. Ich habe extra ein 3.5.1-wiregate-1.41 Kernel installiert (installieren lassen), damit die cp210x usb-uart Wandler funktionieren.
Code:Bus 001 Device 009: ID 10c4:ea60 Cygnal Integrated Products, Inc. CP210x Composite Device Bus 001 Device 008: ID 10c4:ea60 Cygnal Integrated Products, Inc. CP210x Composite Device
Code:root@wiregate248:/home/user# socat - /dev/ttyUSBehzEMHHaupt,raw,echo=0,b9600,parenb=0,cs8,cstopb=1 ��oMbbrcv MPNbbrcw;Wc"xv EMH�p;Wb ��rbe���ww��ǂ�EMHw � EMH�p;Ww�d�bR�V��w�bR�V��w�bR�Vw�bR�U���
Code:root@wiregate248:/home/user# socat - /dev/ttyUSBehzEMHHaupt,raw,echo=0,b9600,parenb=0,cs8,cstopb=1 ��oMbbrcv MPNbbrcw;Wc"xv EMH�p;Wb ��rbe���ww��ǂ�EMHw � EMH�p;Ww�d�bR�V��w�bR�V��w�bR�Vw�bR�U���
Danke und Gruß Moritz
Code:cat /dev/ttyUSB0 | od -tx1
Kommentar
Kommentar