====== Eastron DRS110M ====== {{ :hardware:channels:meters:power:eastron_drs115m.jpg?150}} Das Gerät ist ein Zähler mit einfacher Baubreite und Hutschinenmontage. Der Vorteil ist die integrierte RS485 Schnittstelle, die man elektronisch auslesen kann. Das Gerät gibt es bei Amazon für 30EUR. ====== mechanischer Einbau ====== Das Gerät passt zwar auf eine Standarthutschiene, ist jedoch etwas zu hoch. Hier muss evt die Blende der Schalttafel nachgearbeitet werden. Die Klemmen des Zähler sind allerdings sehr unschön ausgeführt. Unter 6mm^2 Anschlussleitung geht da nichts. Für einen Zähler mit 10A Dauerstrom schon recht großzügig dimensioniert. ====== Anschluss an den Computer: RS485 - USB Wandler ====== Für den Anschluss am Computer benötigt man eine RS485-USB Adapter.\\ Elektrisch wird der Adapter mit dem Stromzähler A<->A , B<->B und G<->G verbunden. Bei kurzer Kabellänge (50cm) kann auf die Terminierung verzichtet werden. ====== Adressen: ====== Oder auch Modbusregister.\\ Einige davon waren in der offiziellen Doku nicht enthalten, es kann daher sein das sie nicht von allen Zählermodellen bereitgestellt werden. ^ Name ^ Adresse ^ Byte ^ Faktor ^ Einheit ^ | Spannung | 00000000 | 2 | 0.1 | V | | Strom | 00000001 | 2 | 0.1 | A | | Frequenz | 00000002 | 2 | 0.1 | Hz | | Wirkeistung | 00000003 | 2 | 10 | W | | Blindleistung | 00000004 | 2 | 10 | VAr | | Scheinleistung | 00000005 | 2 | 10 | VA | | Zaehler1 | 00000010 | 4 | 1 | Wh | | Zaehler1a | 00000011 | 4 | 1 | Wh | | Zaehler2 | 00000020 | 4 | 1 | Wh | | Zaehler2a | 00000021 | 4 | 1 | Wh | | Zeit | 00000031 | | 1 | Format? | | Temperatur | 00000032 | 2 | 1 | °C | | Serielle Schnittstelle | 00000034 | 6 | | | | Baudrate | 00000035 | 2 | | | | Meter ID | 00000036 | 6 | | | | Passwort | 00000037 | 4 | | | | Reset Zählerstände | 00000040 | 0 | | | Registerwerte für die Baudrate: ^ Wert ^ Baud ^ | 0001 | 1200 | | 0002 | 2400 | | 0003 | 4800 | | 0004 | 9600 | ====== Anschluss an den Computer: Software ====== Im Internet kursieren einige Scripte in PHP und Python, um den Zähler auszulesen. ====== Perl-Script ====== Es werden Pakete für die serielle Schnittstelle benötigt: sudo apt-get install libdevice-serialport-perl sudo apt-get install libwww-perl Das Script gibt die Zählerdaten auf der Console aus, eine Speicherung in die Datenbank erfolgt auch. Zuvor muss über das Webinterface der Kanal angelegt sein. Der serielle Port und die UUID (diese bekommt man über das Webinterface von volkszähler) müssen im Script angepasst werden. #!/usr/bin/perl -w # teile von http://www.ip-symcon.de/forum/threads/21407-Stromz%C3%A4hler-mit-RS485/page2 entnommen # 20130802 : ollir use Device::SerialPort; my $port = Device::SerialPort->new("/dev/ttyUSB0") || die $!; $port->baudrate(9600); $port->databits(7); $port->parity("even"); $port->stopbits(1); $port->handshake("none"); $port->write_settings; $port->purge_all(); $port->read_char_time(0); # don't wait for each character $port->read_const_time(100); # 100 millisecond per unfulfilled "read" call my $serialID = "000000000000"; # auszulesende Zähler-ID (12 Stellen), Standard: "000000000000" my $password = "00000000"; # Standartpasswort my $uuid="84356400-f7b8-11e2-8431-07a5d66d6531" ; my $verbose = 2 ; # ======================================== sub sendgetserial { my ($cmd) = @_; my $count; my $saw; my $x; $port->lookclear; $port->write( $cmd ); ($count,$saw)=$port->read(84); # will read 84 chars $x=uc(unpack('H*',$saw)); # nach hex wandeln $cmd =~ s/\n/\\n/mg; $cmd =~ s/\r/\\r/mg; $saw =~ s/\n/\\n/mg; $saw =~ s/\r/\\r/mg; if ( $verbose>10 ) { printf "+++ sendserial\n" ; print " CMD: $cmd \n"; # gibt den Befehl in ASCII aus print " COUNT: $count \n"; # gibt die Anzahl der empfangenen Daten aus print " HEX: $x \n"; # gibt die empfangenen Daten in Hex aus print " ASCII: $saw \n"; # gibt die empfangenen Daten aus printf "--- sendserial\n" ; } return $saw; } # ======================================== sub decodeVAL { my ($val) = @_; if ( $verbose>10 ) { printf "+++ decodeVAL\n" ; print " val = ( $val ) \n" ; } if($val =~ m/\((\d+)\)/) { if ( $verbose>10 ) { print " decoded val = $1\n"; printf " --- decodeVAL\n" ; } return $1; } print " val = ( $val ) \n" ; die "NICHTS gefunden!\n"; print "NICHTS gefunden!\n"; return -8888; } # ======================================== ## Variablen my $cmd; my $res; my %vals = (); # Abfrage der Initialisierung: hier bekommt man die $serialID raus !! print("# INIT 1 #############\n") if ($verbose>8); sendgetserial("/?!\r\n"); # initalisierung ueberprüfen, jetzt mit ID, wenn die ID nicht passt, kommt keine Antwort! print("# INIT 2 #############\n") if ($verbose>8); sendgetserial("/?" . $serialID . "!\r\n"); # ACK / Option Select Message senden ('Programming Mode') # der programming modus muss auch bei einer einfachen Strom,Spgs-Abfrage gesetzt werden if (1) { print("# PROGRAMMING MODE #############\n") if ($verbose>8); $cmd = chr(0x06).chr(0x30).":".chr(0x31).chr(0x0D).chr(0x0A); sendgetserial( $cmd); $cmd = chr(0x01)."P1".chr(0x02)."(".$password.")".chr(0x03).chr(0x61); sendgetserial( $cmd); } # ****************** Werte lesen print("# SPANNUNG #############\n") if ($verbose>8); $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000000()".chr(0x03).chr(0x63) ); $vals { 'Spannung' } = ( decodeVAL $res ) / 10; print("# STROM #############\n") if ($verbose>8); $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000001()".chr(0x03).chr(0x62) ); $vals { 'Strom' } = ( decodeVAL $res ) / 10; print("# FREQUENZ #############\n") if ($verbose>8); $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000002()".chr(0x03).chr(0x61) ); $vals { 'Frequenz' } = ( decodeVAL $res ) / 10; print("# Wirkleistung #############\n") if ($verbose>8); $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000003()".chr(0x03).chr(0x60) ); $vals { 'PWirk' } = ( decodeVAL $res ) * 10; print("# Blindleistung #############\n") if ($verbose>8); $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000004()".chr(0x03).chr(0x67) ); $vals { 'PBlind' } = ( decodeVAL $res ) * 10; print("# Scheinleistung #############\n") if ($verbose>8); $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000005()".chr(0x03).chr(0x66) ); $vals { 'PSchein' } = ( decodeVAL $res ) * 10; print("# Leistungsfaktor #############\n") if ($verbose>8); $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000006()".chr(0x03).chr(0x65) ); $vals { 'cosphi' } = ( decodeVAL $res ) / 1000 ; print("# Zaehlerstand 1 #############\n") if ($verbose>8); $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000010()".chr(0x03).chr(0x62) ); $vals { 'Zaehler1' } = ( decodeVAL $res ) * 1 ; $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000011()".chr(0x03).chr(0x63) ); $vals { 'Zaehler1b' } = ( decodeVAL $res ) * 1 ; print("# Zaehlerstand 2 #############\n") if ($verbose>8); $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000020()".chr(0x03).chr(0x61) ); $vals { 'Zaehler2' } = ( decodeVAL $res ) * 1 ; $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000021()".chr(0x03).chr(0x60) ); $vals { 'Zaehler2b' } = ( decodeVAL $res ) * 1 ; print("# Temperatur #############\n") if ($verbose>8) ; $res = sendgetserial( chr(0x01)."R1".chr(0x02)."00000032()".chr(0x03).chr(0x62) ); $vals { 'Temperatur' } = ( decodeVAL $res ) * 1.0 ; # ****************** LOGOUT print("# LOGOUT #############\n") if ($verbose>8); $cmd = chr(0x01)."B0".chr(0x03).chr(0x71) . "/?!\r\n" ; $res = sendgetserial( $cmd); # ****************** Werte plotten if ($verbose>1) { print("# Werte plotten #############\n"); while ( my ($key, $value) = each(%vals) ) { print " --> $key = $value\n"; } } # ****************** Wert in die Datenbank schreiben use LWP::UserAgent; $val = $vals{ "Zaehler1" } ; print "ZAEHLERSTAND: " . $uuid . " = " . $val . "\n"; my $server_endpoint = "http://localhost/middleware.php/data/${uuid}.json?value=" . $val; print "serverget = " . $server_endpoint . "\n"; my $ua = LWP::UserAgent->new; # set custom HTTP request header fields my $req = HTTP::Request->new(POST => $server_endpoint); $req->header('content-type' => 'application/json'); $req->header('x-auth-token' => 'kfksj48sdfj4jd9d'); # add POST data to HTTP request body $req->content(" "); if (1) { my $ua = LWP::UserAgent->new; my $resp = $ua->request($req); if ($resp->is_success) { my $message = $resp->decoded_content; print "Received reply: $message\n"; } else { print "HTTP GET error code: ", $resp->code, "\n"; print "HTTP GET error message: ", $resp->message, "\n"; } } Das Script abspeichern, ausführbar machen. Das Script kann zum testen manuell aufgerufen werden. Falls alles gut läuft kann das Script im Cron zyklisch aufgerufen werden (''crontab -e''). ===== Beispielausgabe ===== Falls das Script läuft, bekommt man folgende Ausgaben: --> Zaehler2b = 7450 --> Temperatur = 22 --> Zaehler1 = 3040 --> Frequenz = 50 --> Zaehler2 = 7450 --> Zaehler1b = 3040 --> cosphi = 0 --> PWirk = 0 --> PSchein = 0 --> Spannung = 234.2 --> PBlind = 0 --> Strom = 0 Die Zählerangabe sind Wh. ===== Bugs ===== Der Zähler hat das Problem, dass er Zähler 1x am Tag für 60min nicht zählt. Die Datenkommunikation funktioniert, aber der Zähler sendet scheinbar immer die gleichen Daten. Der Fehler betrifft nur die Zählerstände, die Istwerte von Leistung, Spannung und Strom sind nicht betroffen. Das Problem konnte an einem zweiten Zähler verifizieren können. Das Problem tritt immer zwischen 0Uhr und 1Uhr (zählerinterne Zeit) auf. Die Zeit kann allerdings gesetzt werden, so daß das Problem "schiebbar" ist. Wer den Zähler für die Solaranlage nutzt, kann den Bug einfach auf Mitternacht schieben. ===== Todos ===== * Werte in die Datenbank schreiben * prüfen, ob das script auch bei mehreren Zähler an einem RS485 läuft * die ID der Zähler setzen * die Passwörter der Zähler setzen