Gå til innhold
  • Bli medlem
Støtt hjemmeautomasjon.no!

Enda en HAN til MQTT dekoder


Anbefalte innlegg

Jeg innser at verden neppe har behov for enda et slikt program, men jeg er protokoll-nerd og klarte ikke helt å være fornøyd med en strategi der du bare finner verdier basert på relativ posisjon eller evt et søk etter OBIS-koden som en byte-sekvens.  Måtte bare tygge tilstrekkelig på disse DLMS/COSEM pakkene til å forstå hele strukturen ?

 

 
Resultatet finner dere her: https://github.com/bmork/obinsect
 
Jeg kjører denne på et Ubiquity UniFi AP AC Pro aksesspunkt med OpenWrt, ettersom det allerede stod ganske nær sikringsskapet og hadde en ledig USB-port,  med en slik dings til selve HAN-tilkoblingen: https://www.ebay.com/itm/173715712894

 

Dette er altså en HAN til MQTT proxy som antageligvis er noe mer fleksibel enn de fleste andre alternativene, ettersom jeg først parser hele pakken og bygger opp en slags kopi som et JSON object.  Deretter bruker jeg en konfigurerbar del av dette objektet i en eller flere MQTT-meldinger.  Dvs at det er mulig å publisere et variabelt sett med variable til flere MQTT emner, basert på den samme input-pakken fra måleren. For "effektivitetens" skyld postes selvsagt ingenting til emner som ikke inneholder oppdaterte variable.

 

 

F.eks. poster jeg akkurat nå alle målerverdiene med to forskjellige nøkkelsett: OBIS kode og et alias for OBIS-koden (dette er selvsagt ikke så nyttig, men illustrerer mulighetene):
 

{
  "1-1:0.2.129.255": "AIDON_V0001",
  "0-0:96.1.0.255": "7359992895319515",
  "0-0:96.1.7.255": "6525",
  "1-0:1.7.0.255": 0.9470000000000001,
  "1-0:2.7.0.255": 0,
  "1-0:3.7.0.255": 0,
  "1-0:4.7.0.255": 0.047,
  "1-0:31.7.0.255": 3.7,
  "1-0:71.7.0.255": 3.9000000000000004,
  "1-0:32.7.0.255": 247.70000000000002,
  "1-0:52.7.0.255": 246.60000000000002,
  "1-0:72.7.0.255": 247.20000000000002,
  "timestamp": 1559131080
}
{
  "ListId": "AIDON_V0001",
  "SerialNumber": "7359992895319515",
  "Model": "6525",
  "Power": 0.9470000000000001,
  "PowerExport": 0,
  "ReactivePower": 0,
  "ReactivePowerExport": 0.047,
  "CurrentL1": 3.7,
  "CurrentL3": 3.9000000000000004,
  "VoltageL1": 247.70000000000002,
  "VoltageL2": 246.60000000000002,
  "VoltageL3": 247.20000000000002,
  "timestamp": 1559131080
}

 

 

Samtidig poster jeg også litt  andre data-sett fra de samme pakkene til litt andre MQTT emner.  F.eks. sender jeg øyeblikkseffekten alene til et emne som er eksportert via en websocket lister, slik at jeg kan bruke det direkte fra paho mqtt klienten i en browser.  Siden dette bare er en verdi, så blir denne ikke wrappet i JSON (selv om det jo forsåvidt fremdeles er gyldig JSON):

 

$ mosquitto_sub -v -h 192.168.0.1 -t /obinsect/websocket/\#
/obinsect/websocket/power 0.95100000000000007
/obinsect/websocket/power 0.94900000000000007
/obinsect/websocket/power 0.94600000000000006

 

Som dere sikkert også legger merke til, så skalerer jeg verdiene.  Og som vanlig gir flyttallsdivisjon litt stygge tall ettersom det blir litt feil.  Dette er basert på OBIS liste-dokumentasjonen fra NVE, der alle ser ut til å sende effekt som W men oppgir at det er kW skalert til et 4.3 format.  Dette spiller jo selvsagt liten rolle, men jeg har nå valgt å gjøre det slik.  Det kan slås av med en kommanolinje-option om du foretrekker uskalerte verdier.  Helst skulle jo selsagt de skalerte verdiene vært vist med korrekt oppløsning, slik at effektene ovenfor var 0.951, 0.949 og 0.946.  Men dette er JSON double verdier, og ikke tekst.  Dermed blir den slags avrunding/korting opp til JSON parseren.

 

