SML Protokoll identifiziren und zerlegen

Das nächste hat angekündigt sowas als Bug mitzubringen...
Aber ich kann recht gut schreiben. :wink:

Landis+Gyr und Konsorten werden sich ja nicht selbst ins Knie schiessen.
Wobei zuztrauen wäre das denen auch um noch ein paar MDE's abzuverkaufen...

1 Like

Bei mir sieht es mit einem Testdatensatz derzeit so aus:

Verbindung zum Router .
Verbunden mit: FRITZ!Box 7590 UN
HTTP Server gestartet

Start-Sequenz gefunden
Server-ID gefunden
Ende-Sequenz gefunden
1.8.0 Wirkenergie Total Bezug:        283612200
2.8.0 Wirkenergie Total Lieferung:        29637
1.8.1 Wirk Energie Tarif 1 Bezug:         28515
2.8.1 Wirk Energie Tarif 1 Lieferung:          0
1.8.2 Wirk Energie Tarif 2 Bezug:      283583685
2.8.2 Wirk Energie Tarif 2 Lieferung:          0

Die Quick-and-Dirty-Variante mit der Suche nach Zeichenketten führt mich zum Ergebnis. Das werde ich mit aktuellen Datensätzen überprüfen, um die noch nicht erfaßten Möglichkeiten zu finden.

Zum Webserver können wir uns gerne austauschen, allerdings wohl besser in einem eigenen Thema "SML Daten mittels Webserver auswerten" oder so. Später könnte man dann dieses Thema mit der normgerechten Lösung mit der Quick-and-Dirty-Variante verbinden :wink:

1 Like

Ok.. Ich gehe davon aus, das das ein Testdatensatz ist. Sonst würde das irgendwie nicht passen :wink:
Wie bereits schon geschrieben, das mit dem Auswerten durch Vergleich auf bestimmte Zeichenfolgen geht und machen andere ja auch. Es ist nur schade, das es eben nicht durchgängig nutzbar ist, weil das eine Konstellation ist die nur zufällig gerade passt...

Ja, es ist ein Datensatz von meinem Zähler, den ich zur Programmerstellung verwendet habe.

Ups..
Du hast weder in Tarif1 noch in Tarif2 Wirkenergie Lieferung, aber in Summe?
Eigentlich müsste die 2.8.0 in 2.8.1 / 2.8.2 wieder auftauchen...
Kann es sein, das Du ein BalkonKW dran hast?
Das würde den Verlust der Tarifierung begründen.

Hallo
Habe ich auch gerade bemerkt. @agmue da solltest du die Rohdaten noch mal checken.

Ich denke wenn kein Tarif geschaltet wird dann sollte das Tarif 1 sein.
Damit wäre eine PV egal welche immer T1 damit 2.8.0 identisch mit 2.8.1
Jedenfalls denke ich sollte 2.8.0=2.8.1+2.8.2 sein

Nicht unbedingt. Das ist eine 2Tarif-einstellung. die Summen aus 1.8.1 und 1.8.2 ergeben sauber 1.8.0
Es könnte auch unter 2.8.2 auftauchen. Muss aber nicht, wenn nicht tarifiert wird.

Nein.

Im Display wird 1.8.0 angezeigt, was ich an meinen Stromversorger melde, bzw. von ihm abgelesen wird.

Das werde ich tun.

Derzeit sammle ich Daten in einer Datei.

Bitte vergeßt aber @Muecke nicht :wink:

1 Like

Das ist die "Aufnahme über alles"
(1=Bezug, 2=Abgabe . 8=Zeitraum über Alles . 0 Total, 1..9 Tarif) ,
Es wird bei der Kennziffer 0 nicht von Summe gesprochen.

Was für ein Zaehler ist das? IMHO können die doch auch noch andere Dinge anzeigen....

1 Like

Foto bei #96.

Ach schau, für den ED300L gibt es sogar eine schicke Anleitung.
Hab ich mir grad mal angetan.
Passt dann mit meinen bisherigen Erkenntnissen zum Aufbau des Telegrams :wink:
Aber offensichtlich kann der im Modus E21 tatsächlich nur 1.8.0 anzeigen... Schade.

