Arduino - Easymeter Q3DA

Hallo zusammen,

ich habe einen Easymeter Q3DA welcher mir per D0 Schnittstelle alle zwei Sekunden folgende Werte ausgibt :
Parameter: OBIS-Protokoll-Zuordnung
Eigentumsnummer -->1-0:0.0.0255 max. 20 Zeichen Nummer des Stromversorger
Zählerstand --> 1-0:1.8.0
255 (Bezugsregister)
1-0:2.8.0255 (Lieferregister)
1-0:15.8.0
255 (always posi-tive)
Momentanleistung --> L1 1-0:21.7.0255
Momentanleistung --> L2 1-0:41.7.0
255
Momentanleistung --> L3 1-0:61.7.0255
Momentanleistung --> L1+L2+L3 1-0:1.7.0
255
Statusinformation --> 1-0:96.5.5*255

Ich möchte gerne per Arduino und IR-Schreib-Lesekopf die übertragenen Daten des Zählers auswerden und in einem späteren Schritt per mqtt verschicken.

Aktuell verwende ich folgenden Code der mir auch schon einiges auf der Console ausgibt...

#define irRxPin 10
#define irTxPin 11
#include <SoftwareSerial.h>
SoftwareSerial ir = SoftwareSerial(irRxPin,irTxPin); // RX, TX

void setup()
{
  Serial.begin(57600);
  ir.begin(9600);
  ir.listen();
}

void loop() {
 
  if (ir.available())
  {
    int inByte = ir.read();
    inByte &= 0x7F;
    if (inByte < 0x10)Serial.write('0');
    Serial.print(inByte,HEX);
    Serial.write (' ');
  }
}
}

Die Ausgabe serielle sieht in etwa wie folgt aus:
1B 1B 1B 1B 01 01 01 01 76 05 ..... was dann wohl der Anfang ist und dann folgt der Rest.
Insgesamt sind des ca. 400 Zeichen pro "Nachricht"

Den eigentlichen Teil den ich suche 77 07 01 00 01 08 00 FF ( 1-0:1.8.0*255 (Bezugsregister))
oder auch die anderen Werte kann ich allerdings in der Seriellen Übertragung nicht ausfindig machen?
Zumal ich ich meiner Ausgabe keinen HEX-Wert FF habe :-\

Habe ich vielleicht noch ein Problem mit der Codierung ?

Vielleicht könnt ihr mir einen kleinen Denkanstoß geben ?

Vielen Dank im Voraus

Gruß
maestrox

Und, was ist die Frage? oder wolltest Du uns nur mitteilen Daß Du so ein Ding hast? :wink: :wink: :wink:
Grüße Uwe

Sorry hatte zu früh auf save gedrückt da war der Text noch garnicht fertig :slight_smile:

Hallo zusammen,

da ich mit dem Code von oben keine passende OBIS Meldung bezüglich Zäherstand oder momentaner Leistung bekomme habe ich einen anderen Code versucht. Das Programm habe ich mir aus einigen Beispielen und Beträgen zusammen "kopiert". Das Programm sucht nach dem Zählerstand. Der Zäherstand wird in der SML Nachricht mit folgendem HEX Code "Angeführt" 01 00 01 08 01 (siehe Beispiel vom VZ Wiki)

Beispiel vom VZ Wiki
77 07 81 81 c7 82 03 ff 01 01 01 01 04 xx xx xx xx - Herstelleridentifikation
77 07 01 00 01 08 00 ff 01 01 62 1e 52 fc 69 00 00 00 00 04 12 40 38 01 - 1.8.0 (= Wirkarbeit Bezug (+) Zählerstand Total): ?
77 07 01 00 01 08 01 ff 01 01 62 1e 52 01 65 00 00 02 3f 01 - 1.8.1 (= Wirkarbeit Bezug (+) Zählerstand Tarif 1): 5.75 kWh (?)
#include <SoftwareSerial.h> 
#define RX_PIN 10  
#define TX_PIN 11 
SoftwareSerial ir( RX_PIN, TX_PIN);
const char* clientId = "smartmeter";
String readString;
const byte search[] = { 0x01, 0x00, 0x01, 0x08, 0x00 };


byte smlMessage[700]; 
int startIndex;   // for counting startSequence hits
int smlIndex;     // represents the actual position in smlMessage
byte inByte; // for reading from serial 

void setup() {
 startIndex=0;
 Serial.begin(9600);
 ir.begin(9600);
 ir.listen();
 Serial.write("Start...");
}

