Hallo Hofimax,
gute Nachrichten.
Ich habe mich hingesetzt und ein Script für unseren Zähler geschrieben (kaum zu Glauben wieviele Stunden da vergehen wollen bis dies funktioniert :rolleyes:). Dieses teste ich jetzt schon einige Zeit lang, seit einer Woche läuft dieses stabil. Interessanterweise war (für mich) die Schwierigkeit nicht die Daten zu entschlüssseln, sondern mit den Bytes umzugehen (aus hex mach byte, aus 4 Byte mach longint, aus sting mach byte-arrays, aus byte mach hex, usw…).
Aber naja, dafür habe ich einiges in Sachen PHP dazugelernt.
Übrigens ist der Aufbau der Daten etwas anders als ich damals geschrieben hatte: Die Daten, die der Zähler ausspuckt, sind bereits ein MBUS-Frame, der Datenteil des MBUS-Frames ist verschlüsselt.
Herzlichen Dank an dieser Stelle an schnello vom Photovoltaikforum, der den Frame bis ins kleinste Detail aufgeschlüsselt hat und an hutch120 von GitHub, der eine Klasse für MBUS in PHP entwickelt hat, hier konnte ich mir einiges an Code und Vorgehensweisen abschauen.
ohne die beiden hätte ich das nicht geschafft 
Also lang genug gefaselt, hier der Code:
<?php
$IDEnergieAP = 24517 /*[Energie\Energiezähler_PV\Zaehlerstand Einkauf]*/;//0403 (Energie A+ Obis 1.8.0) [kWh]
$IDEnergieAM = 19149 /*[Energie\Energiezähler_PV\Zaehlerstand PV Verkauf]*/;//04833C (Energie A- Obis 2.8.0)[kWh]
$IDEnergieRP = 32405 /*[Energie\Energiezähler_PV\Blindenergie Einkauf]*/;//8410FB8273 (Energie R+ Obis 3.8.1)[kvarh]
$IDEnergieRM = 19230 /*[Energie\Energiezähler_PV\Blindenergie Verkauf]*/;//8410FB82F33C (Energie R- Obis 4.8.1)[kvarh]
$IDWirkleistungPP = 26775 /*[Energie\Energiezähler_PV\Wirkleistung Einkauf]*/;//042B (Wirkleistung P+ Obis 1.7.0)kW
$IDWirkleistungPM = 57080 /*[Energie\Energiezähler_PV\Wirkleistung Verkauf]*/;//04AB3C (Wirkleistung P- Obis 2.7.0)kW
$IDBlindleistungQP = 18481 /*[Energie\Energiezähler_PV\Blindleistung Einkauf]*/;//04FB14 (Blindleistung Q+ Obis 3.7.0)kvar
$IDBlindleistungQM = 13602 /*[Energie\Energiezähler_PV\Blindleistung Verkauf]*/;//04FB943C (Blindleistung Q- Obis 4.7.0)kvar
$IDInkasso = 33683 /*[Energie\Energiezähler_PV\Inkasso]*/;//0483FF04 (Inkasso Obis 1.128.0 ) kWh
$IDRegistervariable = 44661 /*[Energie\Energiezähler_PV]*/;
$key_hex= "E2E5FFFFFFFFFFFFFF74EB201C7C489D";
if ($_IPS['SENDER'] == "RegisterVariable"){
$VarTrigger = IPS_GetVariable($IDInkasso);
//Wenn über 50 sec die Variable nicht aktualisiert wurde
if(($VarTrigger['VariableUpdated'] + 50) < (time())) {
// bereits im Puffer der Instanz vorhandene Daten in $data kopieren
$data = RegVar_GetBuffer($_IPS['INSTANCE']);
// neu empfangene Daten an $data anhängen
$data .= $_IPS['VALUE'];
if (substr($data,0,1) == "\x10"){
// wenn die Zeichen in $data der Anfrage vom MBUS-Master entsprechen
if ($data == "\x10\x40\xF0\x30\x16"){
RegVar_SetBuffer($_IPS['INSTANCE'], "");
RegVar_SendText($_IPS['INSTANCE'],"\xe5");
}else{
if (strlen ($data ) > 5 ) {//datenstring fehler, löschen
RegVar_SetBuffer($_IPS['INSTANCE'], "");
}else{//datenstring noch nicht vollständig in buffer schreiben
RegVar_SetBuffer($_IPS['INSTANCE'], $data);
}
}
}elseif (substr($data,0,1) == "\x68"){
RegVar_SetBuffer($_IPS['INSTANCE'], $data);
$key = hex2bin ($key_hex);
//$data = "\x68\x5F\x5F\x68\x53\xF0\x5B\x00\x00\x00\x00\x2D\x4C\x01\x0E\x0D\x00\x50\x05\x3F\xD0\xFE\xB7\x26\x76\x0C\xC7\xAA\xF0\xB5\x2B\x41\xF0\xC5\x41\xBD\x63\x06\xDC\xD8\xB9\x1B\x3D\xA2\x31\x1E\xF1\x3D\x25\x14\xD0\x96\x00\x82\x16\x1E\xFE\xC4\xB6\xCB\x1E\x0B\x33\x28\xBE\x61\x77\xDC\xA5\x94\xC1\x28\x00\x24\xA8\x35\xF1\xD6\x55\xBA\x71\x82\xB2\x56\xE9\x4B\xD3\x3A\xC0\xA6\xB0\x8D\xA4\x67\x81\xEB\x4E\x91\xE0\x12\x16";
//$dataVolkszaehler = "685F5F6853F05B000000002D4C010E0D0050053FD0FEB726760CC7AAF0B52B41F0C541BD6306DCD8B91B3DA2311EF13D2514D0960082161EFEC4B6CB1E0B3328BE6177DCA594C1280024A835F1D655BA7182B256E94BD33AC0A6B08DA46781EB4E91E01216";
//$data = hex2bin ($dataVolkszaehler);
//$dataM21 = "685F5F6873F05B000000002D4C010E08005005BBF0C8BECA58A381934365227669CB5AA8E73778E5EABB317B76A0430DF8C505BFC03BBB782607DC3741C2028F90C09A9C6E10B08F1B3FBE5A0C91D3D6A7A462D7E78F8AACDDAC5B5B3608C7DB27574CEC16";
//$data = hex2bin ($dataM21);
//hex_dump ($string);
$MBUS_FRAME_FIXED_SIZE_LONG = 6;
$dataarray = byteStr2byteArray ($data);
$dataarray_len = count($dataarray);
if ($dataarray_len < 3) {
//echo ("Got a valid long/control packet start, but we need data to determine the length!");
}else{
$start1 = $dataarray[0];
$length1 = $dataarray[1];
$length2 = $dataarray[2];
if ( $length1 != $length2) {
//echo ("Not a valid M-bus frame. Buffer von IPS wird vorsichtshalber gelöscht");
RegVar_SetBuffer($_IPS['INSTANCE'], "");
}else{
if ($dataarray_len < ($MBUS_FRAME_FIXED_SIZE_LONG + $length1)) {
//echo ("Length of packet incorrect, we need more data! ");
}else{
$start2 = $dataarray[3];
$control = $dataarray[4];
$address = $dataarray[5];
$control_information = $dataarray[6];
$IdentNr1 = $dataarray[7];
$IdentNr2 = $dataarray[8];
$IdentNr3 = $dataarray[9];
$IdentNr4 = $dataarray[10];
$ManufacCode1 = $dataarray[11];
$ManufacCode2 = $dataarray[12];
$Version = $dataarray[13];
$DeviceType = $dataarray[14];
$AcessNr = $dataarray[15];
$MBusState = $dataarray[16];
$ConfigWord1 = $dataarray[17];
$ConfigWord2 = $dataarray[18];
$checksum = $dataarray[$MBUS_FRAME_FIXED_SIZE_LONG + $length1-2];
$stop = $dataarray[$MBUS_FRAME_FIXED_SIZE_LONG + $length1-1];
/**
* Calcuate checksum
*/
$calculated_checksum = $control;
$calculated_checksum += $address;
$calculated_checksum += $control_information;
for ($i = 7; $i < $dataarray_len - 2; $i++) {
$calculated_checksum += $dataarray[$i];
$calculated_checksum = $calculated_checksum % 256;
}
if ($checksum != $calculated_checksum ) {
echo("Checksums do not match!! Buffer von IPS wird vorsichtshalber gelöscht");
RegVar_SetBuffer($_IPS['INSTANCE'], "");
}else{
$encryptedData = "";
for ($i = 19; $i < $MBUS_FRAME_FIXED_SIZE_LONG + $length1-2; $i++) {
$encryptedData .= pack ("C",$dataarray[$i]);
}
echo "Daten zum Entschlüsseln:
";
hex_dump ($encryptedData);
// folgenden Block auskommentieren wenn abfragezyklus 1x pro Minute sein soll, wenn auskommentierung weggenommen wird erfolgt die Variablenaktualisierung 1x pro Sekunde!
if ($dataarray_len > $MBUS_FRAME_FIXED_SIZE_LONG + $length1) {
//reset buffer
$DataAnBuffer = "";
for ($i = $MBUS_FRAME_FIXED_SIZE_LONG + $length1; $i < $dataarray_len; $i++) { //überschüssige daten in den buffer zurückschreiben
$DataAnBuffer .= pack ("C",$dataarray[$i]);
}
RegVar_SetBuffer($_IPS['INSTANCE'], $DataAnBuffer);
//RegVar_SendText($_IPS['INSTANCE'],"\xe5");
}elseif($dataarray_len = $MBUS_FRAME_FIXED_SIZE_LONG + $length1){
RegVar_SetBuffer($_IPS['INSTANCE'], "");
//RegVar_SendText($_IPS['INSTANCE'],"\xe5");
}
/* Open module, and create IV */
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'cbc', '');
//$iv_size = mcrypt_enc_get_iv_size($td);
//$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$iv = pack("C*", $ManufacCode1, $ManufacCode2, 0 , 0 , 0 , 0 , $Version , $DeviceType , $AcessNr , $AcessNr, $AcessNr, $AcessNr, $AcessNr, $AcessNr, $AcessNr, $AcessNr);
echo "IV:
";
hex_dump ($iv);
/* Initialize encryption handle */
if (mcrypt_generic_init($td, $key, $iv) != -1) {
/* Reinitialize buffers for decryption */
mcrypt_generic_init($td, $key, $iv);
$decryptedData = mdecrypt_generic($td, $encryptedData);
mcrypt_generic_deinit($td);
/* Encrypt data */
mcrypt_generic_init($td, $key, $iv);
$encryptedData_Kontrolle = mcrypt_generic($td, $decryptedData);
/* Clean up */
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
if (strncmp($encryptedData, $encryptedData_Kontrolle, strlen($encryptedData)) == 0) {
echo "Entschlüsselung ok
";
} else {
echo "Entschlüsselung error
";
}
echo "Entschlüsselte Daten:
";
hex_dump ($decryptedData);
$decrArray = byteStr2byteArray ($decryptedData);
$decrArray_len = count($decrArray);
if ($decrArray_len != 80) {
echo ("Die Länge der entschlüsselten Daten passt nicht!");
}else{
if ( $decrArray [0]. $decrArray [1] != $decrArray [$decrArray_len-2]. $decrArray [$decrArray_len-1]) {
echo ("keine validen entschlüsselten Daten!");
hex_dump ($decrArray [0]. $decrArray [1]);
}else {
//0403 (Energie A+ Obis 1.8.0) [Wh]
$KontrolleObis = ByteToHex($decrArray[10]) .ByteToHex($decrArray[11]);
$Byte1 = $decrArray[12];
$Byte2 = $decrArray[13];
$Byte3 = $decrArray[14];
$Byte4 = $decrArray[15];
$EnergieAP = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
//$EnergieAP = unpack ("L*",pack ("C*",$Byte4,$Byte3,$Byte2,$Byte1));
//$EnergieAP = pack ("C*",$Byte4,$Byte3,$Byte2,$Byte1);
echo "
Energie A+ Obis 1.8.0: ".$EnergieAP;
//04833C (Energie A- Obis 2.8.0)[Wh]
$KontrolleObis .= ByteToHex($decrArray[16]) .ByteToHex($decrArray[17]) . ByteToHex($decrArray[18]);
$Byte1 = $decrArray[19];
$Byte2 = $decrArray[20];
$Byte3 = $decrArray[21];
$Byte4 = $decrArray[22];
$EnergieAM = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
echo "
Energie A- Obis 2.8.0: ".$EnergieAM;
//8410FB8273 (Energie R+ Obis 3.8.1)[varh]
$KontrolleObis .= ByteToHex($decrArray[23]) .ByteToHex($decrArray[24]) . ByteToHex($decrArray[25]) . ByteToHex($decrArray[26]). ByteToHex($decrArray[27]);
$Byte1 = $decrArray[28];
$Byte2 = $decrArray[29];
$Byte3 = $decrArray[30];
$Byte4 = $decrArray[31];
$EnergieRP = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
echo "
Energie R+ Obis 3.8.1: ".$EnergieRP;
//8410FB82F33C (Energie R- Obis 4.8.1)[varh]
$KontrolleObis .= ByteToHex($decrArray[32]) .ByteToHex($decrArray[33]) . ByteToHex($decrArray[34]). ByteToHex($decrArray[35]). ByteToHex($decrArray[36]). ByteToHex($decrArray[37]);
$Byte1 = $decrArray[38];
$Byte2 = $decrArray[39];
$Byte3 = $decrArray[40];
$Byte4 = $decrArray[41];
$EnergieRM = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
echo "
Energie R- Obis 4.8.1: ".$EnergieRM;
//042B (Wirkleistung P+ Obis 1.7.0)W
$KontrolleObis .= ByteToHex($decrArray[42]) .ByteToHex($decrArray[43]);
$Byte1 = $decrArray[44];
$Byte2 = $decrArray[45];
$Byte3 = $decrArray[46];
$Byte4 = $decrArray[47];
$WirkleistungPP = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
echo "
Wirkleistung P+ Obis 1.7.0: ".$WirkleistungPP;
//04AB3C (Wirkleistung P- Obis 2.7.0)W
$KontrolleObis .= ByteToHex($decrArray[48]) .ByteToHex($decrArray[49]) . ByteToHex($decrArray[50]);
$Byte1 = $decrArray[51];
$Byte2 = $decrArray[52];
$Byte3 = $decrArray[53];
$Byte4 = $decrArray[54];
$WirkleistungPM = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
echo "
Wirkleistung P- Obis 2.7.0: ".$WirkleistungPM;
//04FB14 (Blindleistung Q+ Obis 3.7.0)var
$KontrolleObis .= ByteToHex($decrArray[55]) .ByteToHex($decrArray[56]) . ByteToHex($decrArray[57]);
$Byte1 = $decrArray[58];
$Byte2 = $decrArray[59];
$Byte3 = $decrArray[60];
$Byte4 = $decrArray[61];
$BlindleistungQP = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
echo "
Blindleistung Q+ Obis 3.7.0: ".$BlindleistungQP;
//04FB943C (Blindleistung Q- Obis 4.7.0)var
$KontrolleObis .= ByteToHex($decrArray[62]) .ByteToHex($decrArray[63]) . ByteToHex($decrArray[64]). ByteToHex($decrArray[65]);
$Byte1 = $decrArray[66];
$Byte2 = $decrArray[67];
$Byte3 = $decrArray[68];
$Byte4 = $decrArray[69];
$BlindleistungQM = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
echo "
Blindleistung Q- Obis 4.7.0: ".$BlindleistungQM;
//0483FF04 (Inkasso Obis 1.128.0 ) Wh
$KontrolleObis .= ByteToHex($decrArray[70]) .ByteToHex($decrArray[71]) . ByteToHex($decrArray[72]). ByteToHex($decrArray[73]);
$Byte1 = $decrArray[74];
$Byte2 = $decrArray[75];
$Byte3 = $decrArray[76];
$Byte4 = $decrArray[77];
$Inkasso = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
echo "
Inkasso Obis 1.128.0: ".$Inkasso;
if ($KontrolleObis = "040304833C8410FB82738410FB82F33C042B04AB3C04FB1404FB943C0483FF04") {
echo "
OBIS-Kennzahlen stimmen überein";
$IPSEnergieAP = GetValue($IDEnergieAP);
$IPSEnergieAM = GetValue($IDEnergieAM);
if (($IPSEnergieAP <= round($EnergieAP * 0.001,2)) AND ($IPSEnergieAM <= round($EnergieAM * 0.001,2))) {
//Daten (endlich :-))in IPS-Variablen schreiben (und umrechnen)!
Schreiben($IDEnergieAP, round($EnergieAP * 0.001,2));
Schreiben($IDEnergieAM, round($EnergieAM * 0.001,2));
Schreiben($IDEnergieRP, round($EnergieRP * 0.001,2));
Schreiben($IDEnergieRM, round($EnergieRM * 0.001,2));
Schreiben($IDWirkleistungPP, round($WirkleistungPP * 0.001,2));
Schreiben($IDWirkleistungPM, round($WirkleistungPM * 0.001,2));
Schreiben($IDBlindleistungQP, round($BlindleistungQP * 0.001,2));
Schreiben($IDBlindleistungQM, round($BlindleistungQM * 0.001,2));
SetValue($IDInkasso, round($Inkasso * 0.001,2)); //dieser Wert wird immer geschrieben damit die auslesung alle 60s möglich ist. sonst auslesen alle 1s
echo "
Werte wurden geschrieben, falls sie sich in der Zwischenzeit geändert haben.";
}else{
echo "
Werte wurden nicht geschrieben, fehlerhafte Werte, ausgelesener Zählerwert niedriger als Wert im IPS";
}
}else{
echo "
Fehler bei den OBIS-Kennzahlen (evtl. defekter Frame)";
}
}
}
}
}
}
}
}else{
RegVar_SetBuffer($_IPS['INSTANCE'], "");
}
}
}else{
RegVar_SetBuffer($IDRegistervariable, "");
}
function byteStr2byteArray($s) {
return array_slice(unpack("C*", "\0".$s), 1);
}
function outputByteString($s) {
$out = "";
$bufArr = byteStr2byteArray($s);
foreach( $bufArr as $char ) {
$out .= "\\";
$out .= "x";
if ( intval($char) < 16 ) {
$out .= "0";
}
$out .= dechex($char) . "";
}
echo "
" . $out . "
";
}
function ByteToHex($byte) {
//$retval = "0x";
$retval = "";
if ( $byte <= 0x0F ) {
$retval .= "0" . strtoupper(dechex($byte));
} else {
$retval .= strtoupper(dechex($byte));
}
return $retval;// . " ";
}
function returnByteString($byteString) {
$out = "";
$byteArr = byteStr2byteArray($byteString);
foreach( $byteArr as $byte ) {
//$out .= "\\x";
if ( intval($byte) < 16 ) {
$out .= "0";
}
$out .= dechex($byte) . " ";
}
return strtoupper($out);
}
function hex_dump($data, $newline="
"){
static $from = '';
static $to = '';
static $width = 16; # number of bytes per line
static $pad = '.'; # padding for non-visible characters
if ($from==='')
{
for ($i=0; $i<=0xFF; $i++)
{
$from .= chr($i);
$to .= ($i >= 0x20 && $i <= 0x7E) ? chr($i) : $pad;
}
}
$hex = str_split(bin2hex($data), $width*2);
$chars = str_split(strtr($data, $from, $to), $width);
$offset = 0;
foreach ($hex as $i => $line)
{
echo sprintf('%6X',$offset).' : '.implode(' ', str_split($line,2)) . ' [' . $chars[$i] . ']' . $newline;
$offset += $width;
}
}
function Schreiben ($ID, $Wert){
$AktuellerWert = GetValue($ID);
if ($AktuellerWert<> $Wert){
SetValue ($ID , $Wert);
}
}
Einfach unter der Registervariable float-Variablen anlegen, IDs im oberen Teil des Scripts anpassen, Variablenprofil lt. Bemerkung dahinter den Variablen zuweisen, und Key von der EnerigeAG eingeben.
Das Script liefert 1x pro Minute Werte, Varaiablen werden nur aktualisert wenn sich der Wert geändert hat.
Übrigens glaub ich hast du die Com-Schnittstelle falsch konfiguriert:
Baud: 9600
Datenbits: 8
Stop: 1
Parität: Keine.
Viel Spaß mit dem Script!
Eine abschließende Frage an die Profis:
wie baut ihr aus einem Teil eines Arrays einen Long int zusammen?
Ich mach das mit folgendem Code, das geht doch sicher einfacher, oder?
$Byte1 = $decrArray[66];
$Byte2 = $decrArray[67];
$Byte3 = $decrArray[68];
$Byte4 = $decrArray[69];
$BlindleistungQM = hexdec(ByteToHex($Byte4).ByteToHex($Byte3).ByteToHex($Byte2).ByteToHex($Byte1));
Gruß
Bikasso