Jeg har forsåvidt også en modus som publiserer verdiene som tekst med enhet, men det er normalt ikke spesielt nyttig siden du da må parse i den andre enden.  Men da formatteringen blir penere da.

 

Håper dette kan være nyttig for andre enn meg. Det hjalp ihvertfall meg mye å se COSEM-pakkene som et JSON-object.  Spesielt for å skjønne forskjellenen på strukturen de tre leverandørene har valgt.  Tar helt til slutt med et ekspempel på hvordan det objektet ser ut for hver av de tre. Dette er ment for debugging og er ikke veldig nyttig som output til vanlig

 

Kamstrup:


 

{
  "metadata": {
    "timestamp": 1559132486,
    "framelength": 226,
    "framenumber": 1,
    "srcprog": "./obinsectd",
    "srchost": "miraculix",
    "version": "0.01",
    "serialport": "stdin",
    "parsetime": 1431
  },
  "hdlc": {
    "format": 10,
    "segmentation": false,
    "length": 226,
    "src": 43,
    "dst": 33,
    "control": 19,
    "hcs": 39459,
    "fcs": 58715
  },
  "llc": {
    "lsap": 230,
    "dsap": 231,
    "quality": 0
  },
  "data-notification": {
    "long-invoke-id-and-priority": 0,
    "date-time": 946762380,
    "notification-body": {
      "visible-string-0": "Kamstrup_V0001",
      "obis-1": "1-1:0.0.5.255",
      "visible-string-2": "5706567000000000",
      "obis-3": "1-1:96.1.1.255",
      "visible-string-4": "000000000000000000",
      "obis-5": "1-1:1.7.0.255",
      "double-long-unsigned-6": 0,
      "obis-7": "1-1:2.7.0.255",
      "double-long-unsigned-8": 0,
      "obis-9": "1-1:3.7.0.255",
      "double-long-unsigned-10": 0,
      "obis-11": "1-1:4.7.0.255",
      "double-long-unsigned-12": 0,
      "obis-13": "1-1:31.7.0.255",
      "double-long-unsigned-14": 0,
      "obis-15": "1-1:51.7.0.255",
      "double-long-unsigned-16": 0,
      "obis-17": "1-1:71.7.0.255",
      "double-long-unsigned-18": 0,
      "obis-19": "1-1:32.7.0.255",
      "long-unsigned-20": 0,
      "obis-21": "1-1:52.7.0.255",
      "long-unsigned-22": 0,
      "obis-23": "1-1:72.7.0.255",
      "long-unsigned-24": 0
    }
  }
}

 

Kaifa:


 

{
  "metadata": {
    "timestamp": 1559132574,
    "framelength": 121,
    "framenumber": 2156,
    "srcprog": "./obinsectd",
    "srchost": "miraculix",
    "version": "0.01",
    "serialport": "stdin",
    "parsetime": 33
  },
  "hdlc": {
    "format": 10,
    "segmentation": false,
    "length": 121,
    "src": 1,
    "dst": 513,
    "control": 16,
    "hcs": 37760,
    "fcs": 49191
  },
  "llc": {
    "lsap": 230,
    "dsap": 231,
    "quality": 0
  },
  "data-notification": {
    "long-invoke-id-and-priority": 1073741824,
    "date-time-bug": true,
    "date-time": 1505416990,
    "notification-body": {
      "octet-string-0": "KFM_001",
      "octet-string-1": "6970631401753985",
      "octet-string-2": "MA304H3E",
      "double-long-unsigned-3": 1176,
      "double-long-unsigned-4": 0,
      "double-long-unsigned-5": 131,
      "double-long-unsigned-6": 0,
      "double-long-unsigned-7": 2208,
      "double-long-unsigned-8": 3841,
      "double-long-unsigned-9": 3794,
      "double-long-unsigned-10": 2389,
      "double-long-unsigned-11": 0,
      "double-long-unsigned-12": 2397
    }
  }
}

Aidon:


 