void loop() 
{
   
while (ir.available()) 
 {
   //delay(10);  //small delay to allow input buffer to fill

   inByte = ir.read();  //gets one byte from serial buffer
   inByte &= 0x7F;
   if (inByte == search[startIndex])
   {
     smlMessage[startIndex] = inByte;
     startIndex++;
     if (startIndex == sizeof(search))
      {
        smlIndex = startIndex;
        publishMessage();
        startIndex=0;
      }  
   }
 
 }
}   
 
 if (readString.length() >0) {
   Serial.println(readString); //prints string to serial port out

   readString=""; //clears variable for new input
 }
}


void publishMessage() {

  int arrSize = 2 * smlIndex + 1;
  char smlMessageAsString[arrSize];
  char *myPtr = &smlMessageAsString[0]; //or just myPtr=charArr; but the former described it better.
  
  for (int i = 0; i <= smlIndex; i++) {
    snprintf(myPtr, 3, "%02x", smlMessage[i]); //convert a byte to character string, and save 2 characters (+null) to charArr;
    myPtr += 2; //increment the pointer by two characters in charArr so that next time the null from the previous go is overwritten.
  }

  Serial.println(smlMessageAsString); // for debuging
  char* topic = "/energy/sml";
  char* path = (char *) malloc(1 + strlen(clientId) + strlen(topic) );
  strcpy(path, clientId);
  strcat(path, topic);
  memset(smlMessage, 0, sizeof(smlMessage)); // clear the buffer
  smlIndex = 0;
  
}

Mein Programm gibt zwar den gesuchten HEX wert aus, aber ich brauche ja die nächsten ca. 20 Byte hinter dem Suchwert.
Wie kann ich mein Programm dahingehen am besten anpassen?

LG
Maestrox

Du wirst an Deinem Programm im Dauerbetrieb keine Freude haben, weil Du Dir den Speicher zumüllst.

  1. Du verwendest malloc und gibst den Speicher nie wieder frei.
  2. Du verwendest die Klasse String (obwohl Du readString eigentlich nicht benutzt). String fragmentiert den RAM.

Gibt es für Deine Message einen eindeutig definierten Anfang bzw. ein Ende (Zeichenfolge oder so was)? Das könnte einiges vereinfachen.

Da fehlen noch ein paar Infos, z.B. ein Link zu diesem Wiki.

Übrigens: Wenn Du das Zeichen mit inByte &= 0x7F; bearbeitest, wirst Du nie ein 0xFF finden können.

Gruß Tommy

Hallo,

vielen Dank für deinen Hinweis.
Also Wiki Artikel zu dem SML Protokoll und dem Beispiel für den Q3D findet man hier.

Grundsätlich fängt eine SML Nachticht mit 1b 1b 1b 1b und einem darauf folgendem 01 01 01 01 an.
Danach folgenden IDs und Längenangaben.
Eine SML Nachricht die zb einen Zählerstand enthält beginnt mit :
77 07 + oder OBIS Kennzahl zB. für den Zählerstand (1-0:1.8.0*255)
das wäre dann
77 07 01 00 01 08 00 FF

Das mit dem inByte &= 0x7F; habe ich gemacht, da der Zähler mit 9600/7E1 übeträgt.
Per default empfängt SoftSeria mit 8n1.

Lg
Maestrox

maestrox:
Hallo,

vielen Dank für deinen Hinweis.
Also Wiki Artikel zu dem SML Protokoll und dem Beispiel für den Q3D findet man hier.

Grundsätlich fängt eine SML Nachticht mit 1b 1b 1b 1b und einem darauf folgendem 01 01 01 01 an.
Danach folgenden IDs und Längenangaben.
Eine SML Nachricht die zb einen Zählerstand enthält beginnt mit :
77 07 + oder OBIS Kennzahl zB. für den Zählerstand (1-0:1.8.0*255)
das wäre dann
77 07 01 00 01 08 00 FF

Warum wartest Du dann nicht, bis Du 4x0x1b und danach 4 x 0x01 empfängst. Dann hast Du einen definierten Anfang und kannst das Telegramm aufzeichnen.

Wenn Dann wieder ein 0x1b kommt merkst Du Dir die Position. Wenn es dann wieder 4x0x1b und danach 4 x 0x01 sind, war das die Endeposition und Du schreibst dort ein 0-Byte als Abschluß hin. Wenn nicht, wartest Du wieder auf 0x1b.

Damit kannst Du sicher sein, dass Du eine komplette Message hat und dann kannst Du diese auswerten.

maestrox:
Das mit dem inByte &= 0x7F; habe ich gemacht, da der Zähler mit 9600/7E1 übeträgt.
Per default empfängt SoftSeria mit 8n1.

Dann ist entweder Deine Übertragung falsch oder Deine Info mit dem 0xFF.
Außerdem sollten beide Seiten einer Kommunikation mit dem gleichen Protokoll arbeiten.

Gruß Tommy

Guten Abend,
.
Ich werde im laufe der Woche versuchen den Code dahingehend anzupassen.

