decoderen string uit P1 slimme meter

Arduino specifieke Software
Berichten: 7
Geregistreerd: 20 Okt 2016, 08:42

decoderen string uit P1 slimme meter

Berichtdoor bier » 20 Okt 2016, 09:01

ik ben bezig met het uitlezen van mijn slimme meter (ISKRA 382 me) met een Particle Photon. Dat gaat goed. Als ik de binnenkomende string via de serial monitor lees kan ik alle waarden vinden die ik nodig heb. De output ziet er zo uit:

/ISk5\2ME382-1003

0-0:96.1.1(4B414C37303035303932353935333132)
1-0:1.8.1(15651.274*kWh)
1-0:1.8.2(12177.546*kWh)
1-0:2.8.1(00266.768*kWh)
1-0:2.8.2(00757.967*kWh)
0-0:96.14.0(0001)
1-0:1.7.0(0000.51*kW)
1-0:2.7.0(0000.00*kW)
0-0:17.0.0(0999.00*kW)
0-0:96.3.10(1)
0-0:96.13.1()
0-0:96.13.0()
0-1:24.1.0(3)
0-1:96.1.0(3238303131303031323234303536343132)
0-1:24.3.0(16100733220000)(00)(60)(1)(0-1:24.2.1)(m3)
(04650.798)
0-1:24.4.0(1)
!

Met de code op de site http://12volt.kloppenburgweb.nl/p1-uitl ... t-arduino/ heb ik succesvol de meeste waardes omgezet naar floats die ik via particle.publish naar een tweede Photon stuur waar ik ze opvang en zal gaan displayen met LCD of iets anders. De code voor het lezen van de waardes uit de string ziet er zo uit:



Code: Alles selecteren
// 1-0:1.8.2 = Electricity consumption high tariff (DSMR v4.0)
      if (sscanf(buffer,"1-0:1.8.2(%ld%.%ld%*s" , &tl, &tld) >0 ) {
        tl *= 1000;
        tl += tld;
        mEVHT = tl;   
        //mEVHT = tl * 1000 + tld;
        if (mEVHT > 0) {

          //mEVHT = 0;
        }
      }


Ik ben niet zo goed nog met het werken met strings, buffers en text, maar ik denk te begrijpen wat hier gebeurt en heb het dan ook succesvol kunnen aanpassen om het juiste aantal digits te krijgen.


De enige waarde ik niet 'te pakken krijg' is de gas meter stand. In bovenstaand telegram is dat de regel:

0-1:24.3.0(16100733220000)(00)(60)(1)(0-1:24.2.1)(m3)
(04650.798)

De code op bovengenoemde site geeft mij waarde 0, ofwel, de if werkt niet:
Code: Alles selecteren
// 0-1:24.2.1 = Gas (DSMR v4.0) on Kaifa MA105 meter
      if (strncmp(buffer, "0-1:24.2.1", strlen("0-1:24.2.1")) == 0) {
        if (sscanf(strrchr(buffer, '(') + 1, "%d.%d", &tl, &tld) == 2) {
          mG = (tl*1000)+tld;

        }
      }



Ik snap dat strlen("0-1:24.2.1") de lengte geeft van deze string, dus 10. Maar wat doet strncmp(buffer, "tekst", 10) dan? Zoekt deze de eerste 10 characters op in de buffer die matchen met de tekst die ik zoek? Het getal waar het mij om gaat is 04650.798. Dat staat na (m3) en een nieuwe regel. Ik vraag me af of ik niet gewoon met sscanf die tekst kan zoeken, "(m3)\n(" maar dat werkt helaas ook niet. Dit heb ik geprobeerd:
Code: Alles selecteren

 if (sscanf(buffer,"0-1:24.2.1)(m3)'\n'(%ld.%ld" , &tl, &tld)==2 )      {   mG = (tl*1000)+tld;
}



Kan iemand me uitleggen hoe ik de gezochte waarde uit het telegram kan halen? En mocht je vragen hebben over het andere stuk van de P1 meter (aansluiten, verzenden particle publish, photon, etc.): laat maar weten. stapje bij beetje kom ik dichterbij het doel...

Alvast bedankt
Gr

