Smartmeter auslesen - Verschlüsselung

Ich versuche mich in meinem Neubau gerade damit meinen Smartmeter auszulesen, was für mich ein Riesenprojekt wird, mangels eingeschränkter Programmierkenntnisse :smiley:
Der Zähler hat eine M-BUS Schnittstelle, den Lesekopf dafür habe ich bereits und via RS232 sehe ich auch schon die Suchanfrage des Zählers welche minütlich ausgesendet wird. Soviel dazu.

Die Kommunikation zum Zähler ist verschlüsselt nach OMS, AES-128, „encryption mode 5“. Eine Schlüssel zum Entschlüsseln habe ich vom EVU bereits bekommen. Meine simple Frage: Das lässt sich aber mit den Boardmitteln grundsätzlich schon lösen, sprich an die Daten zu kommen? Nicht das ich mir hier jetz einen abfummle und dann scheiterts daran :rolleyes:

So, nun brauche ich schon eure Hilfe. Ich habe es bereits geschafft per RS232 ankommende Daten im HEX-Format aus dem Zähler zu sehen. Dieser schickt jede Minute eine Suchanfrage aus, die so aussieht (Debug aus einer RegVar).

Suchanfrage.PNG

Diese soll ich nun mit einem „E5h“ innerhalb von 0,5 sec quittieren bevor ich überhaupt eine Abfrage machen kann. Ich hab schon ein bisschen umherprobiert, erfolglos. Kann mir hier jemand auf die Sprünge helfen wie ich sowas angehe?

Dank euch!

Hi!

Ich würde es damit machen:
RegisterVariable — IP-Symcon :: Automatisierungssoftware

Wenn etwas kommt im Target-Skript der RegVar darauf reagieren. Geht ruck zuck. Und wenn XY kommt, einfach skripten „schick ABC zurück“.

Grüße,
Chris

Genau damit habe ich begonnen, aber ich checke leider nicht genau wie ich den Hex Wert übernehmen muß bzw. die Antwort retour sende, ich stelle hier mal meine ersten Codezeilen ein, aber bitte nicht lachen :smiley:

 <?
if ($_IPS['SENDER'] == "RegisterVariable")

{
// 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'];
// wenn die Zeichen xxx in $data gefunden worden sind
    if ($data == '10 40 F0 C1 64 FC')
    {
    
	RegVar_SendText(31963,'e5H');
}
}

?>

So würde mein Quittierbefehl aussehen, funktioniert halt nicht :stuck_out_tongue:

Gibt nix zu lachen! Du hast es wenigstens selbst versucht und sieht gar nicht schlecht aus :slight_smile: Das ist schon weit mehr als andere machen :rolleyes: :wink: Aber hättest schreiben können, dass du das schon gemacht hast :wink:

Dann öffne mal die RegVar und klicke auf DEBUG. Was passiert da? Wird der Buffer immer weiter gefüllt und nicht geleert? :smiley:

Dann bau noch ein „RegVar_SetBuffer($ID, „“);“ ein, damit der Buffer geleert wird. Ist es das nicht, dann mal bitte die Debug-Ausgabe posten.

Grüße,
Chris

Ergänzend sei noch gesagt, dass dein Vergleich und auch dein Senden so nicht funktionieren kann.
Du vergleichst einen String mit Rohdaten aus der RegVar mit einer Zeichenkette von Zahlen/Buchstaben.
Du musst aber beides im gleichen ‚Format‘ haben.
Schau dir dazu mal die Funktion PHP: hex2bin - Manual an sowie die Umkehrfunktion.
Beim senden ist es ähnlich. Bei nur einem Zeichen kannst du z.B. Chr(0xe5) nutzen.
Michael

Kannst es ja erst mal so testen.

 <? 
if ($_IPS['SENDER'] == "RegisterVariable") 

{ 
// 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']; 
// wenn die Zeichen xxx in $data gefunden worden sind 
    if ($data == "\x10\x40\xF0\xC1\x64\xFC") 
    { 
     
    RegVar_SendText(31963,"\xE5"); 
} 
} 

?>

Immer schön mit Debuggen. :smiley:

Hi Rainer!

Geile Sache, funktioniert nur nicht :slight_smile: Bin noch etwas verwirrt wegen der Hex Werte, so wie du das schreibst, da wär ich nicht drauf gekommen.
Kannst es sein das es nicht funktioniert weil ich die Daten nicht in „einer Zeile“ empfange, siehe Screenshot. Das Script läuft auf alle Fälle durch wenn die Daten kommen.

Nein, das Problem ist, dass in deinem Buffer mehr drin steht, als nur das eine Auftreten deiner Hex-Sequenz. Öffne mal das Debug der RegVar, dann siehst Du das. Ich würde hier vltt. mit „preg_match“ arbeiten, oder auf das Auftreten des ersten und letzten Hex-Wertes mit „str_pos“ prüfen. Und wenn Du die Sequenz „gefunden“ hast, muss der Buffer auch wieder geleert werden.

Okay, danke, das mit dem Buffer wer ein guter Tipp und hat mein Verständnis für diesen Buffer erweitert. Ich habe das Script jetzt so abgeändert

 <?