{
  "metadata": {
    "timestamp": 1559132627,
    "framelength": 210,
    "framenumber": 1,
    "srcprog": "./obinsectd",
    "srchost": "miraculix",
    "version": "0.01",
    "serialport": "stdin",
    "parsetime": 192
  },
  "hdlc": {
    "format": 10,
    "segmentation": false,
    "length": 210,
    "src": 65,
    "dst": 2179,
    "control": 19,
    "hcs": 54914,
    "fcs": 50400
  },
  "llc": {
    "lsap": 230,
    "dsap": 231,
    "quality": 0
  },
  "data-notification": {
    "long-invoke-id-and-priority": 1073741824,
    "notification-body": [
      {
        "obis-0": "1-1:0.2.129.255",
        "visible-string-1": "AIDON_V0001"
      },
      {
        "obis-0": "0-0:96.1.0.255",
        "visible-string-1": "7359992890941742"
      },
      {
        "obis-0": "0-0:96.1.7.255",
        "visible-string-1": "6515"
      },
      {
        "obis-0": "1-0:1.7.0.255",

        "double-long-unsigned-1": 1362,
        "structure-2": {
          "integer-0": 0,
          "enum-1": 27
        }
      },
      {
        "obis-0": "1-0:2.7.0.255",
        "double-long-unsigned-1": 0,
        "structure-2": {
          "integer-0": 0,
          "enum-1": 27
        }
      },
      {
        "obis-0": "1-0:3.7.0.255",
        "double-long-unsigned-1": 996,
        "structure-2": {
          "integer-0": 0,
          "enum-1": 29
        }
      },
      {
        "obis-0": "1-0:4.7.0.255",
        "double-long-unsigned-1": 0,
        "structure-2": {
          "integer-0": 0,
          "enum-1": 29
        }
      },
      {
        "obis-0": "1-0:31.7.0.255",
        "long-1": 93,
        "structure-2": {
          "integer-0": 255,
          "enum-1": 33
        }
      },
      {
        "obis-0": "1-0:32.7.0.255",
        "long-unsigned-1": 2500,
        "structure-2": {
          "integer-0": 255,
          "enum-1": 35
        }
      }
    ]
  }
}

 

Så mens Kaifa bare sender en straight liste med verdier, så sender altså Aidon en liste med objekter, der hvert objekt består av en OBIS kode og en verdi. I tilegg har alle numeriske objekter et objekt med en integer og en enum.  Det siste jeg har ikke klart å finne dokumentert hva det betyr.  Jeg gjetter på at det er en slags beskrivelse av tallverdien, der enumen kanskje representerer enhet, men det hadde jo vært fint å finne noe dokumentasjon.

 

Nie, jeg kommer nok ikke til å kjøpe noen IEC-standarder.  Det er jo noe av de mer ubrukelige dokumenter som finnes.  Bare se på det ovenfor og forklar hvordan tre leverandører kunne komme opp med en så totalt forskjellig tolk ning av hvordan pakkene skulle se ut ?

 

 

  • Like 2
  • Thanks 2
Lenke til kommentar
Del på andre sider

7 hours ago, Bjørn Mork said:

... en integer og en enum.  Det siste jeg har ikke klart å finne dokumentert hva det betyr. 

Tror jeg kan svare på det. Jeg har et dokument som heter BS EN 62056-6-2:2013. Det var en vennlig sjel som la det ut på forumet her for en god stund siden.

BSI Standards Publication
Electricity metering data exchange — The DLMS/COSEM suite
Part 6-2: COSEM interface classes

 

På side 30 finner man "Table 3 – Enumerated values for physical units". 

F.eks 27 = W, 28 = VA, 29 = VAR, 33 = A, 35 = V

 

Godt jobba!

  • Thanks 1
Lenke til kommentar
Del på andre sider

10 hours ago, Hårek said:

På side 30 finner man "Table 3 – Enumerated values for physical units". 

F.eks 27 = W, 28 = VA, 29 = VAR, 33 = A, 35 = V

 

Takk for bekreftelsen. Da var det som jeg trodde. Makes sense

 

Ser at jeg burde vært her før og plukket opp standardene. Jaja, de dukker vel opp igjen en gang. Jeg vet jo heller ikke akkurat hva vi skal benytte denne infoen til. Aidon sender altså fullstendig selv-definerende pakker med OBIS-koder, enhet og ekpsonent. Så vi kan parse dem uten å kjenne til noen pre-definert OBIS-liste.  Men den finnes, og dermed er både enheter og skalering låst så vidt jeg kan skjønne.  Og vi må jo fremdeles forholde oss til disse listene for å støtte de andre leverandørene, selv om vi i teorien kunne droppet den for Aidon

 

EDIT: nå var jo dette godt for noe da: Det gjorde det åpenbart at jeg parset "integer" feil.   Håndteringen av fortegn for integer på 8 eller 16 bit var lbak mål.  En 8bit signed int kan jo aldri bli "255"...  Og -1 gir litt mer mening som eksponent. Trenger jo å renske litt opp i koden, men har ihvertfalll pushet en quickfix

 