Advertisement

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: decoderen string uit P1 slimme meter

Berichtdoor Koepel » 20 Okt 2016, 13:17

De Iskra ME382 : http://iskrame382.com/
Die heeft een 'customer port'. Handig.

Particle Photon : https://store.particle.io/#photon. Het bevat een Cortex M3. De Arduino Due heeft ook een Cortex M3, maar een andere chip. Het is dus nauwelijks Arduino compatible, en je bent erg afhankelijk van de Arduino-compatible core die ze erbij leveren. Maar goed, je hebt al een deel werkend, dus dat geeft hoop.

:arrow: Vraag voor anderen : Is dit een serieuze fout :?:
Code: Alles selecteren
sscanf(buffer,"1-0:1.8.2(%ld%.%ld%*s" , &tl, &tld)

De sscanf gaat de %*s lezen en naar een string sturen die echter niet als parameter op de stack staat en de tweede '%' doet toch helemaal niets ?

De strncmp() gaat niet zoeken. Hij vergelijkt waar de pointers op dat moment naar toe wijzen (met hier een maximum aantal van 10).
Ik weet niet hoe het einde van de regel is. Is dat '\n' or '\r\n' of '\r' ?
Dus de sscanf() doet het meeste werk.

Kun je eerst proberen om droog te zwemmen. Dus alleen met de computer en een Arduino met de tekst gewoon in de Arduino. Dan heb je een test sketch nodig om alleen die tekst te ontrafelen. Kun je dat voor ons maken ?
Ik ben niet zo thuis in het inlezen van een bepaalde stream data. Daar zijn standaard manieren voor.

Weet je zeker dat na de "(m3)" een nieuwe regel komt ? Dus het getal dat erna komt, dat heeft geen speciale code aan het begin zoals de rest.
Er wordt regel voor regel gelezen en verwerkt, dus je kunt niet zoeken op "\n" in een regel.

Berichten: 7
Geregistreerd: 20 Okt 2016, 08:42

Re: decoderen string uit P1 slimme meter

Berichtdoor bier » 20 Okt 2016, 13:49

De Photon lijkt aardig Arduino compatible, maar goed dat je opmerkt dat er verschillen zijn. Was ik nog niet tegengekomen behalve de 2e serial. Het werkt al best goed, dus het zal inderdaad moeten lukken.

Ik heb de sscanf regel stap voor stap gemodificeerd van de voor mij werkende sscanf regels voor de andere parameters uit het telegraaf. maar helaas werkt dat niet. Ik heb ook moeite om die if statements te begrijpen. De uitleg die ik her en der vindt over sscanf is vaak ook voor mij wat cryptisch omdat ik gewoon generieke programmeer kennis mis... Als jij praat over een string die niet als parameter op de stack staat, dan begrijp ik dat dus ook niet helaas.