if ($_IPS['SENDER'] == "RegisterVariable")

{
// 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'];
// wenn die Zeichen xxx in $data gefunden worden sind
    if ($data == "\x40\xF0\xC1\x64\xFC")
    {

    RegVar_SendText(31963,"\xe5");
    RegVar_SetBuffer(31963, "");
}
}

?>

dann sprudelts auch schon raus aus dem Zähler :smiley: Die Daten die da ankommen muß ich jetzt erstmal verstehen, dann kommen bestimmt die nächsten Fragen, speziell auch zur Verschlüsselung und wie ich die HEX Daten dann soweit ausfiltere das mir nur die Zählerdaten übrig bleiben.

Hatte ich den Buffer nicht schon erwähnt? :smiley: So werden meine Beiträge gelesen :frowning: :wink: :stuck_out_tongue:
Smartmeter auslesen - Verschlüsselung

Daten „filtern“ kannst du dann gut mit „preg_match“. Da kannst du aus einer Zeichenkette einen Teil als „fix“ abfragen und alles was z.B. danach kommt, bis zum Ende oder sonst wo hin, wird dann als „Wert“ genommen.

Grüße,
Chris

@Bayaro
Ach komm, natürlich hatte ich das gelesen, aber das wirklich Alles was in der RegVar daherkommt auch im Buffer bleibt habe ich nicht gleich geschnallt das gebe ich hiermit zu. Ich sagte ja schon, ich bin auf dem Gebiet absolut kein Genie :smiley:

Und natürlich gleich danke für den nächsten Tipp und auch gleich noch ne Frage hinten nach. Wie entschlüssele ich denn die HEX-Daten :smiley:

Ich weiß das sie symmetrisch verschlüsselt sind nach OMS, AES-128, „encryption mode 5“ und ich habe den Key um sie zu entschlüsseln. Wie baue ich sowas in ein PHP Script ein :eek:

Crypt/Decrypt mit PHP:
Verschlüsseln und Entschlüsseln in PHP | A coding project
PHP: Mcrypt - Manual

Aber ob damit genau deine Verschlüsselung geht, keine Ahnung :slight_smile:

Grüße,
Chris

Hallo hofimax,

ich habe auch Interesse an einer Entschlüsselung dieser Daten, da ich auch einen Siemens TD-3511 eingebaut habe, der mit mir nur verschlüsselt über die IR-Schnittstelle kommunizieren möchte.

Da ich kein PHP-Profi und von Verschlüsselungen auch keinen Dunst habe, kann ich (noch) keinen Code zum gewünschten Ergebnis beitragen. Aber ich hab in dieser Sache schon ein wenig recherchiert:

Im Photovoltaikforum hat ein Mitglied in Zusammenarbeit mit den Entwicklern von Volkszähler.org den Zähler erfolgreich augelesen.

Hier kann man den Projektverlauf nachlesen:

Start: http://www.photovoltaikforum.com/volkszaehler-org-f131/siemens-td-3511-auslesen-t107784.html
und weiter bei: https://github.com/volkszaehler/vzlogger/issues/176

Weitere Erkenntnisse:
-Mcrypt ist erst richtig implementiert in IPS V4.0
-Mcrypt muß CBC können (keine Ahnung was das bedeutet)
-Nachdem man den String entschlüsselt hat enthält man einen M-Bus Frame. Diesen muss man dann in die einzelnen Werte aufdröseln. Da sind wir ja gottseidank im richtigen Unterforum, da gibt’s sicher jemanden der das drauf hat :slight_smile:

Ich habe meinen Zähler über einen USB-IR-Lesekopf von Udo (Volkszähler.org) an meinen Rechner angebunden, auch bei mir kommen bereits Daten an. Somit bin ich prinzipiell in der gleichen Situation wie du. Leider fehlt es bei mir an tieferem PHP/SW/Verschlüsselungs-Know-How um in der Sache weiter zu kommen.

Gruß
Bikasso

Hi Bikasso!

Erstens freut es mich einen Mitstreiter gefunden zu haben und zweitens Megadank für die Links, ich hatte diesbezüglich noch gar nichts gefunden. Leider habe ich momentan wenig Zeit für dieses Thema, aber die nächsten freien Minuten werden ich gleich wieder weiterversuchen, vielleicht bringe ich mit Hilfe der Kollegen hier doch noch was zusammen :rolleyes:

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 :slight_smile:

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

Einfacher ist relativ :smiley:

Ich arbeite meistens lieber direkt mit dem String.
Darum hier mal zwei Varianten für jeweils zwei Anwendungsbereiche.
Die Erste und Zweite nutzen direkt den String wie er von einer RegVar kommt.
Die Dritte und Vierte wäre dann für ein Array mit Bytes.

Einmal wird mit Bitverschiebung gearbeitet, einmal mit unpack.
Ersteres braucht halt Zahlen, letzteres einen String.

/// String - RawByte
$data = "\x11\x0a\xe2\xd4"; // *3571583505‬
$int= (ord($data[3])<< 24) | (ord($data[2])<< 16) | (ord($data[1])<< 8) | ord($data[0]);  // wir brauchen Zahlen, also ord()
var_dump($int);