EDIT2: og siden dette fremdeles kan mappes tilbake, så tok jeg like gjerne med info i "parserdata" objektet. Gjetter samtidig rått og brutalt at 30 = Wh og 32 =VArh.  Så nå ser den delen av en "liste 3" pakke fra Aidon slik ut (korrigering av type for MeterTime skjer senere, så derfor ser den litt uggen ut):

 

        {
          "obis-0":"1-0:1.7.0.255",
          "double-long-unsigned-1":674,
          "structure-2":{
            "integer-0":0,
            "enum-1":"W"
          }
        },
        {
          "obis-0":"1-0:2.7.0.255",
          "double-long-unsigned-1":0,
          "structure-2":{
            "integer-0":0,
            "enum-1":"W"
          }
        },
        {
          "obis-0":"1-0:3.7.0.255",
          "double-long-unsigned-1":0,
          "structure-2":{
            "integer-0":0,
            "enum-1":"VAr"
          }
        },
        {
          "obis-0":"1-0:4.7.0.255",
          "double-long-unsigned-1":109,
          "structure-2":{
            "integer-0":0,
            "enum-1":"VAr"
          }
        },
        {
          "obis-0":"1-0:31.7.0.255",
          "long-1":27,
          "structure-2":{
            "integer-0":-1,
            "enum-1":"A"
          }
        },
        {
          "obis-0":"1-0:71.7.0.255",
          "long-1":28,
          "structure-2":{
            "integer-0":-1,
            "enum-1":"A"
          }
        },
        {
          "obis-0":"1-0:32.7.0.255",
          "long-unsigned-1":2462,
          "structure-2":{
            "integer-0":-1,
            "enum-1":"V"
          }
        },
        {
          "obis-0":"1-0:52.7.0.255",
          "long-unsigned-1":2445,
          "structure-2":{
            "integer-0":-1,
            "enum-1":"V"
          }
        },

        {
          "obis-0":"1-0:72.7.0.255",
          "long-unsigned-1":2463,
          "structure-2":{
            "integer-0":-1,
            "enum-1":"V"
          }
        },
        {
          "obis-0":"0-0:1.0.0.255",
          "octet-string-1":"\u0007�\u0005\u001d\u0003\n\u0000\u0000�\u0000\u0000\u0000"
        },
        {
          "obis-0":"1-0:1.8.0.255",
          "double-long-unsigned-1":3867449,
          "structure-2":{
            "integer-0":1,
            "enum-1":"Wh"
          }
        },
        {
          "obis-0":"1-0:2.8.0.255",
          "double-long-unsigned-1":0,
          "structure-2":{
            "integer-0":1,
            "enum-1":"Wh"
          }
        },
        {
          "obis-0":"1-0:3.8.0.255",
          "double-long-unsigned-1":4929,
          "structure-2":{
            "integer-0":1,
            "enum-1":"VArh"
          }
        },
        {
          "obis-0":"1-0:4.8.0.255",
          "double-long-unsigned-1":139370,
          "structure-2":{
            "integer-0":1,
            "enum-1":"VArh"
          }
        }

 

Eksponent, typer og verdier stemmer overens her, men jeg registrerer at de ikke stemmer helt med det Aidon har dokumentert til NVE, der W, VAr, Wh og VArh alle har et k prefiks og tilsvarende juster eksponent. Jada, det er pirk ?

Endret av Bjørn Mork
La til info om feil-parsing av signed int
Lenke til kommentar
Del på andre sider

  • 3 uker senere...

Heisann!

 

Som deg fant jeg heller ikke noe godt nok og bestemte meg for å prøve selv. Min dekoder tar seg _KUN_ av dekoding av seriell-strømmen til JSON, men fungerer med både Aidon, Kamstrup og Kaifa-målere. I tillegg har den en opsjon for å kjøre et program for hver melding, så det er enkelt å lage en systemd unit som bruker f.eks. mosquitto_pub for å sende meldingen til et MQTT-endepunkt. Programmet er skrevet i Perl og skal fungere helt greit på en Raspberry Pi eller andre Linux-baserte operativsystemer.

 

Her er linken, hvis det skulle være av interesse: https://github.com/robinsmidsrod/ams-han-decoder

 

Personlig bruker jeg det til å sende JSON-meldingene over MQTT (bruker mosquitto) til Node-Red, som igjen kverner litt på meldingene og sender de videre til InfluxDB. Deretter bruker jeg Grafana til å visualisere informasjonen lagret i InfluxDB.

 