Het eind van de regel is mij ook niet bekend, maar volgens mij \n omdat dat helemaal aan het begin in de code gebruikt wordt bij het vullen van de buffer:
Code: Alles selecteren
 if (input == '\n') { // We received a new line (data up to \n)


Dat van die test sketch is inderdaad een goed idee, moet ik gaan doen. Ben nu niet thuis dus heb arduino niet bij de hand, maar kan er vanavond hopelijk mee testen.

Het inlezen van de stream werkt volgens mij zo dat ik zoek op bepaalde tekst. De volgorde waarin ik dat doe maakt niet uit. Ik heb getest dat ik eenzelfde regel die al eens gelezen is nogmaals kan uitlezen. Dus volgens mij doorzoekt de sscanf het hele telegram. Als ik regel voor regel lees is het lastig om de gezochte waarde te vinden omdat 'ie net als heel veel andere regels met ( begint. Daarom wilde ik bij (m3) gaan staan, en dan 1 character/harde return opschuiven.

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: decoderen string uit P1 slimme meter

Berichtdoor Koepel » 20 Okt 2016, 14:11

Ik wacht nog even op anderen, wat die zeggen over die string die niet als parameter op de stack staat. Ik denk zo langzamerhand er 100% zeker van te zijn dat het een serieuze fout is waarbij geheugen wordt overschreven.

Die sketch van "kloppenburgweb.nl", die leest regel voor regel. Per regel wordt gekeken wat er staat. En de sscanf kijkt dus vanaf het eerste karakter van een regel.

Als jij modificaties hebt gemaakt, dan zie ik graag de hele sketch.

Berichten: 7
Geregistreerd: 20 Okt 2016, 08:42

Re: decoderen string uit P1 slimme meter

Berichtdoor bier » 20 Okt 2016, 18:16

De hele sketch, dat kan natuurlijk! Er moet nog wel wat opgeruimd worden natuurlijk, maar dat komt nog wel. En ik zet er graag comments bij omdat ik vaak enige tijd weer niet eraan werk en dan moet ik alles steeds opnieuw uitvinden weer.

cpp code
char input; // incoming serial data (byte)
//bool readnextLine = false;
#define BUFSIZE 75


char Controller[ ] = "P1 output";
float mEVHT=34551.23; // zitten allemaal 'testwaardes' in om de string te versturen voordat uitlezen gelukt was. wordt later overschreven.
float mEVLT=1555.123;
int mEAV=340;
int mEPV=560;
float mEPVHT=122345.678;
float mEPVLT=145872.980;
int T7=0; //nog niet in gebruik
float Energy=5; //niet in gebruik
float mG = 123.45;
char str[255];
char buffer[BUFSIZE]; //Buffer for serial data to find \n .
int bufpos = 0;
int tarief = 3; //Meter actual rate
char c;
long teller = 0;

void setup() {

Serial.begin(9600);
delay(1000);
Serial1.begin(9600);
delay(2000);
}


void loop() {

long tl = 0;
long tld = 0;


if (Serial1.available()) {

input = Serial1.read();


char inChar = (char)input;
// Fill buffer up to and including a new line (\n)

buffer[bufpos] = input&127;
bufpos++;


if (input == '\n') { // We received a new line (data up to \n)

if (sscanf(buffer,"0-0:96.14.0(%ld.%ld*s" , &tl) >0 ) //)
{
tarief = tl;
}

if (sscanf(buffer,"1-0:1.8.1(%ld.%ld" ,&tl, &tld)==2){
tl *= 1000;
tl += tld;
mEVLT = tl/1000.0;

if (mEVLT > 0) {
}
}

// 1-0:1.8.2 = Electricity consumption high tariff (DSMR v4.0)
if (sscanf(buffer,"1-0:1.8.2(%ld.%ld*s" , &tl, &tld) ==2 ) {//% eraf, 2x en ==2 ipv >0
tl *= 1000;
tl += tld;
mEVHT = tl/1000.0;

if (mEVHT > 0) {
}
}

/////// terug geleverd totaal
// 1-0:2.8.1 = Electricity levering low tariff (DSMR v4.0)
if (sscanf(buffer,"1-0:2.8.1(%ld.%ld*s" , &tl, &tld)==2 ) {
tl *= 1000;
tl += tld;
mEPVLT = tl/1000.0;


if (mEPVLT > 0) {
}
}



// 1-0:2.8.2 = Electricity levering high tariff (DSMR v4.0)
if (sscanf(buffer,"1-0:2.8.2(%ld.%ld*s" , &tl, &tld)==2 ) {
tl *= 1000;
tl += tld;
mEPVHT = tl/1000.0;

if (mEPVHT > 0) {

//mEVHT = 0;
}
}


///////// einde teruggeleverd totaal


// 1-0:1.7.0 = Electricity consumption actual usage (DSMR v4.0)
if (sscanf(buffer,"1-0:1.7.0(%ld.%ld%*s" , &tl , &tld) ==2 ) { // %ld is a long double variable


mEAV = tl * 1000 + tld * 10;
//mEAV = tld;
if (mEAV > 0) { //wat doet deze if??

}

}

////////////
// 1-0:2.7.0 = Electricity teruglevering actueel (DSMR v4.0)
if (sscanf(buffer,"1-0:2.7.0(%ld.%ld%*s" ,&tl , &tld) ==2 )//
{
// mEPV = tld;

mEPV = tl*1000 + tld*10; //rekenen met float, dan altijd punt in getal anders berekening fout

if (mEPV > 0) {

}
}

//////////////////// vanaf hier begint de rommel voor de gasmeterstand /////////////////

// 0-1:24.2.1 = Gas (DSMR v4.0) on Kaifa MA105 meter
/*
********telegram line below as received through serial. trying to get 04650.798 out of this*********

0-1:24.3.0(16100733220000)(00)(60)(1)(0-1:24.2.1)(m3)
(04650.798)


*/

// 0-1:24.2.1 = Gas (DSMR v4.0) on Kaifa MA105 meter --- CODE found online for similar meter. their telegram matches mine
// if (strncmp(buffer, "0-1:24.2.1", strlen("0-1:24.2.1")) == 0) { //strlen(#) geeft lengte terug = 10
// dus if (strncmp(input buffer, checken op deze tekst, 10) == 0 --> is die 0 false? dit kan ook met sscan denk ik. zie boven.
int var;
if (sscanf(buffer,"0-1:24.2.1)(m3)'\n'(%ld.%ld" , &tl, &tld)==2 ) //// // opzoeken van de tekst "0-1:24 " in de buffer geeft een true als die erin staat. (denk ik)
{ mG = tld;

}



/* //deze code is oud en werkt niet goed.
if (strncmp(buffer, "0-1:24.2.1", strlen("0-1:24.2.1")) == 0) {
if (sscanf(strrchr(buffer, '(m3)') + 1, "%d.%d", &tl, &tld) == 2) { //waarom stat hier een dubbele if. zou misschien met 1 moeten kunnen, m3 staat er maar 1x in. daarvoor zit een return
mG = (tl*1000.0)+tld;

}
}
*/


////////////////// tot hier ////////////////

// Empty buffer again (whole array)
for (int i=0; i<75; i++)
{
buffer[i] = 0;
}
bufpos = 0;

}

if (input == '!') { //! is end of telegram, data will be sent/printed to serial


//park values before resetting them: niet meer nodig met versturen.

printData();

mEVHT=0;
mEVLT = 0;
mEPVHT=0;
mEPVLT = 0;
mEAV = 0;
mEPV = 0;
//mG = 0;
tarief=3;

} //Einde ! detection

} //end if serial1.available
}



void printData(){
sprintf(str, "%s,%.3f,%.3f,%d,%d,%.3f,%.3f,%.2f,%.1f", Controller, mEVHT, mEVLT, mEAV, mEPV, mEPVHT, mEPVLT, mG, Energy);
Particle.publish("mP1", str,100,PRIVATE);


}


Ik ben benieuwd of je hiermee uit de voeten kunt.
gr

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: decoderen string uit P1 slimme meter

Berichtdoor Koepel » 20 Okt 2016, 19:55

Dat gaat toch echt met één regel per keer.

De structuur van de sketch is niet goed zichtbaar. Zou je op Ctrl+T willen drukken ? Of in het menu "Hulpmiddelen / Automatische opmaak" selecteren. Dan kun je zien dat een ontvangen regel door alle if-statements gaat om te checken of die regel ergens past.

Je zou kunnen testen of het eerste teken van een regel een '(' is. Dat is een beetje flauw.
Beter is om te testen op "0-1:24.3.0" en dan een vlag zetten dat de volgende regel de juiste is. En de vlag uitzetten bij alle andere regels.

Berichten: 7
Geregistreerd: 20 Okt 2016, 08:42

Re: decoderen string uit P1 slimme meter

Berichtdoor bier » 20 Okt 2016, 22:00

ik geloof dat die opmaak nu klopt, het ziet er leesbaar uit. ik had de code knop gebruikt in het venster waar je typt, maar blijkbaar is dat niet de juiste.

On topic: ik begrijp niet wat je bedoelt met een vlag zetten, en hoe ik die regel dan moet pakken. Jij lijkt beter te begrijpen wat ik nodig heb dan ik. zou je me een stukje voorbeeldcode kunnen geven?

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: decoderen string uit P1 slimme meter

Berichtdoor Koepel » 21 Okt 2016, 01:58

Als ik zonder na te denken iets maak voor een 8-bit Arduino, dan ziet het er zo uit zoals hieronder.
Kun je het proberen op een Arduino Uno ? Ik weet niet of jij de PROGMEM en de strncmp_P() hebt.
Code: Alles selecteren
// Arduino.cc 1.6.12, Arduino Uno

// All this data is concatenated to a single string.
// The maximum length for a single line is about 65.
const char data[] =
{
  "/ISk5\2ME382-1003\n"
  "\n"
  "0-0:96.1.1(4B414C37303035303932353935333132)\n"
  "1-0:1.8.1(15651.274*kWh)\n"
  "1-0:1.8.2(12177.546*kWh)\n"
  "1-0:2.8.1(00266.768*kWh)\n"
  "1-0:2.8.2(00757.967*kWh)\n"
  "0-0:96.14.0(0001)\n"
  "1-0:1.7.0(0000.51*kW)\n"
  "1-0:2.7.0(0000.00*kW)\n"
  "0-0:17.0.0(0999.00*kW)\n"
  "0-0:96.3.10(1)\n"
  "0-0:96.13.1()\n"
  "0-0:96.13.0()\n"
  "0-1:24.1.0(3)\n"
  "0-1:96.1.0(3238303131303031323234303536343132)\n"
  "0-1:24.3.0(16100733220000)(00)(60)(1)(0-1:24.2.1)(m3)\n"
  "(04650.798)\n"
  "0-1:24.4.0(1)\n"
  "!\n"
};

const char T1Plus[] PROGMEM = "1-0:1.8.1";
const char T2Plus[] PROGMEM = "1-0:1.8.2";
const char T1Min[] PROGMEM = "1-0:2.8.1";
const char T2Min[] PROGMEM = "1-0:2.8.2";
const char gasDate[] PROGMEM = "0-1:24.3.0";
const char tarief[] PROGMEM = "0-0:96.14.0";

char buffer[75];
int bufpos;
char *pIn;

boolean previouslineM3 = false;

void setup()
{
  Serial.begin( 9600);
  Serial.println(F( "\nStarted"));

  pIn = data;
}

void loop()
{
  long t1, t2, t3;
  static char *p;                // why 'static' ? without it a compiler bug ?

  char c = *pIn++;                    // needed to simulate serial input

  buffer[bufpos] = c;
  bufpos++;

  // Add extra safety.
  // The current line will be broken, but buffer overflow is avoided.
  if( bufpos >= sizeof( buffer))
  {
    bufpos = 0;
  }
 
  if( c == '\n')
  {
    // A new line is in the buffer.
    // Be sure to terminate it.
    buffer[bufpos] = '\0';
    p = buffer;                // set pointer to begin of line.

    if( strncmp_P( buffer, T1Plus, strlen_P( T1Plus)) == 0)
    {
      // Found the +T1 data
      p += strlen_P( T1Plus);

      sscanf( p, "(%ld.%ld", &t1, &t2);
      float f_t1plus = (float) t1 + (float) t2 / 1000.0;
      Serial.print(F( "+T1="));
      Serial.println( f_t1plus, 4);
     
      previouslineM3 = false;
    }
    else if( strncmp_P( buffer, T2Plus, strlen_P( T2Plus)) == 0)
    {
      // Found the +T2 data
      p += strlen_P( T2Plus);

      sscanf( p, "(%ld.%ld", &t1, &t2);
      float f_t2plus = (float) t1 + (float) t2 / 1000.0;
      Serial.print(F( "+T2="));
      Serial.println( f_t2plus, 4);
     
      previouslineM3 = false;
    }
    else if( strncmp_P( buffer, T1Min, strlen_P( T1Min)) == 0)
    {
      // Found the -T1 data
      previouslineM3 = false;
    }
    else if( strncmp_P( buffer, T2Min, strlen_P( T2Min)) == 0)
    {
      // Found the -T2 data
      previouslineM3 = false;
    }
    else if( strncmp_P( buffer, tarief, strlen_P( tarief)) == 0)
    {
      // Found the tarief data
      p += strlen_P( tarief);

      sscanf( p, "(%ld", &t1);
      float f_tarief = (float) t1;
      Serial.print(F( "tarief="));
      Serial.println( f_tarief, 4);
     
      previouslineM3 = false;
    }
    else if( strncmp_P( buffer, gasDate, strlen_P( gasDate)) == 0)
    {
      // Found the data of the gas data
      previouslineM3 = true;
    }
    else if( buffer[0] == '(')
    {
      // We might have found the gas meter data
      if( previouslineM3)
      {
        // We sure have found the gas meter data
        p += 1;               // skip the '('

        sscanf( p, "%ld.%ld", &t1, &t2);
        float f_gasmeter = (float) t1 + (float) t2 / 1000.0;
        Serial.print(F( "gasmeter="));
        Serial.println( f_gasmeter, 4);
      }
      previouslineM3 = false;
    }
    else if( buffer[0] == '!')
    {
      Serial.println();
      delay( 10000);                   // needed to simulate serial input
      previouslineM3 = false;
    }
    else
    {
      // The read line of text has no meaning to us.
      previouslineM3 = false;
    }

    // reset the bufpos to the begin of the buffer.
    bufpos = 0;
    buffer[0] = '\0';      // empty the buffer
  }

  if( pIn >= data + sizeof( data))    // needed to simulate serial input
    pIn = data;                       // needed to simulate serial input
}


De "boolean previouslineM3" is de vlag.
Als er over nadenk, dan denk ik dat de code beter kan. Ook zie ik steeds meer fouten in de code die jij gebruikt.

Volgens mij zit er een bug in de meter. De code voor de gas-waarde is "0-1:24.2.1" en die had misschien gewoon op een nieuwe regel moeten staan.
Op deze manier zou het veel logischer zijn: "0-1:24.2.1(04650.798*m3)"

Kun je dit allemaal vergeten en een library zoeken die al werkt. Je gebruikt de code van het voorbeeld, maar daar zitten behoorlijk veel fouten in. Ik zit ook nog te wachten op een reactie van iemand over die serieuze fout.

Berichten: 7
Geregistreerd: 20 Okt 2016, 08:42

Re: decoderen string uit P1 slimme meter

Berichtdoor bier » 21 Okt 2016, 06:40

Bedankt voor je uitgebreide reactie, hier moet ik even op studeren. Copy paste naar de Photon werkt niet, maar ik ga hier zeker wat aan hebben. ik meld me als ik verder ben.

Een bug in de meter: zou kunnen, maar dan zit het ook in andere meters van een ander merk. De output van de Kaifa meter bijvoorbeeld is gelijk. Mogelijk dus toch gewoon de DSMR standaard.
En een library die werkt: ik zou er best blij mee zijn als die er was, maar wat ik tot nu toe tegen ben gekomen hierover lijkt allemaal beetje van elkaar geleend/gekopieerd. Behalve de Raspberry Pi projecten misschien, maar daar ben ik nog lang niet aan toe!

Ik heb overigens een Uno, dus ophalen op de Uno en doorpassen naar de Photon zou ook nog kunnen wellicht.

Gebruikers-avatar
Berichten: 2655
Geregistreerd: 06 Aug 2016, 01:03

Re: decoderen string uit P1 slimme meter

Berichtdoor Koepel » 21 Okt 2016, 09:54

De code is wat ik zomaar zonder na te denken opschreef. Het kan natuurlijk aangepast worden voor de Photon, dat is geen probleem. Het ging mij nu even om de opbouw van de code.

Nadat ik een regel bij elkaar heb ontvangen, is er een lange lijst met deze dingen:
Code: Alles selecteren
else if( strncmp_P( buffer, T1Min, strlen_P( T1Min)) == 0)

Dat leek mij wat duidelijker om dingen toe te kunnen voegen. De ..._P dingen kun je gewoon overheen lezen.

De strncmp_P() kijkt naar het begin van regel. Pas als dat klopt, dan ga ik met sscanf() de getallen uit de regel vissen.

Kijk bij een Arduino Uno ook eens naar de getallen. De laatste twee cijfers zijn anders, vanwege het 32-bit float getal. Is dat erg ? Ik vind het niet leuk, ik heb liever de harde echte getallen. Meteen al bij het inlezen van de getallen de nauwkeurigheid kwijt raken, dan hoort niet. Zo ga ik niet met data om.

Volgende

Terug naar Arduino software

Wie is er online?

Gebruikers in dit forum: Geen geregistreerde gebruikers en 93 gasten