UDP Datagramm von Energymeter empfangen

Hallo liebe Forengemeinde. Ich bin neu hier und beschäftige mich erst seit kurzem mit dem Arduino.

Aktuell habe ich einen Arduino Uno mit einem W5100 basierten Ethernet Shield im Einsatz. Es sind nur die standardmäßigen Bibliotheken installiert.

Mein erstes Projekt soll ein im Haushalt vorhandenes SMA Energy Meter auslesen.

Dieses habe ich in einem kleinen Testaufbau direkt mit meinem PC verbunden und mittels Whireshark herausgefunden wie die Datagramme aussehen und von wo nach wo gesendet wird. Das Energy Meter sendet zyklisch die aktuellen Messdaten als UDP Unicast Datagramme. Im Anhang findet ihr einen Screenshot von Wireshark.
Anbei noch die Informationen als Text.

Ziel IP 239.12.255.254
Port 9522
IP Energy Meter 169.254.8.48
Datagramm Länge: 624Bytes

Im ersten Schritt möchte ich die Datagramme nur einlesen und einfach ohne jegliche Bearbeitung im Serial Monitor ausgeben lassen. Ich habe aktuell leider das Problem, dass ich nichts empfange. Kann mir jemand sagen was ich falsch mache ?
Ich bin für jede Hilfe super dankbar. Vielen Dank an alle. Liebe Grüße
Andreas

Hier ist der Code.

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>    // UDP library from: bjoern@cs.stanford.edu 12/30/2008

