====== Voltcraft VSM-103 ====== Conrad hat einen preisgünstigen [[http://www.conrad.de/ce/de/product/125442/VOLTCRAFT-VSM-103-DREHSTR-ZAeHLER|Drehstromzähler mit S0-Schnittstelle]] und Modbus in RS-485 Variante und Infrarot. Leider gibt es dazu kaum Doku. Nach dem Kauf des [[http://www.conrad.de/ce/de/product/125441/VOLTCRAFT-SMART-METERING-SYSTEM|Komplettsystems]] und etwas Schnittstellenauswertung und probieren gibt's dazu hier ein paar weitere Infos. Bei weiteren Infos oder Fragen am Besten hier eintragen. ===== Allgemeines ===== *Preisgünstig *Undokumentierte Schnittstelle *Hoher Eigenverbrauch: ca. 2W (relativ zu anderen Zählern, jeweils laut Datenblatt) ===== RS-485 ===== Der Zähler hat eine RS-485 Schnittstelle zum Abfragen. Diese arbeitet in der Betriebsart **8e2**. Bisher ist nur die Geschwindigkeit mit 1200 baud getestet worden. Laut Doku sollte es auch schneller gehen. ==== Modbus ==== Der Modbus wird binär nach RTU betrieben. Siehe dazu auch http://de.wikipedia.org/wiki/Modbus und insbesondere http://www.simplymodbus.ca/. Grundätzliche Idee: Es gibt einen Master und mehrere Slaves. Der Master schickt Anfragen an die Slaves die darauf antworten. === Anfrage === Der VSM-Zähler erkennt (mindestens und soweit bekannt) Anfragen nach dem Schema | ^ID ^Funktion ^Register ^Länge ^CRC| ^Beschreibung |Für VMS-103: 1, 1 Byte|Für VSM-103: 4, 1 Byte|2 Byte, je nach gewünschtem Wert|2 Byte, bei VSM-103 immer 2|CRC, 2 Byte| ^Beispiel (hex)|01|04|00 96|00 02|91 e7|Momentanen Wirk-Gesamtleistung? | === Antwort === Auf erkannte Anfragen antwortet der Zähler dann mit einem Datentelegramm mit meist 8 Byte (Achtung: das folgende Beispiel ist nicht konsistent: es enthält eine 9 Byte Antwortnachricht, passt nicht zum 8 Byte Format): | ^ID ^Funktion ^Daten-Laenge ^Daten ^CRC| ^Beschreibung|Für VMS-103: 1, 1 Byte|Für VSM-103: 4, 1 Byte|Anzahl der Bytes der Antwort, 1 Byte|Daten|CRC| ^Beispiel (hex)|01|04|04|3b a3 d7 0a | d9 75|Momentanen Wirk-Gesamtleistung: 0,05 kWatt| === Datenformat === Die Antworten liegen (soweit bisher bekannt) in den 4 Byte als 32Bit Gleitkommazahl vor: http://de.wikipedia.org/wiki/IEEE_754. Wer damit bisher nicht zu tun hatte, hier gibt's eine Excel-Vorlage zum berechnen: http://www.simplymodbus.ca/FAQ.htm#Types === CRC === Auch hier http://www.simplymodbus.ca/FAQ.htm#CRC gibt's eine Excel-Vorlage um die 2-Byte CRC zu berechnen. Der VSM-103 beantwortet nur Anfragen mit korrekter CRC. === Bisher bekannte Register === ^Register ^hex ^Art ^Beschreibung ^Einheit ^Stellen(*) ^Bemerkung| |16|0010|U1|Spannung zwischen N und L1|[V]| |18|0012|U2|Spannung zwischen N und L2|[V]| |20|0014|U3|Spannung zwischen N und L3|[V]| |78|004e|F|Frequenz|[Hz]|2| |80|0050|I1|Strom auf L1|[A]| |82|0050|I2|Strom auf L2|[A]| |84|0052|I3|Strom auf L3|[A]| |86|0054|IN|Strom auf N|[A]| |?? IN und I geben gleiche Werte?!| |88|0058|I|Strom gesamt|[A]| |:::| |144|0090|P1|Wirkleistung auf L1|[kW]|3|-->1 Watt Auflösung| |146|0092|P2|Wirkleistung auf L2|[kW]|3| |148|0094|P3|Wirkleistung auf L3|[kW]|3| |150|0096|P|Wirkleistung gesamt|[kW]|3| |208|00d0|S1|Scheinleistung L1|[kvar]|3| |210|00d2|S2|Scheinleistung L2|[kvar]|3| |212|00d4|S3|Scheinleistung L3|[kvar]|3|?? Bei mir immer Null?!| |214|00d6|S|Scheinleistung ges|[kvar]|3| |272|0110|Q1|Blindleistung L1|[kvar]|3| |274|0112|Q2|Blindleistung L2|[kvar]|3| |276|0114|Q3|Blindleistung L3|[kvar]|3| |278|0116|Q|Blindleistung ges|[kvar]|3| |336|0150|LF1|Leistungsfaktor L1| |338|0150|LF2|Leistungsfaktor L2| |340|0150|LF3|Leistungsfaktor L3| |342|0150|LF|Leistungsfaktor ges| |352|0160|Z1|Zaehlerstand Wirkenergiebezug|[kWh]|2|-->0,01 kWh Auflösung| |354|0162|Z2|Zaehlerstand Blindenergiebezug? |[kWh]|2|nur Vermutung, nicht bestätigt| |358|0160|Z3|Zaehlerstand Wirkenergieabgabe? |[kWh]|2|nur Vermutung, nicht bestätigt| |360|0168|Z4|Zaehlerstand Blindenergieeinspeisung? |[kWh]|2|nur Vermutung, nicht bestätigt| |1318|0526|??|VSM Antwortet mit: 0x01040456341256275c|||| |1560|0618|??|Zaehlerstand ?|||| |65298|ff12|??||||| |65314|ff22|??||||| Auch bei Einträgen ohne Fragezeichen ist die Beschreibung nicht absolut sicher, solange niemand das Bestätigt bitte die erhaltenen Werte auf Sinnhaftigkeit prüfen! (*) Hier nur: Aussage über die Nachkommastellen, nicht über die Messgenauigkeit ===== S0-Schnittstelle ===== Der VSM-103 verfügt über zwei S0-Schnittstellen für Wirk- und Blindenergie mit einer Konstante von 400 Impulsen pro kWh. Ungetestet. ===== Infrarot-Schnittstelle ===== Per Infrarot soll ebenfalls eine Modbus-RTU Kommunikation mit 1200 baud möglich sein. Ungetestet. ===== Software ===== Bin kein guter Softwerker, aber es funktioniert ;-) Verbesserungen willkommen. #!/usr/bin/perl # Nur bischen Zeit fuer die Logdatei aufbereiten... $unix_time = time; ($jahr, $monat, $tag, $stunde, $minute, $sekunde) = (localtime)[5,4,3,2,1,0]; $monat=$monat+1; $jahr=$jahr+1900; if ($tag<10) {$tag='0'.$tag;} if ($monat<10) {$monat='0'.$monat;} if ($stunde<10) {$stunde='0'.$stunde;} if ($minute<10) {$minute='0'.$minute;} if ($sekunde<10) {$sekunde='0'.$sekunde;} # Anfrage an den Terminalserver (http://wut.de/e-58631-ww-dade-000.php ) # Sollte auch ueber normale Serielle gehen. Dann mit Adapter RS-232<-->RS-485 use IO::Socket; $socket = IO::Socket::INET->new(PeerAddr=> "192.168.1.23", # Terminalserver PeerPort=> "8000", Proto => "tcp") or die "Kaputt!"; # Vordefinierte Register, CRC ist da schon berechnet :-) Hier nur zwei, sonst wird's arg unuebersichtlich. # Momentanleistung 150 $frage[0]=pack('H*',"01040096000291e7"); # Zaehlerstand > 352 $frage[1]=pack('H*',"01040160000fb1ec"); # Wie erhaelt man nochmal die Laenge eines Arrays? ;-) $fragen=2; # Nun fuer jeden Angfragestring durchlaufen for ($anfrage=0; $anfrage < $fragen; $anfrage++) { # Den Anfragestring an den Anschluss schicken. print $socket $frage[$anfrage]; # Einlesen von 8 Byte # Es werden stumpf 8 Byte erwartet, nix mit Timout oder so... $i=-1; while ($i < 8 ){ sysread($socket, my ($byte), 1); $i++; $antwort[$i]=$byte; } $antwortcount=$i; # In der Subroutine den Wert umrechnen. # Es werden stumpf die Datenbits genommen, nix CRC-Pruefung oder so... $wert[$anfrage]=parse32b($antwort[3],$antwort[4],$antwort[5],$antwort[6]); } close($socket); # Jetzt werden die ermittelten Werte in einer Zeile die Logdatei angehaengt, mit ';' getrennt open (LOG,">>/pfad/datei") or die "Logdatei kann nicht geschrieben werden"; print LOG $jahr.$monat.$tag."T".$stunde.$minute.$sekunde.';'.$unix_time.";"; for ($anfrage=0; $anfrage < $fragen; $anfrage++) { print LOG $wert[$anfrage].";" } print LOG "\n"; close(LOG); sub parse32b # 32bit nach Dezimal wandeln, http://de.wikipedia.org/wiki/IEEE_754 { ($byte0, $byte1, $byte2, $byte3) = @_; # So klappts nicht. Vielleicht irgend was falsch herum? #print "Bytes: ". unpack('H*',$byte0) ." ". unpack('H*',$byte1) ." ". unpack('H*',$byte2) ." ". unpack('H*',$byte3). "\n"; #$ra = unpack('f*',$byte0. $byte1. $byte2. $byte3); #print $ra."\n"; ## So klappen's, wenn auch nicht so elegant $bin=unpack('B*', $byte0.$byte1.$byte2.$byte3 ); $sign=substr($bin,0,1); if ($sign==1){$sign=-1;} if ($sign==0){$sign=+1;} $exponent=substr($bin,1,8); $exponent=oct("0b".$exponent); $exponent=$exponent-127; $mantisse=substr($bin,9); $mantisse=oct("0b".$mantisse); $mantisse=$mantisse/8388608; $mantisse=1+$mantisse; $wert= $sign * $mantisse * ( 2 ** $exponent ) ; return($wert); } ===== Beispiele ===== ==== Verbrauch morgens ==== Momentanleistung der drei Phasen (linke y-Achse) und Zählerstand (rechte y-Achse), Abfrageintervall ca. 10 Sekunden. {{:hardware:channels:meters:power:morgens_beschreibung.png|}}