Dezimalzahl aus Char-Datensatz auslesen

Hallo an alle Forummitglieder

Ich bin ein Neuling mit Arduino.

Ich möchte den Eigenverbrauch meiner Photovoltaikanlage erhöhren und lese meinen Semax Smartmeter mit DMSR v5.0.2- Protokll über ein RJ12 Kabel und Arduino Uno aus. Mein Ziel ist es, über Relais (an Pin 2,3,4,5), Verbraucher anzusteuern.

Nach langem probieren mit der Hardware (nach Anleitung http://domoticx.com/p1-poort-slimme-meter-uitlezen-hardware/) habe ich mit folgendem Sketch den entsprechenen Datensatz ausgelesen, welcher sich alle 10Sekundne erneuert.


Vorformatierter Text

#include <AltSoftSerial.h>

#include <AltSoftSerial.h>
// AltSoftSerial always uses these pins:
//
// Board          Transmit  Receive   PWM Unusable
// -----          --------  -------   ------------
// Teensy 2.0         9        10       (none)
// Teensy++ 2.0      25         4       26, 27
// Arduino Uno        9         8         10
// Arduino Mega      46        48       44, 45
// Wiring-S           5         6          4
// Sanguino          13        14         12
 
AltSoftSerial altSerial;
char c;

    
void setup() {
    Serial.begin(115200);
    altSerial.begin(115200);
}
 
void loop() {

    
    if (altSerial.available()) {
        c = altSerial.read(); 
       
        // --- 7 bits instelling ---
        c &= ~(1 << 7);
        char inChar = (char)c;

    Serial.print(c);

}
}

Char-Datensatz:

1-3:0.2.8(50)
0-0:1.0.0(211211164814W)
0-0:96.1.1(3230333830333738)
1-0:1.8.1(003107.883kWh)
1-0:1.8.2(001105.053
kWh)
1-0:2.8.1(007040.886kWh)
1-0:2.8.2(000052.335
kWh)
1-0:32.7.0(236.5V)
1-0:52.7.0(233.2
V)
1-0:72.7.0(234.5V)
1-0:31.7.0(001
A)
1-0:51.7.0(009A)
1-0:71.7.0(004
A)
1-0:2.7.0(00.000*kW)
1-0:1.7.0()
!4781

Für mich ist der Wert 1-0:2.7.0(00.000*kW) wichtig.

Wie kann ich diesen Wert mit einer if-Bedingung im Sketch verwenden?

Hab schon etliche Threads hier durchgeschaut, finde aber nichts, was ich gut verstehe und mir weiterhilft…

Ich hoffe ihr könnt mir weiterhelfen.
Grüße

Lesen Sie eine Zeile in einen buffer und analysieren Sie sie, um zu sehen, ob sie Ihrem Interesse entspricht.

➜ dieses Tutorial könnte helfen Serial Input Basics

1 Like

Da könnten die beiden Funktionen strchr und strtof helfen - wenn schon bekannt ist, dass es sich um die 1-0:2.7-Zeile handelt.

char* input = "1-0:2.7.0(00.000*kW)";
char* zeigerKlammerAuf = strchr(input, '(');
if (zeigerKlammerAuf != NULL)
{
  zeigerKlammerAuf++; // zeigt jetzt auf die 0 hinter der Klammer
  float value = strtof(zeigerKlammerAuf, NULL); // stoppt automatisch beim *
  Serial.print("value = ");
  Serial.println(value, 2);
}
else
{
  Serial.println("Das war wohl nix.");
}

Ich habe das weder kompiliert noch getestet, aber als Anregung sollte es genügen.

Warnung:
Ein float value in einer Bedingung zu verwenden, kann wegen unvermeidbarer Einschränkung der Genauigkeit des Datentyps zu Problemen führen.
Prüfung auf Gleichheit/Ungleichheit würde ich als verboten betrachten. Kleiner/größer ist möglich, wenn Schwellwerte nicht exakt getroffen werden müssen.

@ stiago2

mit strtod klappt es auch am Arduino

/*
   Semax Smartmeter with DMSR
   https://forum.arduino.cc/t/dezimalzahl-aus-char-datensatz-auslesen/934748
   Started with Example 4 https://forum.arduino.cc/t/serial-input-basics-updated/382007/3

   message examples
   1-0:71.7.0(004A)
   1-0:2.7.0(00.000*kW)
   1-0:1.7.0()

   by noiasca - with a little help from wmo158
*/

const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;

int dataNumber = 0;             // new for this version

void setup() {
  Serial.begin(115200);
}

void loop() {
  recvWithEndMarker();
  showNewNumber();
}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

  if (Serial.available() > 0) {
    rc = Serial.read();

    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
    }
  }
}