// String - RawByte
$data = "\x11\x0a\xe2\xd4"; // *3571583505‬
$int = unpack("L",$data)[1];
var_dump($int);

// Byte Array
$decrArray = array(66=> 0x11, 67 => 0x0a, 68 => 0xe2, 69 => 0xd4); 
$int= ($decrArray[69]<< 24) | ($decrArray[68]<< 16) | ($decrArray[67]<< 8) | $decrArray[66];
var_dump($int);

// Byte Array
$decrArray = array(66=> 0x11, 67 => 0x0a, 68 => 0xe2, 69 => 0xd4);
$int = unpack("L",chr($decrArray[66]).chr($decrArray[67]).chr($decrArray[68]).chr($decrArray[69]))[1];  // wir brauchen einen String, also chr()
var_dump($int);
int(3571583505)
int(3571583505)
int(3571583505)
int(3571583505)

Michael

Hallo Bikasso!

Ich finde es natürlich mega das du dir die Arbeit gemacht hast dieses Script zu schreiben. Ich hatte in letzter Zeit überhaupt keine Geduld mit solchen Sachen und wenn ich mir das Script so ansehe, hätt ich das so auch nie hinbekommen.

Natürlich hab ich es jetzt gleich ausprobiert, noch funktioniert es nicht bei mir, weiß leider noch nicht warum. Es läuft zwar durch, schreibt aber keine Werte in meine angelegten Variablen. Habe natürlich eben selbige, + RegVar ID + meinen Energie AG Key angepasst. Ebenso mußte ich die Suchadresse des Zählers anpassen, welche bei mir so aussieht:

$data == "\x10\x40\xF0\xC1\x64\xFC"

Dann sehe ich im Debugmode zwar das der Zähler Daten schickt, warum sie aber nicht in die Variablen geschrieben werden muß ich mir noch anschauen bzw. ob es überhaupt die richtigen Daten sind die da retour kommen…

Auf alle Fälle danke nochmal für deine Mühe und toll das du dieses Script hier zur Verfügung stellst, da werden sich in Zukunft sicher noch mehrere darüber freuen :smiley:

Okay, ich denke da komme ich noch nicht weiter! Ich hab dir mal den Debug von meiner RegVar reingestellt, irgendwie sehen die Daten bei mir ja anders aus. Kannst du das mal mit deinen vergleichen und mir vielleicht sagen ob das so stimmt wie es bei mir durchläuft?

Hallo,

@Nall Chan,
danke für die super Infos, da erkennt man die Profis:)
Die Variante mit der Bit-Verschiebung ist ja tricky, auf sowas wär ich nie gekommen. Genauso auf den String wie auf ein Array zuzugreifen. Das wäre in an deren Programmiersprachen wie z.B. in VB nicht denkbar.
Interessanterweise hatte ich auch nach längerer googlerei keine brauchbare Lösung gefunden, deshalb mein Umweg über die Hex-Strings. Dein Beitrag jedoch erklärt dies sehr gut, ich finde dein Beitrag gehört in die Rubrik nützliche Scripte oder Vorgehensweisen.

Mein Problem ist dass mir oft nicht klar ist welcher Datentyp sich in der Variable befindet, in PHP muss / kann man ja die Variablen nicht deklarieren.
Für ein besseres Verständnis hilft mir jetzt der Befehlt var_dump, den kannte ich bis jetzt auch nicht, danke :slight_smile:

$data1 = "\x11\x0a\xe2\xd4"; // *3571583505‬
$data2 = array(0=> 0x11, 1 => 0x0a, 2 => 0xe2, 3 => 0xd4);  
$test1 =   ($data1[0]);  
var_dump ($test1);
$test2 =   ($data2[0]);  
var_dump ($test2);

im Speicher sollten meiner Meinung zwar $data1 und $data2 gleich aussehen, 4 Bytes mit gleichem Inhalt aneinandergereiht, die Ausgabe des ersten Bytes ergibt aber folgendes Ergebnis:

ergibt:

string(1) ""
int(17)

Somit sollte man immer im Hinterkopf haben als welchen Datentyp PHP die Daten abspeichert.

@hofimax:
Hast du auch wirklich die Schnittstelle so eingestellt wie ich geschrieben hab?
wenn ja, du hast einen Zähler Siemens TD-3511 mit welcher Version?
Deine Debug-Daten des Frames sehen unterm Strich etwas seltsam aus. Ein MBUS-Frame beginnt immer mit 68h, dann zwei gleiche Bytes hintereinander und endet mit 16h; das 16h kann ich schon mal in deinem Debug-Print nicht sehen;
Deshalb glaub ich dass mein Script diesen Frame als Fehlerhaft sieht (was er ja dann auch ist).
Im Fenster „Meldungen“ gibt dir mein Script eh immer eine Info was nicht passt. Was bekommst du für Meldungen? mach ggf., wenn du keine Meldung von diesem Script bekommst, bei allen Echo’s die Auskommentierung weg.

Gruß
Bikasso