// Network Settings
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(169, 254, 8, 1);             // IP from EM Meter 169.254.8.48
IPAddress gateway(192, 168, 178, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress RecipientIP(239, 12, 255, 254);   // Multicast Dest IP from Energy Meter
unsigned int portMulti = 9522;              // local Multicast Port to be listen on

// buffers for receiving and sending data
char packetBuffer[624];
unsigned int packetSize = 624;

// An EthernetUDP instance to send and receive packets over UDP
EthernetUDP Udp;

void setup() 
{
  // for debug only
  Serial.begin(9600);
  // start Ethernet
  Ethernet.begin(mac,ip);
  // start multicast UDP
  Udp.beginMulticast(portMulti, RecipientIP);
  
}
void loop ()
{
  // read the packet into packetBufffer 
  Udp.read(packetBuffer,packetSize);
  // for debug only
  // write packetBuffer to serial
    Serial.println("Arduino UDP Ready for listen to Energy Meter ");
    Serial.print("Listening to Unicast at:  ");
    Serial.println(portMulti);
    
    Serial.print("Recipient_IP: ");
    Serial.println (RecipientIP);
    Serial.println("Packet Content: ");
    Serial.println(packetBuffer);

    delay(5000);

}

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden. Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

Warum verwendest Du Multicast?
Ich vermute: Du bekommst gar nichts, weil Milticast Reception meint: da war auch ein Multicast-Sender: Multicast und Unicast unterscheiden sich in der IP-Adresse benutzt: kann gut sein, dass Du deshalb nichts empfaengst.

Wenn ich UDP "Single/UNI Cast" verwende, dann sieht das in meinen Code so aus (Prinzip):

  • oeffne den UDP port, fuer ANY (jeder darf mir senden)
  • jeden, der mir mit diesem UDP port was sendet, sehe ich (egal vom wem, ich bin ja "offen" fuer ANY an diesem Port)

Multicast ist ein separates Thema: das verwendet (so weit ich weiss) andere IP-Adressen und ist nicht immer garantiert, ob das auch geht (oder erlaubt ist: die fluten ja das Netzwerk, kann ja auch sein, Dein Netzwerk-Router "verbietet" die Weiterleitung von Multicast).

Versuch doch einfach mal eine Uni-Cast-Verbindung:

  • Server (MCU, receiver) oeffnet UDP Port mit "ANY" fuer jede Sender-Adresse rein kommend
    (ein ganz einfaches "bound" mit Port und ANY)
  • Sende vom Client (PC, sender) direkt nur an diesen Server (musst natuerlich die MCU Adresse kennen und beim Absenden angeben)

Multicast ist sicher etwas "trickier": Ich denke, "Multicast" gibt es nur auf Sender-Seite: ein Empfaenger (Server, Deine MCU) braucht das gar nicht wissen, ob er ein Multicast-Receiver ist:
Der empfaengt so-und-so alles fuer das er "offen" ist (egel von wem).
Nur der Sender muss entscheiden, ob "fuer alle", oder nur fuer einen sendet. Dein Empfaenger hat keine Ahnung ob es Multicast war (es sei denn, die IP-Adresse an der er "listen" macht, wuerde das vorgegeben, die ist aber sicher eine andere als eine Unicast-Adresse).

Einfach auf Unicast umstellen (und mit ANY am Server empfeaengt von allen moeglichen Sendern, ohne zuwissen, wer das war).

bei Fipsok gibt es gute Beispiele für UDP.
Was mir bei dir eigenartig vorkommt sind die IP Addresse und das Gateway.
Das Delay macht vermutlich auch nichts gutes im Sketch.

Deine ip passt nicht zum Gateway.

Gruß Tommy

Weil der Sender (siehe Screenshot) Multicast sendet.

Gruß Tommy

Hallo

Vielen vielen Dank für die wertvollen Hinweise. Der Tip mit dem Unicast hat funktioniert.
Ich habe den Quellcode so umgeschrieben, dass der Arduino am Port 9522 lauscht und siehe da Ich empfange jetzt etwas.

```cpp
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>    // UDP library from: bjoern@cs.stanford.edu 12/30/2008

   
    // Network Settings

  byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
  IPAddress ip(192, 168, 178, 25);
  unsigned int localPort = 9522;                         // local port to listen on
 
  EthernetUDP Udp;                                      // An EthernetUDP instance to send and receive packets over UDP
  char packetBuffer[600];                               //buffer to hold incoming packet,


void setup() 

{
  // start the Ethernet and UDP:// for debug only
  Serial.begin(9600);
  // activate Ethernet shield
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);
}

  
void loop ()
{
  
  int packetSize = Udp.parsePacket();                    // check of packet to be received
  if(packetSize)                                         // execution if packet has been received
    {
      Serial.print("Received packet of size: ");          // packet size information
      Serial.println(packetSize);
      Serial.print("From Remote Port: ");               
      IPAddress remote = Udp.remoteIP();                 // port information
      Serial.println(Udp.remotePort());

      Udp.read(packetBuffer,packetSize);                 // read the packet into packetBuffer
      Serial.print("Contents: ");
      
      

      int number = (int)strtol(packetBuffer[0x34], NULL, 16);
      Serial.println(number);


    }
    else
    {
      Serial.println("No packet received");
    }


    delay(1000);

}


Mein Empfangspuffer aktuell als Char Array deklariert.

Das Datagramm besteht aus Hex Zahlen, wobei die Nutzdaten mit 53 40 41 beginnen.  Dies entspricht als ASCII:  53--> S , 40--> M , 41--> A

Diese 3 Buchstaben bekomme ich auch angezeigt wenn ich den kompletten Paketbuffer mittels serialprint ausgeben lasse.  

Mein Problem ist gerade wie ich den relevanten Datenteil extrahiere und als Integer umwandeln kann. Im Screenshot habe ich diesen mal markiert.
Ich bin für jeden Hinweis sehr dankbar. 

Viele Grüße

Andreas

![Screenshot UDP Energymeter|690x299](upload://sfuropIcR8Co7d7B6mwfYj9Wun3.jpeg)

Dein Teil unter Sketch ändern, nicht in Code Tags setzen :wink:
und dein Foto ohne ! reinsetzen

Cool, "well done".
Unicast ist viel einfacherer (und klarer). Multicast kann "tricky" sein, wenn z.B. Router das "fluten des Networks mit Multicast" verhindern.
Und als MCU (Empfaenger) will ich ja nicht auch alles und den ganzen "Kram nicht fuer mich" reinlassen. Multicast kann sogar in der FW "geblockt" (nicht unterstuetzt) sein.

Hallo

Danke für den Hinweis. Hier nochmal der Screenshot; ohne macht die Frage nicht viel Sinn :slight_smile:

Wie gesagt die ersten 6 Bytes sind tatsächlich ASCII Zeichen. Danach folgen HEX Ziffern. Das erste was an Daten relevant ist habe ich mal markiert. Es ist / beginnt an der Stelle 0x20 im Empfangspuffer. Der zweite relevante Messwert beginnt an der Stelle 0x34.

Man sieht, dass mehrere HEX Zahlen einen Wert bilden. Nur wie bekomme ich daraus einen Integer Wert? Daran scheitere ich gerade. Hat jemand eine Idee ?

Bin für jede Anregung dankbar. Viele Grüße Andreas

Man sieht aber

Weder 3 noch 5 Byte machen einen Sinn.
Woher nimmst Du die Info, dass das sinnvolle Inhalte wären?

Gruß Tommy

Ich sehe auch nicht viel. Und die "Annahme", da waere ASCII ist sicher falsch (jeder Wert kann aussehen wie ASCII), e.g. 0x48 0x41 0x4C 0x4C = "HALL" (als hex value vermutlich: 0x4C4C4148)

Was machst denn genau?
Wenn Du ein UDP-Paket mit irgendwas drin verschickst, dann sollte es doch genauso - als Byte-Stream - am Empfaenger ankommen (1:1).

Wenn Du nun 4byte values darin verpackst, dann musst Du "nur" wissen, in welchem Endian-Format die einzelnen Bytes verschickt werden. Das haengt aber auch davon ab, wie Du denn 32bit Werte ueber Network (UDP) versendest:

Machst Du z.B. das (Prinzip, kein nutzbarer Code):

char *udpBuffer[BUF_LEN] ALIGNED(32);
int *valPtr = (int *)udpBuffer;
*valPtr = 0x12345678;

udpSender(udpBuffer);

dann bekommst Du auf Host-Seite (Empfaenger) einen "Byte-Stream" der so aussieht:

0x78 0x56 0x34 0x12

Warum?:
Du schreibst einen 32bit-Wert in einen Byte-Buffer. Die MCU ist ARM based and die verwendet Little Endian. Das meint: die Bytes sind "verdreht": das niedrigste Byte kommt zuerst, das hoechste zuletzt (weiter hinten im Speicher). Es sieht also so aus, als wenn Du ein "Byte-Array" "rueckwaerts" lesen musst (angenommen ein Hex-Dump mit Bytes auf Bildschirm, wie auch mit Deinem Network-Sniffer-Dump).

Anmerkung: dieses ALIGN(32) ist notwendig, wenn Du 32bit-Werte direkt in den Byte Buffer schreiben willst (wie hier, mit cast). Alles muss 32bit aligned sein und bleiben, sonst crasht es.

Nun musst Du nur auf Empfaenger-Seite wissen, wie denn 32bit (4 Bytes) via einem "Byte Buffer" kodiert wurden, uebertragen wurden. Du musst das Little Endian auf Empfaenger-Seite genauso machen, beachten.

Wenn Du empfangen wuerdest via:

char udpBuffer[BUF_LEN) ALIGNED(32);
udpReceiver(udpBuffer);

int *valPtr;
valPtr = (int *)udpBuffer;

int val;
val = *valPtr;

dann "koennte" es vielleicht klappen: Du liest 4 Bytes, welche als ein 4-Byte-Word verwendet werden (hier als int).
Das klappt, solange der Empfaenger (Host, PC), auch Little Endian verwendet (und die meisten Prozessoren sind Little Endian, auch im PC).
Aber das ist nicht garantiert (eine 68000 MCU hat Big Endian, alte iMacs). Also wissen, wie ist denn das Endian auf beiden Seiten.

So, besser (und sicherer) waere:
Du weisst, dass die 4 Bytes als Little Endian versendet wurden (Dein "Protokoll"), also "verdreht". Dann kannst Du ja das 4-Byte-Word selbst wieder bilden, z.B. mit:

char udpBuffer[BUF_LEN);  //muss jetzt nicht aligned sein
udpReceiver(udpBuffer);

int val;
val   = udpBuffer[0];             //lowest byte first
val |= udpBuffer[1] << 8;
val |= udpBuffer[2] << 16;
val |= udpBuffer[3] << 24;   //Little Endian format used - highest byte last

dann machst Du diese Little Endian Conversion explizit selbst: Du sammelst die 4 Bytes, die in "falscher Reihenfolge" kommen und generierst ein int Wert fuer Deine Platform (als 4-Byte Value), ohne wissen zu muessen, was das Endian auf Deiner Platform ist - aber wissend, mit welchem Endian es verschickt wurde!.

Es ist eine Frage des "Protokolls", einer "Vereinbarung" zwischen Sender und Empfaenger, wie denn Words (alles groesser als ein Byte) verpackt wurden (und wieder entpackt werden muessen).

BTW:
Wenn Du Network-Sniffer (WireShark) verwendest - Du solltest genau wissen, was MAC, IP, TCP/UPD Layer ist: wo faengt denn genau der Inhalt des UDP-Paketes an? (ohne den ganzen "Kram" davor, mit IP-Address, IP-Header usw.)

UDP ist 1:1 - was Du sendest sollte so (als Byte-Stream) auch ankommen, beim UDP-Receiver.
Was darin verpackt ist und wie - ist Deine Konvention (Protokoll).
Musst nur darauf achten: wenn alles groesser als ein Byte, also als ein Word in den Puffer kommt, dann schlaegt der Endian zu.

Oder einfacher gesagt:
Das Netzwerk bietet das Versenden und Empfangen von "Byte-Streams". Was darin und wie verpackt ist - das Netzwerk hat keine Ahnung. Es ist Aufgabe Deiner Software, das zu "interpetieren" (Dein "Protokoll", Dein "Paketieren und Entpacken").

Beide Seiten betrachten: Du brauchst eine "Paerchen-SW" auf Sender und Empfaenger-Seite, die sich beide "verstehen" (wissen, was und wie darin verpackt ist = Teil des "Protokolls").

BTW:
Wenn Du Bild von OSI-7-Schichten-Modell siehst, z.B.
OSI 7 Layer Model
dann sieht man:
UDP ist von unten bis nach Schicht 4 (Transport). Darueber sitzt aber auch Schicht 6 (Presentation). Genau da hinein gehoert das mit dem Endian: "wie muss ich denn die Daten im Byte-Stream dekodieren damit sie zu meiner Platform passen" (z.B. wieder als int-Werte)?

Hallo zusammen,

Danke für die ausführlichen Erklärungen. Das mit der Länge der relevaten Werte ist natürlich richtig. Es sind 4 Byte oder 8 Byte Werte vorhanden. Ich hätte das in dem Screenshot besser markieren sollen. Ich bitte um Entschuldigung.

Ich habe eine Beschreibung zu dem Datagramm.
Das ganze beginnt mit einem Header von 28 Byte.

Der eigentliche Nutzdatenbereich beginnt tatsächlich mit den 3 Zeichen SMA
(was der Hersteller des Energymeters ist).

Schaut man sich die 600 Bytes der Nutzdaten Ausgabe des Energy Meter genauer an (hexadezimal), so fällt einem relativ schnell eine Struktur auf, die anscheinend mit 0400 oder 0800 endet.

Zerlegt man dies vorsichtig, ergeben sich kleine Gruppen mit Adressierungen.

Teilt man das weiter auf, so kommen 4 und 8 Bytes zum Vorschein, was wohl auch mit 04 und 08 gemeint ist.

Der erste Block ist die Summe an Werten aus Block 2, 3 und 4.
Daher ist davon auszugehen, dass es sich folgt aufteilt:

Datenblock 1: Gesamtsummen
Datenblock 2: Messwerte an L1
Datenblock 3: Messwerte an L2
Datenblock 4: Messwerte an L3

Hier mal die komplette Aufschlüsselung. Leider ist das Bild etwas klein geraten.
Bitte korrigiert mich aber hier kommt nicht der niederwertige Bitbereich zu erst.
Oder lese ich die Ausgabe vom Wireshark nur falsch ?

Viele Grüße
Andreas

data stream|502x500

Sorry:
UDP ist 1:1 als: "was Du sendest als UDP kommt auch als UDP wieder an".

Wenn Du fragst, wie man denn Network-Sniffer Dump "lesen" soll: Du musst das UDP-Paket darin finden, nur das interessiert (nicht der ganze "andere Kram").
Wie man all die anderen "Overhead-Bytes" im Dump lesen muss, waere die gesamten ETH und IP-Header zu dekodieren zu versuchen und zu verstehen.

Es muss so wie gesendet im Empfaenger wieder ankommen. Mach doch mal ein Dump von dem Puffer, den Du als UDP versendest und dem Puffer der als UDP wieder ankommt im Empfaenger.
Oder: wenn Network-Sniffer: finde zuerst, was denn das UDP-Paket ist.
Aber alle bits von allen Layern (ETH/MAC, IP) zu verstehen ist interessant (aber viel zu schwer).

Was sendest Du als UDP and was kommt an?
(ein Dump von Deiner FW, auf beiden Seiten)
Ist das "matching"?

Wenn Du nun zusaetzlich "Overhead", Daten im UDP-Paket hast, z.B. diesen "Header von 28 Byte" - wer soll wissen, wo der her kommt, wie der zu verstehen ist?
Wenn Du "Roh-Daten" von einem anderen Chip holst, diese 1:1 ueber UDP versendest - dann waere doch die Frage "was dieser Chip macht" (nicht zu UDP)?

Ich glaube, Du willst wissen, was denn das Energy Meter an Daten (und wie) ausgibt. Das hat nichts mit Netzwerk am Arduino oder mit UDP zu tun. Du fragst nach einem externen Geraet, dass wohl keiner besser kennt als Du selbst.
Sorry.

Mach doch wirklich Dump: was sendest Du ab - was kommt an (als UDP).
Ist das korrekt? Vermutlich - alles weitere darin, die Daten vom Energy Meter - wer soll das wissen hier (ausser der Hersteller)?
Wir koennen sicher nicht den Inhalt des UDP dekodieren, der vom Energy Meter kommt (den keiner hier kennt, der nichts mit Arduino zu tun hat...). Sorry

Will nicht ganz so ruppig sein (eher helfen).
Ist Deine Frage zu UDP oder zu "dem Ding?"

  1. Was ist der genaue Typ des Energy Meters? (vielleicht finde ich was im Netz dazu)
  2. Wie holst Du die Daten dort ab? Was machst Du damit?
    (Ich vermute: ueber UART liest Du den aus und willst das ueber UDP Network versenden.
    Das sollte 1:1 gehen. Was aber in der UART-Antwork steckt als Info? Das muesste man mit Energy Meter Dokumentation pruefen, beim Hersteller erfragen...)
  3. Kommt denn das was Du sendest wieder genaus so an? Ich hoffe und denke.
  4. Was denn diese Daten bedeuten? Das waere allein mit dem Energy Meter zu pruefen
    (Hersteller, Doku, Spec....), hat nichts mit Netzwerk, Arduino zu tun (ausserhalb dieses Forums)

Ein Tipp, wenn Du denn "das Ding reverse engineeren" willst:
Mach doch mal kleine Aenderung, an dem was das Ding misst: z.B. etwas mehr Strom-Verbrauch. Welcher Wert (welche Bytes) aendern sich nun?
Oder auch mal Spannung varieren? Misst der die und gibt die irgendwo in den Bytes aus? (veraenderte Bytes zu sehen?)
Wenn Du parallel misst, z.B. den Strom - korrelieren die Werte "etwas"?
Siehst Du einen Zusammenhang mit "Strom aendern - anderer Messwert und anderer Wert vom dem Ding in den Bytes?"
Wenn ja: kannst Du zumindest verstehen, wo der den Strom "kodiert" wird, wie der Wert zusammen haengt (oder die Spannung).

Ein externes Geraet zu verstehen, was das denn tut (hier mehr: wie der denn Werte uebermittelt), ist sehr muehsam (und Reverse Engineering - manchmal sogar "verboten" zu tun).

Was weisst Du denn ueber dieses Ding?
Kannst Du Unterlagen finden, bekommen, wie das denn Messwerte bereit stellt und uebermittelt/kodiert?
Wenn nicht Info dazu: viel Spass beim "Herausknobeln" was dieses Ding denn irgendwie zu machen scheint.

Sorry das ich hier rienplache, SMA Protokolle sind doch zugänglich egal ob Modbus oder UDP. Zwei Sekunden suche hat so was ergeben

Great, danke: das meine ich auch: "gibts Infos im Netz zu dem Ding...?"
Viel Erfolg dem OP, das Ding aus zu lesen (ohne Sarkasmus). Klingt spannend, wuerde ich auch "mal machen" aber keine Anwendung dafuer fuer mich (meine Zaehler hier in USA sind anders).
Alle Infos zusammen tragen, diese ueber Kreuz verknuepfen, das "Verstandene" verifizieren (probieren) ... ist aufregender als "nach fertigen Loesungen zu fragen". LOL :smiley:

Hallo zusammen,

Ich finde es echt toll, dass mein kleines Projekt hier solche Unterstützung findet. Das wollte ich mal los werden.

Ich beschreibe mal was das Energy Meter aktuell bei mir macht.

Das SMA Energy Meter sitzt in der Hauptverteilung unseres Hauses und überwacht den Hausanschluss. Die vorhandene PV Anlage und der Batteriespeicher wird über dem SMA Smart Home Manager gesteuert und das Energy Meter liefert die Einspeise und Bezugswerte für die Regelung der Batterieladung und des Einspeisemanagements.

Endziel des Arduino Projektes soll es sein, dass dieser das Datagramm des Energy Meters ausliest, den Wert für die Einspeiseleistung extrahiert und damit einen Phasenanschnitts Controller ansteuert der wiederum die Leistung eines Heizstabes einstellt. Ziel ist es überschüssigen Strom für die Warmwassererzeugung zu verwenden. Die Leistung des Heizstabes soll also immer auf die aktuell zur Verfügung stehende Einspeiseleistung auf Null zu bringen sofern was Wasser im Speicher noch nicht seine Endtemperatur erreicht hat und das mechanische Bimetall Thermostat den Heizstab abschaltet.

Was die rechtlichen Hinweise von tjaekel bzgl des reverse engineering angeht.

Ich hatte die gleichen Bedenken als ich mit dem Projekt angefangen habe. Deshalb verzeiht mit bitte die "Salami Taktik".

Den Link zu der Beschreibung des Herstellers SMA zu dem Protokoll vom Zähler kannte ich noch nicht. Das Dokument darin liegt mir vor. Allerdings ist meine Version nur 2 Seiten lang und liefert nur die Port Nr. und die Info das per UDP gesendet wird.

Die komplette Aufschlüsselung, wie in der neueren Version, ist darin nicht enthalten.
Da SMA da aber anscheinend zwischenzeitlich freigegeben hat sollte es rechtlich keine Probleme geben, da ich damit ja kein Produkt erschaffen will.

Da ich was den Arduino betrifft noch am Anfang der Lernkurve bin wäre mein erstes Ziel den Nutz Daten Stream des Energy Meters einzulesen und in einem Array zu speichern. Den Wert für die aktuelle Einspeisung im Serial Monitor auszugeben.

Viele Grüße Andreas