void showNewNumber() {
  if (newData == true) {
    // output all
    Serial.print(F("This just in ... "));
    Serial.println(receivedChars);
    // "filter"
    if (!strncmp(receivedChars, "1-0:2.7.0(", 10))
    {
      Serial.println(F("fits"));
      char* zeigerKlammerAuf = strchr(receivedChars, '(');
      float value = strtod(zeigerKlammerAuf + 1, NULL); // stoppt automatisch beim *
      Serial.print(F("value = "));
      Serial.println(value, 2);
    }
    newData = false;
  }
}

wenn du uns ein vernünftiges Datenblatt verlinkst woraus man das Protokoll ablesen könnte, könnte man da sicher mehr auslesen...

1 Like

Diese Geschwindigkeit ist sportlich für eine Softwarelösung, sobald Dein Programm länger wird. Besser wäre ein µC mit einer zweiten seriellen Schnittstelle in Hardware wie der Mega2560.

Unter hemmungsloser Verwendung der vorhergehenden Beiträge habe ich mal einen Vorschlag zusammengestellt. Er läßt sich problemlos um weitere Werte erweitern, weshalb ich eine Referenz verwendet habe. Das erlaubt viele Rückgabewerte.

#include "SoftwareSerial.h"
#define RX_PIN 8
#define TX_PIN 9
SoftwareSerial altSerial(RX_PIN, TX_PIN);

void recvWithEndMarker(float &leistung) {
  const byte maxChars = 32;
  static char receivedChars[maxChars] = {'\0'};
  static byte ndx = 0;
  const char endMarker = '\n';
  char rc;

  if (altSerial.available() > 0) {
    rc = altSerial.read();

    if (rc != endMarker) {
      if (ndx < maxChars - 1) {
        receivedChars[ndx] = rc;
        ndx++;
        receivedChars[ndx] = '\0';
      }
    } else {
      //Serial.println(receivedChars);
      if (!strncmp(receivedChars, "1-0:2.7.0(", 10))
      {
        char* zeigerKlammerAuf = strchr(receivedChars, '(');
        leistung = strtod(zeigerKlammerAuf + 1, NULL); // stoppt automatisch beim *
      }
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
    }
  }
}

