<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ehz &#8211; bubuxblog</title>
	<atom:link href="https://www.bubuxblog.de/tag/ehz/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.bubuxblog.de</link>
	<description></description>
	<lastBuildDate>Mon, 22 Feb 2021 18:05:44 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>
	<item>
		<title>Raspberry Pi &#8211; eHZ auslesen</title>
		<link>https://www.bubuxblog.de/raspberry-pi-ehz-auslesen/</link>
					<comments>https://www.bubuxblog.de/raspberry-pi-ehz-auslesen/#comments</comments>
		
		<dc:creator><![CDATA[diefenbecker]]></dc:creator>
		<pubDate>Wed, 13 Nov 2013 21:13:01 +0000</pubDate>
				<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[ehz]]></category>
		<category><![CDATA[Raspberry PI]]></category>
		<category><![CDATA[RaspberryPI]]></category>
		<category><![CDATA[Stromzähler]]></category>
		<guid isPermaLink="false">http://blog.bubux.de/?p=89</guid>

					<description><![CDATA[Heute mal was aus der Kategorie RaspberryPi. Ich habe versucht meinen Stromzähler gewissenhaft immer am Monatsbeginn abzulesen um eine kleine (analoge) Statistik über den Stromverbrauch zu führen. Leider hab ich schon im zweiten Monat vergessen den Zählerstand pünktlich zu notieren. Das musste also irgendwie automatisiert werden&#8230; Glücklicherweise haben wir einen digitalen Stromzähler von EMH metering [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Heute mal was aus der Kategorie RaspberryPi. Ich habe versucht meinen Stromzähler gewissenhaft immer am Monatsbeginn abzulesen um eine kleine (analoge) Statistik über den Stromverbrauch zu führen. Leider hab ich schon im zweiten Monat vergessen den Zählerstand pünktlich zu notieren. Das musste also irgendwie automatisiert werden&#8230;</p>



<p>Glücklicherweise haben wir einen digitalen Stromzähler von <a title="EMH Stromzähler" href="http://www.emh-metering.com/de/produkte/ehz-i/" target="_blank" rel="noopener">EMH metering</a> der sich prima auslesen lassen sollte. Er sendet ungefragt und ohne Unterbrechung <a title="Wikipediaartikel zu SML" href="http://de.wikipedia.org/wiki/Smart_Message_Language" target="_blank" rel="noopener">SML-Nachrichten</a> (Smart Message Language) über ein IR-Diode in die weite Welt (die meistens an der Tür des Zählerkastens endet).</p>



<figure class="wp-block-image"><a href="http://blog.bubux.de/wp-content/uploads/2013/11/IMG_3333.jpg"><img fetchpriority="high" decoding="async" width="229" height="300" src="http://blog.bubux.de/wp-content/uploads/2013/11/IMG_3333-229x300.jpg" alt="IMG_3333" class="wp-image-145" srcset="https://www.bubuxblog.de/wp-content/uploads/2013/11/IMG_3333-229x300.jpg 229w, https://www.bubuxblog.de/wp-content/uploads/2013/11/IMG_3333-784x1024.jpg 784w, https://www.bubuxblog.de/wp-content/uploads/2013/11/IMG_3333.jpg 1379w" sizes="(max-width: 229px) 100vw, 229px" /></a></figure>



<p>Als &#8222;Server&#8220; kam für mich nur der Raspberry Pi in frage. Der ist günstig in der Anschaffung und im Verbrauch. Betriebssystem ist ein <a title="raspbian.org" href="http://www.raspbian.org/" target="_blank" rel="noopener">Raspbian </a>auf einer 8GB SD-Karte.</p>



<span id="more-89"></span>



<p>Als Lesekopf habe ich mich für eine Komplettlösung entschieden die man über <a title="Volkszähler-Wiki" href="http://wiki.volkszaehler.org/hardware/controllers/ir-schreib-lesekopf-usb-ausgang" target="_blank" rel="noopener">Volkszähler.org</a> beziehen kann. Das war einfacher und günstiger als wenn man die Einzelteile in verschiedenen Geschäften zusammen suchen muss. Das Ganze kommt dann im schicken gelben Gehäuse inkl. Magnet zur Befestigung am Zähler.</p>



<p>Eine einfache Schaltung aus Phototransistor und Widerstand hätte es wahrscheinlich auch getan, aber dann fehlt noch der Seriell-USB-Wandler da ich den Lesekopf ja mittels USB an den Raspberry anschliessen wollte.</p>



<p>Der Lesekopf wird per USB an den Raspi angeschlossen und vom Linux als &#8222;/dev/ttyUSBx&#8220; erkannt. Der Treiber für den verwendeten Silabs-Chip ist im Kernel enthalten. Um den Lesekopf zu testen, kann z.B.&nbsp; minicom genutzt werden.</p>



<figure class="wp-block-image"><a href="http://blog.bubux.de/wp-content/uploads/2013/11/IMG_3334.jpg"><img decoding="async" width="300" height="224" src="http://blog.bubux.de/wp-content/uploads/2013/11/IMG_3334-300x224.jpg" alt="IMG_3334" class="wp-image-146" srcset="https://www.bubuxblog.de/wp-content/uploads/2013/11/IMG_3334-300x224.jpg 300w, https://www.bubuxblog.de/wp-content/uploads/2013/11/IMG_3334-1024x768.jpg 1024w" sizes="(max-width: 300px) 100vw, 300px" /></a></figure>



<pre class="wp-block-preformatted">sudo apt-get install minicom

sudo minicom -s</pre>



<p>Der Zähler arbeitet mit 9600 Baud, 8 bit, Parität &#8222;keine&#8220;. Diese Einstellungen und das richtige Device müssen im minicom eingestellt werden und dann sollte so was ähnliches zu sehen sein:</p>



<pre class="wp-block-preformatted">0000000 1b 1b 1b 1b 01 01 01 01 76 07 00 0a 00 6f 31 74
0000020 62 00 62 00 72 63 01 01 76 01 01 07 00 0a 00 20
0000040 10 7c 0b 06 45 4d 48 01 04 c5 6b 7d ce 01 01 63
0000060 86 9d 00 76 07 00 0a 00 6f 31 75 62 00 62 00 72
0000100 63 07 01 77 01 0b 06 45 4d 48 01 04 c5 6b 7d ce
0000120 07 01 00 62 0a ff ff 72 62 01 65 00 20 79 be 7a
...</pre>



<p>Um diese Einstellungen auch für andere Programme/Scripte/&#8230; einzustellen, ist folgender Befehl einzugeben wobei ggf. das Device (ttyUSB0) anzupassen ist:</p>



<pre class="wp-block-preformatted">stty -F /dev/ttyUSB0 1:0:8bd:0:3:1c:7f:15:4:5:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0</pre>



<p>Ist alles richtig eingestellt und der Lesekopf am Zähler angebracht, kann mit</p>



<pre class="wp-block-preformatted">cat /dev/ttyUSB0 | od -tx1</pre>



<p>geprüft werden was die serielle Schnittstelle empfängt.</p>



<p>Jetzt kommt der schwierigere Teil: Das &#8222;Entschlüsseln&#8220; des SML-Datenstroms. Folgendes Bash-Script rufe ich alle 2 Minuten per <a href="http://de.wikipedia.org/wiki/Cron" target="_blank" rel="noopener">CRON </a>auf. Das Script liest für 10 Sekunden den Datenstrom von der seriellen Schnittstelle und schreibt die empfangenen Daten in eine Datei (Pfad ggf. anpassen). Anschliessend wird ein PHP-Script zur Auswertung des aufgezeichneten Datenstroms aufgerufen.</p>



<pre class="wp-block-preformatted">#!/bin/bash

###&nbsp; Pfad und Dateinamen initialisieren
datapath=/var/www/strom/data/
fileext=_$(date +%Y%m%d%H%M%S).txt

### Daten von serieller Schhnittstelle einlesen
cp -v /dev/ttyUSB0 ${datapath}serialin${fileext}  &amp;
sleep 10
kill -TERM $!

### Script zum Auswerten des SML-Datenstroms aufrufen
php /var/www/strom/sml.php</pre>



<p>Das PHP-Script liest die eben generierte Datei ein und werten den SML-Datenstrom aus und schreibt die extrahierten Werte in eine MySQL-Tabelle. Im Script lese ich den öffentlichen Schlüssel des Zählers, den Zählerstand und die aktuelle Wirkleistung aus. Diese Werte werden in die MySQL-Tabelle eingetragen und die vom obigen Script erzeugt Datei wird wieder gelöscht.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;?php
require_once 'sml_parser.php';
$pathname='/var/www/strom/data/';

if ($handle = opendir($pathname)) {
	while (false !== ($file = readdir($handle))) {
		if ($file != "." &amp;&amp; $file != "..") {
			$files[]=$file;
		}
	}

	if(is_array($files)) {
		sort($files);
		foreach($files as $file) {
			if(substr($file,0,9)=='serialin_') {
				$sml_parser = new SML_PARSER();
				$sml_parser->parse_sml_file($pathname.$file);
				$values = $sml_parser->get_first_values();

				print_r($values);

				$time = date('Y-m-d H:i:s',filemtime($pathname.$file));
				$OBIS_1_8_1 = $values['0100010801FF']['value']*$values['0100010801FF']['scaler']/1000; # Wh -> kWh
				$public_key = $values['8181C78205FF']['value'];
				$active_power = $values['01000F0700FF']['value'];

				$connection=mysql_connect($mysqlhost, $mysqluser, $mysqlpwd) or die ("Verbindungsversuch fehlgeschlagen");
				$mysqldb="haus";
				mysql_select_db($mysqldb,$connection) or die("Konnte die Datenbank nicht waehlen.");
				$sql = "INSERT INTO stromzaehler
								(timestamp,public_key,zaehlerstand,active_power)
								VALUES (CURRENT_TIMESTAMP,'$public_key','$OBIS_1_8_1','$active_power')";
				mysql_query($sql) or die($sql);

				//Fuellt die Snapshot-Tabelle
				$sql = "UPDATE strom_snapshot SET zeitstempel=CURRENT_TIMESTAMP, zaehlerstand='$OBIS_1_8_1', wirkleistung='$active_power',
								aktuelles_jahr=(SELECT max(zaehlerstand)-min(zaehlerstand) FROM stromzaehler WHERE year(timestamp) = YEAR(NOW())),
								aktueller_monat=(SELECT max(zaehlerstand)-min(zaehlerstand) FROM stromzaehler WHERE month(timestamp) = MONTH(NOW()) AND year(timestamp) = YEAR(NOW())),
								aktueller_tag=(SELECT max(zaehlerstand)-min(zaehlerstand) FROM stromzaehler WHERE DATE(timestamp) >= DATE_SUB(NOW(),INTERVAL 1 DAY))";
				mysql_query($sql) or die($sql);
				unlink($pathname.$file);
			}
		}
	}
	closedir($handle);
}
?></pre>



<p>Das war nicht so kompliziert. Interessanter ist die eingebunde sml_parser-Klasse. Diese übernimmt das eigentliche Parsen der SML-Daten und das Auslesen der <a rel="noopener" href="http://de.wikipedia.org/wiki/OBIS-Kennzahlen" target="_blank">OBIS-Kennzahlen</a> (siehe Weblinks). Die Klasse hat ein Bekannter geschrieben der zufällig die gleiche Aufgabenstellung zur gleichen Zeit wie ich hatte.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="php" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;?php
class SML_PARSER {
        public $files;

        private $obis_arr = array(
                '0100000000FF' => array('1-0:0.0.0*255','Seriennummer'),
                '0100010700FF' => array('1-0:1.7.0*255','Momentane Wirkleistung Bezug'),
                '0100020700FF' => array('1-0:2.7.0*255','Momentane Wirkleistung Lieferung'),
                '0100010801FF' => array('1-0:1.8.1*255','Wirk-Energie Tarif 1 Bezug'),
                '0100020801FF' => array('1-0:2.8.1*255','Wirk-Energie Tarif 1 Lieferung'),
                '0100010802FF' => array('1-0:1.8.2*255','Wirk-Energie Tarif 2 Bezug'),
                '0100020802FF' => array('1-0:2.8.2*255','Wirk-Energie Tarif 2 Lieferung'),
                '0100010803FF' => array('1-0:1.8.3*255','Wirk-Energie Tarif 3 Bezug'),
                '0100020803FF' => array('1-0:2.8.3*255','Wirk-Energie Tarif 3 Lieferung'),
                '8181C78203FF' => array('129-129:199.130.3*255','Hersteller-ID '),
                '8181C78205FF' => array('129-129:199.130.5*255','Public-Key'),
                '01000F0700FF' => array('1-0:15.7.0*255','Active Power'),
                '0100010800FF' => array('1-0:1.8.0*255','Wirkarbeit Bezug Total: Zaehlerstand'),
                '0100000009FF' => array('1-0:0.0.9*255',' Geraeteeinzelidentifikation'),
                '00006001FFFF' => array('0-0:60.1.255*255','Fabriknummer'),
        );
        private $data;
        private $crc16_global;
        private $crc16_message;

        private $crctab = array( # Hilfsarray zur Berechnung der CRC
        0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
        0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
        0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
        0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
        0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
        0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
        0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
        0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
        0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
        0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
        0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
        0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
        0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
        0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
        0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
        0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
        0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
        0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
        0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
        0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
        0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
        0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
        0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
        0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
        0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
        0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
        0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
        0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
        0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
        0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
        );

        function __construct() {
        }

        function debug($text,$showhexdata=true) {
                return; # ggfs. auskommentieren.

                echo "DEBUG: '$text'";
                if($showhexdata) echo " : ". substr($this->data,0,150);
                echo "\n";
        }

        function error($message) {
                return; # ggfs. auskommentieren.

                $e = new Exception();
                $m = $e->getTraceAsString();
                $m = explode("\n",$m);
                unset($m[0]);
                $m = implode("\n",$m);

                echo("ERROR: $message ! \n".$m."\n");
        }

        function sml_crc16($part,$global=true) {
                /*  Vorlage C-Programm siehe:
                 *  http://www.photovoltaikforum.com/datenlogger-f5/emh-ehz-protokoll-t86509.html
                 */

                $cp = $this->hex2bin($part);

                for ($i=0 ; $i&lt;strlen($cp) ; $i++) {
                        $char = ord($cp{$i});

                        $this->crc16_message = ($this->crc16_message >> 8) ^ ($this->crctab[($this->crc16_message ^ $char) &amp; 0xff]);
                        if(!$global) continue;
                        $this->crc16_global  = ($this->crc16_global >> 8)  ^ ($this->crctab[($this->crc16_global  ^ $char) &amp; 0xff]);
                }
        }

        public function hex2bin($hexstr) {
                $n = strlen($hexstr);
                $sbin="";
                $i=0;
                while($i&lt;$n)
                {
                        $a =substr($hexstr,$i,2);
                        $c = pack("H*",$a);
                        if ($i==0){$sbin=$c;}
                        else {$sbin.=$c;}
                        $i+=2;
                }
                return $sbin;
        }


        private function match($match) {
                global $list_indent;

                if(substr($this->data,0,strlen($match))&lt;>$match) {
                        $this->error("'$match' expected, got '".substr($this->data,0,50)."...'");
                }else{
                        $this->sml_crc16($match);
                        $this->data = substr($this->data,strlen($match));
                        #echo "MATCH: $match\n";
                }
        }

        private function read($len) {
                if($len==0) {
                        return;
                }

                if(strlen($this->data)&lt; ($len*2)) $this->error("can't read enough bytes");
                $result = substr($this->data,0,2*$len);
                $this->data = substr($this->data,2*$len);

                $this->sml_crc16($result);
                return $result;
        }

        private function parse_sml_data($list_item=0) {
                global $list_indent;
                $TYPE_LEN = $this->read(1);

                if($TYPE_LEN=='00') {
                        return $TYPE_LEN; # EndOfSmlMessage
                }

                $TYPE = $TYPE_LEN{0}.'x';     # only high-nibble
                $LEN  = hexdec($TYPE_LEN{1}); # only low-nibble

                while($TYPE{0} &amp;0x8) {  # Multi-Byte TypeLen-Field
                        $LEN = $LEN * 0x10;
                        $TYPE_LEN = $this->read(1);
                        $TYPE = $TYPE_LEN{0}.'x';     # only high-nibble
                        $LEN  += hexdec($TYPE_LEN{1}); # only low-nibble
                        $LEN--; # 1 abziehen wegen zusätzlichem TL-Byte
                }

                if($LEN==1) return;

                switch($TYPE) {
                        case '0x': # Octet
                                #return $this->hex2bin($this->read($LEN-1));
                                return $this->read($LEN-1);
                                break;

                        case '5x': # Integer
                                return hexdec($this->read($LEN-1));
                                break;

                        case '6x': # UnsignedInt
                                return hexdec($this->read($LEN-1));
                                break;

                        case '7x': # List
                                $list_indent++;
                                for($i=1;$i&lt;=$LEN;$i++) $this->parse_sml_data($i);
                                $list_indent--;
                                break;

                        default :
                                $this->error("Error, unexpected type '$TYPE' TL=$TYPE_LEN ".$this->data);
                }

                #echo "\n";
                return $TYPE_LEN;
        }

        private function readOctet() {
                $TYPE_LEN = $this->read(1);
                if($TYPE_LEN=='01') return;

                if($TYPE_LEN{0}=='0') {
                        $LEN  = hexdec($TYPE_LEN{1}); # only low-nibble
                        $octet = $this->read($LEN-1);
                        return $octet;
                }else{
                        return "[Error, cant read octet : $TYPE_LEN]";
                }
        }

        private function readInteger() {
                $TYPE_LEN = $this->read(1);
                if($TYPE_LEN=='01') return;

                if($TYPE_LEN{0}=='5') {
                        $LEN  = hexdec($TYPE_LEN{1}); # only low-nibble
                        $integer = $this->read($LEN-1);
                        return $integer;
                }else{
                        return "[Error, cant read unsigned : $TYPE_LEN]";
                }
        }

        private function readUnsigned() {
                $TYPE_LEN = $this->read(1);
                if($TYPE_LEN=='01') return;

                if($TYPE_LEN{0}=='6') {
                        $LEN  = hexdec($TYPE_LEN{1}); # only low-nibble
                        $unsigned = $this->read($LEN-1);
                        return $unsigned;
                }else{
                        return "[Error, cant read unsigned : $TYPE_LEN]";
                }
        }

        private function readInteger8() {
                $val = hexdec($this->readInteger($this->data));
                if($val &amp; 0x80) $val = 0xfe - $val;
                return $val;
        }

        # =============================================================================================
        # High-Level SML-Funktionen
        # =============================================================================================

        private function readOpenResponse() {
                $this->match('76'); # 76 = List of 6 items
                $result['codepage']    = $this->readOctet($this->data);
                $result['clientId']    = $this->readOctet($this->data);
                $result['reqFileId']   = $this->readOctet($this->data);
                $result['serverId']    = $this->readOctet($this->data);
                $result['refTime']     = $this->readOctet($this->data);
                $result['sml-Version'] = $this->readOctet($this->data);

                return $result;
        }

        private function readCloseResponse() {
                $this->match('71'); # 71 = List of 1 item
                $result['signature']   = $this->readOctet($this->data);

                return $result;
        }

        private function readListEntry() {
                $this->match('77'); # 77 = List of 7 item

                $result['objName']          = $this->readOctet($this->data);
                $result['status']           = $this->readUnsigned($this->data);
                $result['valTime']          = $this->parse_sml_data($this->data);
                $result['unit']             = $this->readUnsigned($this->data);
                $result['scaler']           = $this->readInteger8($this->data);
                $result['value']            = $this->parse_sml_data($this->data);
                $result['valueSignature']   = $this->readOctet($this->data);

                if(isset($this->obis_arr[$result['objName']])) {
                        $result['OBIS']=$this->obis_arr[$result['objName']][0];
                        $result['OBIS-Text']=$this->obis_arr[$result['objName']][1];
                }

                if(in_array($result['objName'],array('8181C78203FF','0100000000FF','00006001FFFF'))) {
                        # ggfs. weitere objNames in die Liste aufnehmen
                        $result['value'] = $this->hex2bin($result['value']);
                }

                switch($result['unit']) {
                        case '1B' : $result['unit']='W';
                        case '1E' : $result['unit']='Wh';
                }

                if($result['scaler']) $result['scaler'] = pow(10,$result['scaler']);

                return $result;
        }

        private function readValList() {
                $this->debug('ENTER readValList');
                $TYPE_LEN = $this->read( 1);

                if($TYPE_LEN{0}=='7') {
                        $LEN = hexdec($TYPE_LEN{1});
                        for($i=0;$i&lt;$LEN;$i++) {
                                $this->debug("ENTER readListEntry [$i]");
                                $result[]=$this->readListEntry($this->data);
                        }
                        $this->debug('EXIT readValList : '.print_r($result,true),false);
                        return $result;
                }else{
                        echo('Error reading value-list!');
                }
        }

        #####################################################################################

        private function readListResponse() {
                $this->match('77'); # 77 = List of 1 item
                $result['clientId']         = $this->readOctet($this->data);
                $result['serverId']         = $this->readOctet($this->data);
                $result['listName']         = $this->readOctet($this->data);
                $result['actSensorTime']    = $this->parse_sml_data($this->data);
                $result['vallist']          = $this->readValList($this->data);
                $result['actGatewayTime']   = $this->parse_sml_data($this->data);
                $result['signature']        = $this->readOctet($this->data);

                return $result;
        }

        private function readMessageBody() {
                $this->match('72'); # 72 = List of 2 items
                $result['choice']  = $this->readUnsigned($this->data);
                switch($result['choice']) {

                        case '0101':
                                $this->debug('PROCESS OpenRequest');

                                $result['choice']='OpenRequest';
                                $result['body'] = $this->readOpenResponse($this->data);
                                break;

                        case '0201':
                                $this->debug('PROCESS CloseRequest');

                                $result['choice']='CloseRequest';
                                $result['body'] = $this->readCloseResponse($this->data);
                                break;

                        case '0701':
                                $this->debug('PROCESS GetListResponse');

                                $result['choice']='GetListResponse';
                                $result['body'] = $this->readListResponse($this->data);
                                break;

                        default:
                                $this->debug('PROCESS UnknownRequest ('.$result['choice'].')');
                                //$result['body']  = $this->parse_sml_data($this->data);
                }

                return $result;
        }

        private function parse_sml_message() {
                $this->debug('ENTER parse_sml_message');

                $this->crc16_message = 0xFFFF; # Pruefsumme zuruecksetzen

                $this->match('76');       # 76 = List of 6 items
                $result['transactionId'] = $this->readOctet();
                $result['groupNo']       = $this->readUnsigned();
                $result['abortOnError']  = $this->readUnsigned();
                $result['messageBody']   = $this->readMessageBody();

                $crc_calc = strtoupper(substr('000'.dechex(($this->crc16_message ^ 0xffff)),-4));
                $result['crc_calc'] = substr($crc_calc,-2).substr($crc_calc,0,2); # Wert 4-stellig ausgeben

                $result['crc16']         = $this->readUnsigned();
                $this->match('00');       # endOfSmlMsg = 00

                $result['crcMsgCheck'] = ($result['crc_calc'] == $result['crc16']);

                $this->debug('EXIT parse_sml_message. CRC='.(($result['crcMsgCheck'])?'OK':'FAIL'),false);
                $this->debug('--------------------------------',false);
                return $result;
        }

        # =============================================================================================
        # Schnittstellenfunktionen
        # =============================================================================================

        public function parse_sml_hexdata($hexdata) {
                $this->files = array();
                $this->data = strtoupper($hexdata);

                $sml_header='1B1B1B1B01010101';
                $sml_footer='0000001B1B1B1B1A';

                $start = strpos($this->data,$sml_header);
                if($start===false) return;

                if($start) {
                        #echo "$start bytes skipped at beginning!\n";
                        $this->data=substr($this->data,$start);
                }

                while($this->data) {
                        $skip = false;
                        $messages = array();

                        $this->crc16_global = 0xffff;
                        $this->match($sml_header);

                        while($this->data&lt;>'' &amp;&amp; substr($this->data,0,16)!=$sml_footer) {
                                $message = $this->parse_sml_message();
                                if($message['crcMsgCheck']) {
                                        $messages[] = $message;
                                }else{ # if no success, skip to next file
                                        $start = strpos($this->data,$sml_header);
                                        if($start===false) return;

                                        if($start) {
                                                #echo "$start bytes skipped in between!\n";
                                                $this->data=substr($this->data,$start);
                                                $skip=true;
                                                break;
                                        }
                                }
                        }

                        if($skip) continue;

                        $this->match($sml_footer);
                        $this->match('03');

                        $crc_calc = strtoupper(substr('000'.dechex(($this->crc16_global ^ 0xffff)),-4));
                        $crc_calc = substr($crc_calc,-2).substr($crc_calc,0,2); # Wert 4-stellig ausgeben
                        $crc16 = $this->read(2);

                        $this->files[] = array(
                            'crcFileCheck'=>($crc_calc == $crc16),
                                'messages'=>$messages
                        );
                }
        }

        public function parse_sml_string($string) {
                return $this->parse_sml_hexdata(bin2hex($string));
        }

        public function parse_sml_file($filename) {
                $this->parse_sml_hexdata(strtoupper(bin2hex(file_get_contents($filename))));
        }

        public function get_first_values() {
                foreach($this->files as $file) {
                        foreach($file['messages'] as $message) {
                                if($message['messageBody']['choice']=='GetListResponse') {
                                        $vallist = $message['messageBody']['body']['vallist'];

                                        $result = array();
                                        foreach($vallist as $value) $result[$value['objName']]=$value;

                                        return $result;
                                }
                        }
                }

                return array();
        }
}
?></pre>



<p>Grafisch dargestellt sieht dann z.B. die Wirkleistung folgendermaßen aus:</p>



<figure class="wp-block-image"><a href="http://blog.bubux.de/wp-content/uploads/2013/11/wirkleistung.png"><img decoding="async" width="1137" height="282" src="http://blog.bubux.de/wp-content/uploads/2013/11/wirkleistung.png" alt="wirkleistung" class="wp-image-149" srcset="https://www.bubuxblog.de/wp-content/uploads/2013/11/wirkleistung.png 1137w, https://www.bubuxblog.de/wp-content/uploads/2013/11/wirkleistung-300x74.png 300w, https://www.bubuxblog.de/wp-content/uploads/2013/11/wirkleistung-1024x253.png 1024w" sizes="(max-width: 1137px) 100vw, 1137px" /></a></figure>



<p>Der Wochenverbrauch sähe dann z.B. so aus:</p>



<figure class="wp-block-image"><a href="http://blog.bubux.de/wp-content/uploads/2014/02/verbrauch_m.png"><img loading="lazy" decoding="async" width="600" height="230" src="http://blog.bubux.de/wp-content/uploads/2014/02/verbrauch_m.png" alt="verbrauch_m" class="wp-image-318" srcset="https://www.bubuxblog.de/wp-content/uploads/2014/02/verbrauch_m.png 600w, https://www.bubuxblog.de/wp-content/uploads/2014/02/verbrauch_m-300x115.png 300w" sizes="auto, (max-width: 600px) 100vw, 600px" /></a></figure>



<p>Wie das genau funktioniert, stelle ich in einem späteren Artikel vor. Erstmal viel Spaß beim Auslesen des Zählers.</p>



<h4 class="wp-block-heading">Probleme mit der Berechnung der Prüfsumme</h4>



<p>Bei Problemem mit der Berechnung der Prüfsumme wie in den Kommentaren beschrieben die z.B. bei Benutzung eines anderen Stromzählers wie den von EMH auftreten kann, hilft das Auskommentieren folgender Zeilen in der Datei sml_parser.php. Das ist erstmal nur ein Workaround bis ich Zeit finde mich dem eigentlichen Problem zu widmen&#8230;</p>



<pre class="wp-block-preformatted">//if($message['crcMsgCheck']) {
	$messages[] = $message;
/*}else{ # if no success, skip to next file
        $start = strpos($this-&gt;data,$sml_header);
        if($start===false) return;

        if($start) {
	        //echo "$start bytes skipped in between!\n";
		$this-&gt;data=substr($this-&gt;data,$start);
                $skip=true;
                break;
        }
}*/</pre>



<p>Gruß<br>Chris</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bubuxblog.de/raspberry-pi-ehz-auslesen/feed/</wfw:commentRss>
			<slash:comments>97</slash:comments>
		
		
			</item>
	</channel>
</rss>