Vielen Dank bis dahin für den Support :slight_smile:

Gruß
Maestrox

Hallo maestrox,

warum willst Du unbedingt HEX-Daten auswerten ?
Das EASYMETER sendet ASCII Klartext.
Wie wäre es mit folgendem Sketch. Bei mir funktioniert der gut.

Je nach Nutzung einer SoftSerial-Schnittstelle über Digital-Ports, oder (nur bei MEGA oder DUO) einer der Hardwareschnittstellen Serial1 - serial3) sind die beiden Alternativanteile des Sketches durch setzen/entfernen der Kommentarzeichen auszuwählen.

(Weitere Erklärungen sind in den Kommentaren enthalten)

Durch Kontrolle der Länge und zB. erstes und letztes Zeichen eines Datensatzes kann man Übertragungsfehler mit hinreichender Sicherheit erkennen und ausblenden.

Die einzelnen Meßwerte kann man immer an der selben Position in einem Datensatz finden und als Teilstring weiter verarbeiten.

Umwandlung in Gleitkommazahlen für Nutzung in Tabellenkalkulations- oder Datenbankprogramme ist dann auch einfach möglich.

#include <SoftwareSerial.h>         // Bibliothek für serielle Kommunikation über beliebige Digitalports 
                                    // "softSerial()"-Bibliothek stellt in etwa gleiche Funktionen und Syntax wie "Serial()"-Bibliothek zur Verfügung.
                                    
SoftwareSerial softSerial(11, 10); // Pin D11 ist Rx, Pin D10 ist Tx.

// Variablen definieren und initialisieren
char Rx1Chr = "";       // Variable zur Zwischenspeicherung eines empfangenen Bytes
String Rx1String ="";   // in diesem String werden die Einzelzeichen zu einem Text kumuliert

void setup()
{
  // initialisieren der seriellen Schnittstelle des Arduino.
  Serial.begin(57600);
  Serial.println("Serial interface ready for action");

/*/ Alternative 1:
  //Initialisieren der Schnittstelle "Serial1" (nur bei MEGA und DUO möglich) 
  Serial1.begin(9600,SERIAL_7E1);   // 6900 bit/s, 7 Nutzdatenbits, even-Parity, 1 Stopbit
  // Serial1.println("Serial interface1 ready for action"); // nur, wenn auch Daten an "Serial1" gesendet werden können
*/
// Alternative 2: 
  // Initialisieren der seriellen Schnittstelle aus der SoftwareSerial Bibliothek.
  softSerial.begin(9600); // 6900 bit/s, es werden immer 8 bit pro Zeichen eingelesen (ggf. ist das oberste bit (MSB)das Parity-bit)
  // softSerial.println("softSerial interface ready"); // nur, wenn auch Daten an "softSerial" gesendet werden können
  
}

void loop() 
{ 
/*/Alternative 1:
//Nutzung der Serial1-Schnittstelle (nur MEGA oder DUO)
  while(Serial1.available() > 0){          // solange Daten im Rx-Pufferspeicher sind (maximal 64 Byte) oder neue empfangen werden
    Rx1Chr = Serial1.read();               // Speichere das empfangene Zeichen in der Variablen
    Rx1String.concat(Rx1Chr);              // empfangenes Zeichen an den Rx-String anfügen
  }
*/  
// Alternative 2:
// Nutzung der SoftwareSerial-Schnittstelle  
  while(softSerial.available()) {          // solange Daten im Rx-Pufferspeicher sind (maximal 64 Byte) oder neue empfangen werden
    Rx1Chr = softSerial.read();            // Speichere das älteste empfangene Zeichen in der Variablen
    Rx1Chr &= B01111111;                   // das MSB enthält das Paritätsbit und wird durch diese bit-weise UND-Verknüpfung auf 0 gezwungen
    Rx1String.concat(Rx1Chr);              // empfangenes Zeichen an den Rx-String anfügen
  }
  

 // Weiterverarbeitung der empfangenen Daten ( Beispiel zum Mitlesen auf dem seriellen Monitor in der Arduino-IDE)
  Serial.print(Rx1String);                 // sende den empfangenen Text auf die USB-Schnittstelle
  Rx1String="";                            // Stringvariable für nächsten Datenblock frei machen
}

Gruß

Tommy56:
Warum wartest Du dann nicht, bis Du 4x0x1b und danach 4 x 0x01 empfängst. Dann hast Du einen definierten Anfang und kannst das Telegramm aufzeichnen.

Wenn Dann wieder ein 0x1b kommt merkst Du Dir die Position. Wenn es dann wieder 4x0x1b und danach 4 x 0x01 sind, war das die Endeposition und Du schreibst dort ein 0-Byte als Abschluß hin. Wenn nicht, wartest Du wieder auf 0x1b.

