Die Augen des Stromzählers
Gestern habe ich mich mal intensiv mit meinem Stromzähler auseinander gesetzt. Das ist ein ziemlich neumodisches Gerät, ein Iskra MT175. Dieses Modell verfügt über eine Infrarotschnittstelle (nach DIN EN 62056-21) mit der die Zählerstände ausgelesen werden, und das muss natürlich ausprobiert werden.
Zu dem Zweck habe ich mir eine Hardware gebastelt die die Daten auslesen kann. Dazu später mehr, in einem anderen Beitrag. Mein Zähler überträgt etwa alle zwei Sekunden einen Datensatz. Automatisch, ohne dass ich ihn darum bitten müsste. Wenn ich den auslese erhalte ich einen formschönen Haufen Hex-Code, ähnlich diesem:
|
1B 1B 1B 1B 01 01 01 01 76 05 01 D3 D7 BA 62 00 62 00 72 63 01 01 76 01 01 05 00 9B F2 94 0B xx xx xx xx xx xx xx xx xx xx 01 01 63 B3 78 00 76 05 01 D3 D7 BB 62 00 62 00 72 63 07 01 77 01 0B xx xx xx xx xx xx xx xx xx xx 07 01 00 62 0A FF FF 72 62 01 65 01 8A 4D 15 77 77 07 81 81 C7 82 03 FF 01 01 01 01 04 49 53 4B 01 77 07 01 00 00 00 09 FF 01 01 01 01 0B xx xx xx xx xx xx xx xx xx xx 01 77 07 01 00 01 08 00 FF 65 00 00 01 82 01 62 1E 52 FF 59 xx xx xx xx xx xx xx xx 01 77 07 01 00 01 08 01 FF 01 01 62 1E 52 FF 59 xx xx xx xx xx xx xx xx 01 77 07 01 00 01 08 02 FF 01 01 62 1E 52 FF 59 xx xx xx xx xx xx xx xx 01 77 07 01 00 10 07 00 FF 01 01 62 1B 52 00 55 xx xx xx xx 01 77 07 81 81 C7 82 05 FF 01 01 01 01 83 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx 01 01 01 63 C6 12 00 76 05 01 D3 D7 BC 62 00 62 00 72 63 02 01 71 01 63 EE 1A 00 1B 1B 1B 1B 1A 00 F3 C7 |
Wie man unschwer erkennt habe ich private Daten anonymisiert. Lacht nicht!
Nachdem ich da längere Zeit mit verbracht habe weiß ich mittlerweile ziemlich genau was da steht. Und damit andere einen besseren Einstieg finden schreibe ich das mal hier auf.
Die Sprache nennt sich Smart Message Language (SML). Es gibt auch Zähler die die Daten in anderen Formaten, oder direkt im ASCII-Format ausgeben. SML ist aber ein Standard, und wird von vielen Herstellern genutzt. Wie das funktioniert kann man zum Beispiel in der Technischen Richtlinie BSI TR-03109-1 beim Bundesamt für Sicherheit in der Informationstechnik nachlesen. Wenn man das tut kann man den Datensatz da oben tatsächlich lesbar machen:
|
1B 1B 1B 1B -- Start Escape 01 01 01 01 -- Start Übertragung Version 1 |
Die ersten vier Bytes sind einfach eine Markierung für den Anfang der Übertragung, in der zweiten Zeile steht dass wir Version 1 des Protokolls lesen.
Als nächstes müssen wir ein wichtiges Konzept verstehen. Das erste Byte der folgenden Nachricht lautet ’76‘. Man muss das als Nibbles sehen, und bei Hex-Zahlen bedeutet das Ziffer für Ziffer. Das erste Nibble ‚7‘ können wir auf Seite 42 (natürlich ) des oben verlinkten Dokumentes nachschlagen. Da steht eine Tabelle mit einer Zeile ‚X111LLLL‘. Jetzt matcht das binäre ‚X111‘ auf das erste Nibble, also haben wir es hier mit einer Liste zu tun. Das zweite Nibble gibt die Länge an, wir erwarten also eine Liste mit 6 Elementen.
Das erste Byte der folgenden Zeilen ist jeweils nach dem gleichen Schema aufgebaut. In der Regel steht das erste Nibble für den Datentypen, das zweite für die Länge — merkwürdigerweise bei einfachen Datentypen die Länge inclusive dieses Längen-Bytes. Datentyp 0 in Zeile 4 ist laut Seite 42 ein Octet String. Die 5 sagt dass dieser Teil einschliesslich der Längenangabe 5 Bytes umfasst, wir erwarten nach der Länge also noch vier Bytes. Auf Seite 17 des Dokumentes steht dass hier eine Transaktions-ID kommen muss.
Nachdem das Prinzip klar sein sollte werde ich nicht mehr alles haarklein entschlüsseln. Das heisst: ich habe schon. Aber an dieser Stelle überlasse ich das mal dem geneigten Leser.
|
76 -- SML Message mit 6 Elementen 05 01 D3 D7 BA -- transactionId 62 00 -- groupNo 62 00 -- abortOnError 72 -- messageBody 63 01 01 -- getOpenResponse 76 -- Liste mit 6 Elementen 01 -- codepage / optional 01 -- client id / optional 05 00 9B F2 94 -- reqFileId 0B xx xx xx xx xx xx xx xx xx xx -- server Id 01 -- refTime / optional 01 -- smlVersion / optional 63 B3 78 -- CRC 00 -- End of SML message |
Man sieht durch die Einrückung ziemlich deutlich dass man sich den Datensatz gut als Liste von Listen vorstellen kann. Außen wird eine Liste mit sechs Elementen angekündigt (76), darin stehen ein Octet String (beginnt mit 0, Zeile 4), zwei Unsigned Integer (6, Zeilen 5 und 6), eine weitere Liste mit zwei Elementen (72, Zeile 7), ein weiterer Unsigned Integer (6, Zeile 16) mit der Prüfsumme und eine Markierung für das Ende der Nachricht (00, Zeile 17). Die Liste mit den zwei Elementen enthält wiederum einen Unsigned Integer (6, Zeile 8 ) und eine Liste mit sechs Elementen (76, Zeile 9). Die meisten dieser sechs Elemente sind Octet Strings die samt des Längen-Bytes eine Länge von 1 haben (01), also leere Strings. Ein String (Zeile 12) enthält eine File-ID, einer (Zeile 13) die Server-ID. Letztere kann man auch direkt auf dem Gerät lesen, die ist aufgedruckt. Beruhigend.
Jetzt wird es ernst: eine weitere Liste mit sechs Elementen (76), die ersten Zeilen entsprechen dem Block oben:
18 19 20 21 22 23 24 25 26 27 28 29 30
|
76 05 01 D3 D7 BB 62 00 62 00 72 63 07 01 -- getListResponse 77 01 -- clientId / optional 0B xx xx xx xx xx xx xx xx xx xx -- serverId 07 01 00 62 0A FF FF -- listName / optional 72 -- actSensorTime / optional 62 01 -- choice: secIndex 65 01 8A 4D 15 -- secIndex (uptime) |
Enthalten ist eine Liste mit zwei Elementen (72, Zeile 22). Der erste Octet String (Zeile 23) gibt den Typ der Nachricht an, es ist ein getListResponse. Der wiederum besteht auf sieben Elementen (77, Zeile 24). Interessant ist hier vielleicht das vierte Element (72, Zeile 28. An dieser Stelle soll die aktuelle Zeit stehen, und die kann auf verschiedene Weise angegeben werden. Erklärt ist das auf Seite 22 des BSI-Dokumentes, dementsprechend haben wir es hier durch die 01 in Zeile 29 mit einem secIndex zu tun. In Zeile 30 folgt dann der Wert. Die Zahl 0x018A4D15 entspricht in etwa der Zeit die das Gerät hier eingebaut ist, das wird also eine Art Betriebsstundenzähler sein.
Die Liste die in Zeile 31 startet ist das fünfte Element der Liste aus Zeile 24. Und hier kommt der wirklich spannende Teil: die Messdaten. Naja, und ein paar Meta-Daten. Erst das Kürzel des Herstellers Iskra (ASCII-Codes in Zeile 38), dann nochmal die bereits bekannte Server ID. Die nächsten drei Elemente enthalten die verbrauchte Energie (die Kilowattstunden), sowohl als Summe als auch aufgesplittet in zwei Tarife — wenn man denn zwei Tarife hat.
Hier kommen wir übrigens zu einem Teil den ich noch nicht verstanden habe: in Zeile 50 wird ein Status angegeben. Wenn mir jemand sagen kann was der bedeutet: immer her damit! 0x0182 ist dezimal 386, darauf kann ich mir keinen Reim machen.
Die Energiewerte muss man übrigens durch 10.000 teilen um auf die kWh zu kommen die am Gerät angezeigt werden.
In Zeile 78 steht die aktuelle Leistung, also wie viel Watt tatsächlich in diesem Moment verbraucht werden. In Zeile 86 folgt ein ‚public key‘. Der steht auch auf dem Gerät, ich nehme an der ist relevant wenn der Zähler wirklich ’nach Hause telefoniert‘. Die SML-Kommunikation funktioniert nämlich nicht nur über die Infrarot-Schnittstelle, sondern bei Bedarf auch über die Stromleitung. So kann der Anbieter Verbrauchswerte ablesen ohne dass dafür jemand zu Besuch kommen muss.
Die beiden Werte in Zeile 88 und 89 sind optional und vervollständigen so die Liste die in Zeile 24 begonnen wurde.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
|
77 -- valList 77 -- SML_ListEntry 07 81 81 C7 82 03 FF -- objName 01 -- status / optional 01 -- valTime / optional 01 -- unit / optional 01 -- scaler / optional 04 49 53 4B -- value -- Herstelleridentifikation (ISK) 01 -- valueSignature / optional 77 07 01 00 00 00 09 FF 01 01 01 01 0B xx xx xx xx xx xx xx xx xx xx -- Server ID 01 77 07 01 00 01 08 00 FF 65 00 00 01 82 01 62 1E 52 FF 59 xx xx xx xx xx xx xx xx -- Gesamtverbrauch 01 77 07 01 00 01 08 01 FF 01 01 62 1E 52 FF 59 xx xx xx xx xx xx xx xx -- Verbrauch Tarif 1 01 77 07 01 00 01 08 02 FF 01 01 62 1E 52 FF 59 xx xx xx xx xx xx xx xx -- Verbrauch Tarif 2 01 77 07 01 00 10 07 00 FF 01 01 62 1B 52 00 55 xx xx xx xx -- Wirkleistung total 01 77 07 81 81 C7 82 05 FF 01 01 01 01 83 02 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx -- public key 01 01 -- listSignature / optional 01 -- actGatewayTime / optional |
Oh, noch ein interessanter Punkt zu Zeile 86: das Nibble ‚8‘ ist eigenlich eine 0, also wieder ein Octet String. Da aber das Most Significant Bit gesetzt ist wird daraus eine 8, und das bedeutet dass die Länge der folgenden Daten nicht nur durch das zweite Nibble — die 3 — angegeben wird, sondern zusätzlich durch das folgende Byte. Nützlich, denn mit nur einem Nibble könnte man maximal 15 Bytes ankündigen (nachdem eines für die Länge draufgegangen ist). So kommen wir auf 0x32 == 50 Bytes. Ausreichend also für 48 Bytes Public Key und zwei Bytes Längenangabe.
Es folgen noch eine Prüfsumme und das förmliche Ende der zweiten Nachricht:
|
63 C6 12 -- crc 00 -- end of message |
Die dritte Nachricht ist einfach nur da um das Ende der Übertragung anzukündigen.
92 93 94 95 96 97 98 99 100 101 102 103
|
76 05 01 D3 D7 BC 62 00 62 00 72 63 02 01 -- getCloseResponse 71 01 63 EE 1A -- crc 00 -- end of message 1B 1B 1B 1B -- end escape 1A 00 F3 C7 |
Zu guter Letzt folgt in Zeile 102 nochmal die vom Anfang bekannte Escape-Sequenz, dann 1A um wirklich die Nachricht abzuschließen und nochmal drei Bytes für eine Prüfsumme.
Jetzt noch was in eigener Sache: viele Blogs schließen ihre Artikel grundsätzlich mit einer Frage an die Leser, um Kommentare zu fischen. Ich finde das penetrant, und mache das in der Regel nicht. Aber nach diesem furztrockenen (!) Artikel erlaube ich mir das ausnahmsweise mal: bitte ein Handzeichen von allen die bis hier durchgehalten haben! Muss kein Lob sein, ein Hallo Welt genügt.