Har lagt ved et eksempel på List 3 (den største meldingen) fra Aidon 6525-måler, og som du ser har jeg både HDLC info (header), selve COSEM datastrukturen (payload) og en forenklet versjon av COSEM-dataene (data) som er mer egnet for å sende videre til andre applikasjoner. Feltet "payload" skal alltid være riktig, men "data" kan være feil hvis du har en Kaifa-måler som ikke sender ut registerne i riktig OBIS-rekkefølge (f.eks. forskjell på 1-fas og 3-fas). I så fall må scriptet endres litt i funksjonen "decode_cosem_structure" for å sette opp riktig rekkefølge på OBIS-kodene. Hvis du fremdeles sliter med dekoding av COSEM-registerne er Perl-koden ganske godt dokumentert på hvor opplysningene finnes. Det kan hende du finner noe du kan bruke der selv.

 

-- Robin

AIDON_V0001_list_3_example.json

Lenke til kommentar
Del på andre sider

Ser flott ut. Ville nok også valgt perl eller python om det ikke var for at jeg planla å kjøre dette på et aksespunkt der ingen av disse var installert fra før.  Det blir veldig mye bloat av et lite script hvis du må installere et helt script-rammeverk for å kjøre det ?

 

Jeg er forsåvidt med på tankegangen om å gjøre det enkelt ved å bare pipe videre, men når du piper til mosquitto_pub så må du jo etablere en ny sesjon til brokeren for hver eneste melding.  Det spiller kanskje ikke så stor rolle hvis du bare skal håndtere én måler.  Men det føles likevel som litt unødvendig mye overhead...  Har du vurdert AnyEvent::MQTT eller tilsvarende?  Bruker det for en del jobbting og det funker jo rimelig greit.

Lenke til kommentar
Del på andre sider

23 timer siden, Bjørn Mork skrev:

Ser flott ut. Ville nok også valgt perl eller python om det ikke var for at jeg planla å kjøre dette på et aksespunkt der ingen av disse var installert fra før.  Det blir veldig mye bloat av et lite script hvis du må installere et helt script-rammeverk for å kjøre det ?

 

Den skjønner jeg også. Hvis det var en del av kravet mitt hadde jeg nok prøvd å skrive det i Go, Rust eller et eller annet kompilerbart språk.

 

23 timer siden, Bjørn Mork skrev:

Jeg er forsåvidt med på tankegangen om å gjøre det enkelt ved å bare pipe videre, men når du piper til mosquitto_pub så må du jo etablere en ny sesjon til brokeren for hver eneste melding.  Det spiller kanskje ikke så stor rolle hvis du bare skal håndtere én måler.  Men det føles likevel som litt unødvendig mye overhead...  Har du vurdert AnyEvent::MQTT eller tilsvarende?  Bruker det for en del jobbting og det funker jo rimelig greit.

 

Vel, etter at jeg hadde implementert den funksjonaliteten så slo det meg at hvis man ikke ønsker å starte et program for hver ny melding kan man jo pipe hele strømmen (som er JSON-meldinger, en per linje med newline på slutten hvis man bruker flagget for kompakte JSON-meldinger) direkte til et annet program som detekterer hvert enkelt JSON-objekt og sender det av gårde når det er ferdig mottatt. Tror muligens jq eller mosquitto_pub allerede har støtte for dette...

 

Grunnen til at jeg ikke brukte en MQTT-modul fra CPAN var at jeg ønsket å holde avhengigheter minimale, for å gjøre det enkelt å installere. Derfor jeg også ikke har brukt Moo, Moose eller strukturert det objektorientert.

 

-- Robin

Lenke til kommentar
Del på andre sider

Bli med i samtalen

Du kan publisere innhold nå og registrere deg senere. Hvis du har en konto, logg inn nå for å poste med kontoen din.

Gjest
Skriv svar til emnet...

×   Du har limt inn tekst med formatering.   Lim inn uten formatering i stedet

  Du kan kun bruke opp til 75 smilefjes.

×   Lenken din har blitt bygget inn på siden automatisk.   Vis som en ordinær lenke i stedet

×   Tidligere tekst har blitt gjenopprettet.   Tøm tekstverktøy

×   Du kan ikke lime inn bilder direkte. Last opp eller legg inn bilder fra URL.

×
×
  • Opprett ny...

Viktig informasjon

Vi har plassert informasjonskapsler/cookies på din enhet for å gjøre denne siden bedre. Du kan justere dine innstillinger for informasjonskapsler, ellers vil vi anta at dette er ok for deg.