Vielleicht rückt der Versorger die PIN für den erweiterten Datensatz heraus? Das war bei unserem kein Problem, nur die Eingabe mit der Taschenlampe war etwas frickelig.

1 Like

Hallo ich muss mich noch mal auf Deine Aufbröselei #105 beziehen, danke dafür das hat mir nach längrem studieren ein Brett vom Kopf genommen.

Dein Ansatz besteht ja darin sich von Typ/Length zu Typ/Length zu hangeln und die dazu gehörigen Daten dazwischen zu sammeln. Letzlich wie in der Spez vom BSI in Kapittel 6 beschrieben Soweit bin ich da bei Dir. Damit würde das auch bei unterschiedlichen Längenagaben passen.

Irgendwann kommt man dann z.B bei der Auswertung zur Liste 7 und zu einem Ergebniss das könnte man irgedwie lesbar ausgeben aber wie zuordnen ?

77           List 7 Elemente             3/9. Element
     07 01 00 01 08 00 ff                     1/7. Element
     64 1c 01 04 72                           2/7.
     62 01                                    3/7.
     65 00 23 c9 77                           4/7.
     62 1e                                    5/7.
     52 ff                                    6/7.
     69 00 00 00 00 00 46 29 36 01            7/7.

z.B Ausgabe

Code 1.8.0 xxxx yyyyy 000123456.7Wh

wobei xxxx für das Elemt 2 und yyyy für das Elemt 4 steht
Mit dem vorhergehenden OBIS Code 180 der Liste kann man nun noch die nachfolgenden Daten der 180 zuordnen. Nur wo wird festgelegt um was für Daten es sich handet ? ok beim letzten steht noch eine physikaische Einheit vor in dem Fall die 0x1E für Wh
Mir fehlt da im Moment völlig ein Ansatz was man da machen könnte,
Heinz

Dein zweites Element passt irgendwie nicht, nach 64 dürften nur 3 Bytes folgen. Die 72 dahinter startet eine Liste mit 2 Einträgen, die als ein Eintrag in der Siebenerliste gezählt wird.

Genauso gehört die letzte 01 nicht zum Wert, sondern ist das letzte Element der Siebenerliste.

Ich habe die Spezifikation nicht hier, aber eines der Listenelemente gibt die Skalierung an, also durch welche Zehnerpotenz man den Wert am Ende teilen muss, um den eigentlichen Fließkomawert zu erhalten.

[EDIT] Schau mal hier, da hat sich jemand die Mühe gemacht, das aufzudröseln.

2 Likes

Das wäre sehr nett :slight_smile:

So habe ich das bisher auch verstanden.
Und diese Daten kann man sich dann einzeln ausgeben lassen. (auf dem Seriellen Monitor, und später über eine Web Schnittelle so das man keinen externen Server benötigt).