Damit kannst Du sicher sein, dass Du eine komplette Message hat und dann kannst Du diese auswerten.
Dann ist entweder Deine Übertragung falsch oder Deine Info mit dem 0xFF.
Außerdem sollten beide Seiten einer Kommunikation mit dem gleichen Protokoll arbeiten.

Gruß Tommy

Guten Abend,

leider bin ich heute erst wieder dazu gekommen mich etwas weiter mit dem Projekt auseinanderzusetzen.
@helios1
Das habe ich auch immer gedacht aber leider habe ich es nie geschafft, dass ich den ASCII String sauber angezeigt bekomme.
Ich habe jetzt deinen Code mit der "Alternative 2" getestet, da ich nur einen UNO habe.
Leider bekomme ich auch mit diesem Code keine saubere Ausgabe.
So siehts in etwa aus:

❑ESY❑❑❑7D❑❑co❑uw❑❑❑❑ uws....

Ich denke da passt etwas nicht mit der Seriellen Übertragung.
maestrox

Serial.begin(57600);
Serial.println("Serial interface ready for action");

Funktioniert denn das?
Wenn ja, liegt es an der SoftSerial-, wenn nicht, an der Serial-Schnittstelle.
Das halbiert die möglichen Probleme.

Hallo,

das

Serial.println("Serial interface ready for action");

wird sauber übertragen.

Ich habe jetzt auch mal den IR-TTL Kopf per FTDI Adapter an mein Notebook angeschlossen und mit HTerm.exe geschaut was so ankommt. Leider auch nicht wirklich ASCII ...

Wie kann ich am besten die Serielle Übetragung testen?

Gruß
Maestrox

while(Serial1.available() > 0){          // alle Daten im Rx-Pufferspeicher
   int  Rx1Chr = Serial1.read();               // lesen
   Serial.println (Rx1Chr, HEX);               // und den Code Hex anzeigen
}

Hallo,

mit dem Code

while(Serial1.available() > 0){          // alle Daten im Rx-Pufferspeicher
   int  Rx1Chr = Serial1.read();               // lesen
   Serial.println (Rx1Chr, HEX);               // und den Code Hex anzeigen
}

Bekomme ich natürlich alles in Hex ausgegeben.
1B1B1B1B1111765E28D4........

Leider nicht wirklich lesbar. Ich dachte das ich wie bei helios1 die Nachricht direkt als ACSII ausgeben kann.

Gruß
Maestrox

Und wie möchtest du das Zeichen 0x1B gerne ausgeben? Wenn du anstelle von meinem println wenigstens ein Leerzeichen schreibst, ist es doch schön lesbar.
Um daraus beispielsweise den Text "" zu machen, musst du nur etwas Fleissarbeit leisten.
Fragt sich, ob sich das lohnt.
In der ASCII - Codierung ist halt alles <0x20 und die 0x7F ein Steuercode, den du nach Geschmack behandeln kannst.

Hallo,

also eigentlich sollte der Zähler eine Nachricht in folgendem Format übertragen (wie auch von helios1 beschrieben)

siehe hier

/ESY5Q3DA1024 V3.021-
0:0.0.0255(XXX)
1-0:1.8.0
255(00003985.3838842kWh)
1-0:2.8.0
255(0000284.9368242kWh)
1-0:21.7.255
255(000176.85W)
1-0:41.7.255
255(001025.66W)
1-0:61.7.255
255(002529.68W)
1-0:1.7.255
255(02292.19W)
1-0:96.5.5
255(82)
0-0:96.1.255*255(1ESYXXXXXXXXXX)!

Leider bekomme ich es weder per Arduino oder Com-Port lesbar...

Grundsätlich fängt eine SML Nachticht mit 1b 1b 1b 1b und einem darauf folgendem 01 01 01 01 an.
Danach folgenden IDs und Längenangaben.
Eine SML Nachricht die zb einen Zählerstand enthält beginnt mit :
77 07 + oder OBIS Kennzahl zB. für den Zählerstand (1-0:1.8.0*255)
das wäre dann
77 07 01 00 01 08 00 FF

Das sieht doch ziemlich ähnlich aus...

Da hast du natürlich recht aber leider ist die empfangene Nachricht nicht komplett.
Ich empfange immer nur ca 230 Byte

Guten Morgen :slight_smile:

ich habe jetzt mal einen anderen Versuch mit meinem PI3 gestartet.
Ich habe mich an diese Anleitung gehalten und entsprechend auf den Pi3 angepasst.
Aber auch hier bekomme ich nicht den gewünschten Output :cry:

Gruß
maestrox

Dazu solltest Du wohl besser ein PI-Forum befragen.

Gruß Tommy