void loop() {
  float leistung = -1;
  recvWithEndMarker(leistung);
  if (leistung > 0) {
    Serial.print(F("Leistung = "));
    Serial.println(leistung, 3);
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("\nStart");
  //altSerial.begin(115200);
  altSerial.begin(9600);
}

Ich hoffe, mein Beitrag hilft Dir :slightly_smiling_face:

1 Like

Hallo Agmue

Vielen Dank für deine Hilfe. Ich habe deinen Sketch auf altserial angepasst und bekomme diesen Datensatz.

include <AltSoftSerial.h>

#define RX_PIN 8
#define TX_PIN 9

AltSoftSerial altSerial;
char c; 


void setup() {
  Serial.begin(115200);
  Serial.println("\nStart");
  //altSerial.begin(115200);
  altSerial.begin(115200);
  pinMode(3, OUTPUT);
}

void recvWithEndMarker(float &leistung) {
  const byte maxChars = 32;
  static char receivedChars[maxChars] = {'\0'};
  static byte ndx = 0;
  const char endMarker = '\n';
  char rc;

  if (altSerial.available()) {
    c = altSerial.read();

    c &= ~(1 << 7);
        char inChar = (char)c;
        
            Serial.print(c);

    if (c != endMarker) {
      if (ndx < maxChars - 1) {
        receivedChars[ndx] = c;
        ndx++;
        receivedChars[ndx] = '\0';
      }
    } else {
      //Serial.println(receivedChars);
      if (!strncmp(receivedChars, "1-0:2.7.0(", 10))
      {
        char* zeigerKlammerAuf = strchr(receivedChars, '(');
        leistung = strtod(zeigerKlammerAuf + 1, NULL); // stoppt automatisch beim *
      }
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
    }
  }
}

void loop() {
  float leistung = -1;
  recvWithEndMarker(leistung);
  if (leistung >= 0) {
    Serial.print(F("Leistung = "));
    Serial.println(leistung, 3);
  }
  
  if (leistung = 0.000) {
    
    digitalWrite(3, HIGH);
  } else {
    // turn LED off:
    digitalWrite(3, LOW);

}
}

0-0:1.0.0(211211212404W)
0-0:96.1.1(3230333830333738)
1-0:1.8.1(003112.307kWh)
1-0:1.8.2(001106.447
kWh)
0-0:2.8.1(007040.886kWh)
1-0:2.8.2(000052.335
kWh)
1-0:32.7.0(233.2"V)
1-0:52.7.0(235.4V)
1-0:72.7.0(235.8
V)
1-0:31.7.0(007A)
1-0:51.7.0(000
A)
1-0:71.7.0(000A)
1-0:2.7.0(00.000
kW)
Leistung = 0.000
1-0:1.7.0()
!322D

ich habe mal den Schaltwert des Relais auf 0.000 gesetzt. Dies funktioniert leider noch nicht. Hättest du mir da einen Tip wie ich die Leistung in eine If-Bedingung kriege. Dank eurer Hilfe bin ich nah dran :pray:

bitte noch mal lesen:

if (leistung <= 0.1) {
1 Like

Und das ist außerdem eine Zuweisung, kein Vergleich.
Ich spendiere ein =.

1 Like

:slight_smile:

Ich habe auch viele kostenlose, wenn das helfen kann :innocent:

=====================================

Hab ich doch gewusst, das das * aus #1 da nicht reingehört.
Und bedenke: Das Format ist 3,3!
Da können also auch 3 Vorkommastellen drin sein.

Ich hatte vorhin meinen Sketch weg geschmissen - hätte ich mal nicht machen sollen ;(

Na mal sehen, ob ich das nochmal gebaut bekomme.

  if (leistung > 0.000) {
    digitalWrite(3, LOW);
  } else {
    // turn LED off:
    digitalWrite(3, HIGH);
  }
1 Like

Danke fürs darauf hinweisen. Nach der Korrektur funktioniert es tadellos. Ich brauche zum Glück nur Kleiner/grösser. :wink:

Vielen Dank für eure schnelle Hilfe. Ohne Euch hätte ich das bestimmt nicht geschafft!

Bitte gerne :slightly_smiling_face:

Wenn Du wolltest, könntest Du auch mehrere Werte auswerten oder anzeigen, dafür habe ich die Referenz eingebaut.

@stiago2

wie schon im Beitrag #5 ersucht:

Wenn du ein Datasheet verlinkst oder zumindst sehr genau beschreiben kannst was die einzelnen Werte sind, dann könnte man da einen generellen Sketch schreiben und die Nachwelt hätte auch etwas davon.

-0:1.0.0(211211212404W)
0-0:96.1.1(3230333830333738)
1-0:1.8.1(003112.307<em>kWh)
1-0:1.8.2(001106.447</em>kWh)
0-0:2.8.1(007040.886<em>kWh)
1-0:2.8.2(000052.335</em>kWh)
1-0:32.7.0(233.2"V)
1-0:52.7.0(235.4<em>V)
1-0:72.7.0(235.8</em>V)
1-0:31.7.0(007<em>A)
1-0:51.7.0(000</em>A)
1-0:71.7.0(000<em>A)
1-0:2.7.0(00.000</em>kW)
1-0:1.7.0()
!322D

man muss ja nicht gleich den CRC nachrechnen.
... aber eine Strutkur wo alle daten sauber drinnen sind, wäre schon fein. Wir können zwar einiges erarten, aber richtige Bezeichner wären schön.

auch ein Link auf das Produkt oder eine GANZ GENAUE Typenbezeichnung wäre nett.

Das Protokoll ist umfangreicher(https://www.bundesnetzagentur.de/DE/Beschlusskammern/BK06/BK6_81_GPKE_GeLi/Mitteilung_Nr_20/Anlagen/Obis-Kennzahlen-System_2.0.pdf?__blob=publicationFile&v=2) , aber da steht drin, was Du suchst.

1 Like

Ich habe mich leider zu früh gefreut.
Heute bei Überproduktion wurde zwar die Leistung sauber im seriellen Monitor angezeigt, aber hat für die If-Bedingungen keine logische Werte geliefert. Das Relais hat erst bei folgender Bedingung reagiert, aber nur statisch und nicht auf Veränderung der "Leistung".

Vorformatierter Text

if (leistung < -2.000) {
    digitalWrite(3, LOW);
  } else {
    // turn LED off:
    digitalWrite(3, HIGH);
  }

Hier mehr zum Protokoll und dem Gerät:

Das Gerät ist ein Komunikationsmodul Semax Am103. Baugleich wie Elster Am103 (AM103 / AM110 - Semax AG).

Damit ich das Protokoll abrufen konnte, musste ich zusätzlich zum Inverter mit BS170( nach Anleitung [http://domoticx.com/p1-poort-slimme-meter-uitlezen-hardware/ ]) einen 1kOhm Wiederstand zwischen +5V und Data line setzen.

da wäre interessant
-ein vollständiger Sketch
-das konkrete Kriterium( kleiner -2.0) ????
-und entsprechende Mustermessages die etwas bewirken oder fehlerhaft nicht bewirken.

den lokal bei mir simuliert schaffe ich eine Abfrage auf < negativ

`#include <AltSoftSerial.h>

#define RX_PIN 8
#define TX_PIN 9

AltSoftSerial altSerial;
char c; 

void setup() {
  Serial.begin(115200);
  Serial.println("\nStart");
  //altSerial.begin(115200);
  altSerial.begin(115200);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
}

void recvWithEndMarker(float &leistung) {
  const byte maxChars = 32;
  static char receivedChars[maxChars] = {'\0'};
  static byte ndx = 0;
  const char endMarker = '\n';
  char rc;

  if (altSerial.available()) {
    c = altSerial.read();

    c &= ~(1 << 10);
        char inChar = (char)c;
        
            Serial.print(c);

    if (c != endMarker) {
      if (ndx < maxChars - 1) {
        receivedChars[ndx] = c;
        ndx++;
        receivedChars[ndx] = '\0';
      }
    } else {
      //Serial.println(receivedChars);
      if (!strncmp(receivedChars, "1-0:2.7.0(", 10))
      {
        char* zeigerKlammerAuf = strchr(receivedChars, '(');
        leistung = strtod(zeigerKlammerAuf + 1, NULL); // stoppt automatisch beim *
      }
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
    }
  }
}

void loop() {
  float leistung = -1;
  recvWithEndMarker(leistung);
  if (leistung >= 0) {
    Serial.print(F("Leistung = "));
    Serial.println(leistung, 3);
  }
  
  if (leistung > 0.050) {
    digitalWrite(3, LOW);
  } else {
    // turn LED off:
    digitalWrite(3, HIGH);
  }
}`

Relais bleibt angezogen bei Leistung 0.209 und 0.000