Das Problem habe ich schon von Anfang an.
Aufgrund der bisherigen anderen Probleme habe ich das ignoriert.
Inzwischen komme ich bei eurem Code`s gar nicht mehr mit ich habe total den Überblick verloren :frowning:

Gerne würde ich auch Info dagegensetzen dazu beitragen, und versuchen den Code zu verstehen. :frowning:

Gruß Mücke

Unsere Stromzähler sind unterschiedlich, daher bräuchte ich Daten von Dir. Ich habe gestern 298 meiner Datensätze gespeichert, die ich durchforsten möchte. Das könnte ich dann auch mal mit Deinen Daten probieren. Sobald was Vernüftiges rauskommt, würde ich es Dir auch erklären wollen. Mein Vorschlag daher, speichere Deine Datensätze in eine Datei.

1B 1B 1B 1B 01 01 01 01 76 07 00 13 0F 53 ...
1 Like

(Vorab: Ich hatte oben schon geschrieben, das ich irgendwann die Liste nicht enDetail aufgelöst habe, weil die Struktur ja bekannt ist - sorry für die Verwirrung)
Es gibt zwei voneinander getrennte Dinge.
Der Aufbau der Nachricht im zweiten Block ist im Block selbst versteckt.
Aus #105 Ab Zeile 22:

// Zweiter Block
76             (List 6 Elemente)
 05 00 a0 d9 b7 (TID)                     1/6. Element
 62 00          (GID)                     2/6. Element
 62 00          (Errorhandle)             3/6. Element
 72            (List 2 Elemente)          4/6. Element
  63 07 01      (TYP) (SML_GetList.Res)    1/2. Element
  77            (Inhalt) 7 Elemente        2/2. Element

Da wo jetzt TYP steht, ist versteckt, was als nächstes kommt.
Die gesamte Typenliste findet sich in der Detailspec Anhang IVb die ich oben verlinkt habe auf Seite 20.
Uns interessieren nur alle die auf .res (für Response) enden.
In diesem Fall also SML_GetProfileList.Res.
Was da drin steht, ist in 5.1.8 (Seite 29) beschrieben.
Und von dort aus durchhangeln u.s.w.
Was jetzt folgt ist:

// ->>>> Ende Kopfdaten <<<<<-
   79            List 9 Elemente            5/7. Element
    77           List 7 Elemente             1/9. Element

Und ab der ersten Liste (77) bestimmt der Hersteller was kommt.
Diese Liste wurde mit 79 (Es folgt eine Liste mit 9 Elementen) angekündigt. Die kann mit jeder Ausgabe auch anders sein!
Das Format ist vorgegeben. Hangeln wir uns mal durch:
In SML_GetList.Resinteressiert uns die valList.
Die hat den Typ SML_List.
Der Typ ist SML_List hat einen valListEntry vom Typ SML_ListEntry.
Und SML_ListEntry ist widerum vom Typ SEQUENZ und das ist das, was Du jetzt siehst.
Am Beispiel Deiner 1.8.0:

    77           List 7 Elemente             3/9. Element
     07 01 00 01 08 00 ff                     1/7. Element
     64 1c 01 04                              2/7.
     72                                       3/7.
      62 01                                    
      65 00 23 c9 77                           
     62 1e                                    4/7.
     52 ff                                    5/7.
     69 00 00 00 00 00 46 29 36               6/7.
     01                                       7/7.

Wichtig ist dann jeweils der erste Eintrag.
Der beginnt mit der OBIS-Kennzahl.
01 - Elektrische Energie
00 - Kanalnummer
01 - Wirkenergie Bezug (02 - Wirkenergie Abgabe)
08 - Zeittyp1 (09 Zeittyp2)
00 - Total (01 ... 09 Tarif 1 bis Tarif 9)
Die Zeilen 2 - 5 sind Optional. Wenn die mit 01 anfangen, folgt nix. Ansonsten inhaltlich das, wie es in der Beschreibung steht.
Bei Dir also
Element 2: (Unsigned 4 Bytes) Status
Element 3: (List) Zeit - bestehend aus 2 Elementen. 1.: TYP 2. Inhalt
Element 4: (Unsigned 2 Byte) Einheit - Kwh
Element 5: (Signed 2 Byte) Skalerierung
Element 6: (Unsigned 9 Byte) Dein Verbrauchswert
Element 7: Signatur (Optional und leer)

In den ersten beiden Blöcken stehen andere Dinge drin. Die komplette Liste der Kennzahlen fehlt mir leider. Aber normalerweise liefert jeder Zählerhersteller eine Tabelle oder zumindest eine Liste, in der drin steht, was als PUSH auf der optischen Schnittstelle kommt....

Soweit beantwortet?
[Zwei Nachträge:]
Die Längenangaben oben beinhalten immer das TypeLength-Byte! Also das erste Byte, was ankündigt, was da und in welcher Länge kommt.
Für die ersten beiden Einträge gibt es eine Erklärung in @agmue seiner Kennziffern.
Dort wäre das erste Element gestartet mit 81 81 C7 82 03 FF für die Angabe des Herstellers und das zweite startet mit 01 00 00 00 09 FF für die Zählernummer.
Im Feld Value stehts dann im Klartext.
Weitere 81 81 C7 Felder finden sich in Kapitel 1.1.13....

5 Likes

Der hier gibt Dir immer die Nachrichtenlänge aus. Der Wert muss noch korrigiert werden, aber daraus liesse sich der Puffer bauen.

// Serielle Schnittstelle TCRT5000 zum ISKRA-MT681
#define RXD2 15                         // TCRT5000 (D0)
#define TXD2 13                         // ohne belegung
#define Baudrate 9600                   // vorgabe vom Stromzähler
#define Protocol SERIAL_8N1             // vorgabe vom Stromzähler
// Serielle Schnittstelle ESP32 zu PC
#define Baudrate_USB 115200             // 

byte sequenzIndex;

uint16_t msgLen;
uint16_t msgPos;

bool isStart;
bool isEnde;
bool isNew = true;;

void setup()
{
  // Serial1.begin(9600);
  Serial1.begin(Baudrate, Protocol, RXD2, TXD2);
  Serial.begin(Baudrate_USB);
}

void einlesen()
{
  if (Serial1.available())                      // Daten im Serial1 ?
  {
    byte inByte = Serial1.read();               // auslesen
    findStartEnd(inByte);
    if (isStart && !isEnde)                     // Start ja, Ende nein
    {
      msgPos++;
    }
  }
}

void findStartEnd(const byte myByte)
{
  switch (sequenzIndex)                         // Suche ESC und Folgebytes
  {
    case 0:                                     // 4 Bytes für ESC-Sequenz 0x1b
    case 1:
    case 2:
    case 3:
      myByte == 0x1B ? sequenzIndex++ : sequenzIndex = 0;
      break;
    case 4:                                    // Festlegung was als nächstes kommt
      switch (myByte)
      {
        case 0x1A:                             // EndeKennung bei SML-Version 1
          isEnde = true;
          isStart = false;
          sequenzIndex = 0;
          break;
        case 0x01:                             // Erste StartKennung 0x01
          sequenzIndex++;
          break;
      }
    /* FALLTHRU */
    case 5:                               // 3Bytes Inhalt je nach SML-Version
    case 6:
      myByte == 0x01 ? sequenzIndex++ : sequenzIndex = 0;
      break;
    /* FALLTHRU */
    case 7:
      if (myByte == 0x01)        // Das 4te byte nach ESC ist auch richtig?
      {
        Serial.println(F("Startsequenz vollständig erkannt"));
        sequenzIndex++;
        isStart = true;
        msgPos = 0;
        // isEnde = false; // wird hier nicht gebraucht, während Ausgabe gelöscht
      }
      else
      {
        sequenzIndex = 0;
      }
      break;
    default:
      sequenzIndex = 0;
      break;
  }
}

void ausgeben()
{
  if (isEnde)
  {
    Serial.print(F("Länge Nachrichteninhalt: "));
    Serial.println(msgLen);
    isEnde = false;
  }
}
void loop()
{
  einlesen();
  ausgeben();
}

@Miq1 Ich würde aus dem, nachdem Start erkannt wurde, immer in 4-Byte Häppchen puffern und den Puffer auf erneutes ESC prüfen. Wenn true, wäre das erste Byte des nächsten 4-Byte Block auf Ende zu prüfen.
Wenn kein ESC dann wird das irgendwas inhaltliches, was dann aufgedröselt werden könnte.
Andere Idee?

1 Like

Zuviel Speicherkapazität.. Eindeutig. :wink:
Das aufdröseln kannst Du, wenn Du die Liste mit dem Nachrichtenformat verwendest.
Der Block 1 ist klar.
Der Block 2 auch, bis zu dem Punkt, wo die Kopfdaten enden.
Inhallich, was in den Listen (77) steht, habe ich gerade eben erläutert.
Damit solltest Du eigentlich alles raus bekommen.

1 Like

Ich bin eigentlich ein großer Fan von State machines und würde vermutlich byteweise lesen und auf eine ESC-Sequenz nur warten, wenn eine Datenstruktur abgeschlossen ist.