Arduino + Ethernetshield als TCP Client ?

Hallo,

ich möchte Werte aus einem Wechselrichter auslesen. In der Doku des Herstellers (die ich vertraulich behandeln muss) heißt es, man muss zuerst eine Verbindung zum Port 81 aufbauen und dann 8 Bytes als Request senden, der Server antwortet dann mit 11 Bytes.
Ich habe den Webclient Sketch entsprechend modifiziert,

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
byte server[] = { 192,168,2,11 };    // Wechselrichter

// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192,168,2,20);

// Initialize the Ethernet client library
// with the IP address and port of the server 
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;

void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(9600);

  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    // try to congifure using IP address instead of DHCP:
    Ethernet.begin(mac, ip);
  }
  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.println("verbinde...");
  
  client.connect(server, 81);
 

  // if you get a connection, report back via serial:
  if (client.connected()) {
    Serial.println("verbunden");
    // Make a  request:
    client.print(0x62);
    client.print(0xff);
    client.print(0x03);
    client.print(0xff);
    client.print(0x00);
    client.print(0x45);
    client.print(0x58);
    client.print(0x00);
  } 
  else {
    // kf you didn't get a connection to the server:
    Serial.println("keine Verbindung");
  }
}

void loop()
{
  // if there are incoming bytes available 
  // from the server, read them and print them:
  if (client.available()) {
    byte c = client.read();
    Serial.print(c);
  }

  // if the server's disconnected, stop the client:
  if (!client.connected()) {
    Serial.println();
    Serial.println("trennen.");
    client.stop();

    // do nothing forevermore:
    while(true);
  }
}

aber mir gelingt es nicht eine Antwort zu bekommen.
Der Wechselrichter hat auch einen Webserver (Port 80), da hat es mit dem Webclient Sketch funktioniert, nur würde ich gerne die andere Methode verwenden.

Es gibt eine PC Software, die das macht. Von dieser habe ich mal das Wireshark Protokoll angehängt (192.168.2.11 und 12 sind die Wechselrichter, …102 der PC).

Hat jemand eine Idee?

Viele Grüße
Karl_napp

wireshark.pcapng (15.1 KB)

Hi, Du hast in deinem Code etliche Debug Ausgaben via der serielle Schnittstelle drin. Dies ist schon mal gut. Was bekommst du da angezeigt ?

Sollte das nicht write() statt print() sein?

http://arduino.cc/en/Reference/ClientPrint

Print data to the server that a client is connected to. Prints numbers as a sequence of digits, each an ASCII character (e.g. the number 123 is sent as the three characters '1', '2', '3').

Das ist nicht was du willst

@Serenifly: Logisch, und ich habe es wieder mal nicht gesehen :blush:

Keine Panik. Ich übersehe manchmal auch viel offensichtliches :)

Hallo

und danke für Eure Antworten. Mit client.write() hatte ich es auch schon probiert, da bekomme ich beim Überprüfen aber eine Fehlermeldung (call of overloaded 'write(int)' is ambiguous), wenn ich (0x00) einsetze. Ich habe es dann mit (0, HEX) probiert, da kam die Fehlermeldung nicht mehr, aber auch vom Server immer noch keine Antwort. Andererseits gibt es z.B. bei (62, HEX) anstatt (0x62) auch eine Meldung (invalid conversion from 'int' to 'const uint8_t*'). Ich glaube aber, das Problem liegt schon vorher: Wenn ich den Sketch starte bekomme ich im Serial Monitor zuerst "verbinde..." und nach etwa einer halben Minute "keine Verbindung" und "trenne.". Weiß vielleicht jemand eine Möglichkeit, wie man mit einfachen Mitteln den Netzwerkverkehr zeischen Arduino und dem Server protokollieren kann?

Viele Grüße Karl Napp

karl_napp: eine Fehlermeldung (call of overloaded 'write(int)' is ambiguous), wenn ich (0x00) einsetze.

Null ist ein Sonderfall, wegen des in C definierten Null-Pointers, der ebenfalls den numerischen Wert 0 hat und die vordefinierte Funktion dann nicht weiß, ob sie ein 0-Byte (= 1 Byte) oder einen 0-Pointer (= 2 Bytes) senden soll.

Probiere beim Senden Deiner 0-Bytes mal:

client.write((byte)0x00);

Hallo,

@ Jurs,
danke für den Hinweis, es gibt beim Überprüfen da keine Fehlermeldung, hat mich aber leider auch nicht weiter gebracht.

Ich habe mal Folgendes probiert:

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
byte server[] = { 192,168,2,11 };    // Wechselrichter

// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192,168,2,20);

// Initialize the Ethernet client library
// with the IP address and port of the server 
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;

void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(9600);

  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    // try to congifure using IP address instead of DHCP:
    Ethernet.begin(mac, ip);
  }
  // give the Ethernet shield a second to initialize:
    delay(1000);
    Serial.println("Client connect");
    client.connect(server, 81);
   
    Serial.println("weiter geht's...");
    delay(1000);

    client.write(0x62);
    client.write(0xff);
    client.write(0x03);
    client.write(0xff);
    client.write((byte)0x00);
    client.write(0x45);
    client.write(0x58);
    client.write((byte)0x00);     
    Serial.println("gesendet...");
}

void loop()
{
  
  if (client.available()) {
    Serial.println("frage ab...");
    byte c = client.read();
    Serial.print(c);
  }
  else Serial.println("keine Antwort");

  // if the server's disconnected, stop the client:
  if (!client.connected()) {
    Serial.println();
    Serial.println("trennen.");
    client.stop();

    // do nothing forevermore:
    while(true);
  }
}

Zwischen den Meldungen “Client connect” und “weiter geht’s…” vergeht ca eine halbe Minute, dann folgen sofort:
gesendet…
keine Antwort
trennen.

Ich fürchte, dass da überhaupt keine Verbindung aufgebaut wird. Kann es sein, dass da <Ethernet.h> nicht funktioniert, weil der Hersteller was proprietäres programmiert hat?

Viele Grüße
Karl Napp

karl_napp:
Ich fürchte, dass da überhaupt keine Verbindung aufgebaut wird.

Na klar, da tritt ein Timeout auf.

Und wenn Du wenigstens eine minimale Fehlerbehandlung im Sketch vorsehen würdest, dann würdest Du das auch sehen und bräuchtest dann nicht mal den Versuch machen, etwas zu senden.

Versuch’s mal mit:

   if (client.connect(server, 81))
   {
     Serial.println("connected");     
     client.write(0x62);
     client.write(0xff);
     client.write(0x03);
     client.write(0xff);
     client.write((byte)0x00);
     client.write(0x45);
     client.write(0x58);
     client.write((byte)0x00);     
     Serial.println("gesendet...");
   }
   else 
   {
     Serial.println("connection failed");
   }

Dann siehst Du doch genau, dass Du keine Verbindung zum Server bekommst!

karl_napp:
Kann es sein, dass da <Ethernet.h> nicht funktioniert, weil der Hersteller was proprietäres programmiert hat?

Ethernet ist Ethernet, das ist standardisiert.
Was proprietär ist, ist das Kommonikationsprotokoll des Herstellers.

Du hattest irgendwie ein Wireshark-Protokoll gesendet. Was für ein Format hat die Datei? Ich kann da nur eine Binärdatei sehen, die ich mir nicht anzeigen lassen kann.

Mußt Du das Kommunikationsprotokoll vielleicht am Gerät konfigurieren/einstellen? Du schreibst, das Gerät kann sowohl HTTP auf Port 80 als auch das Firmenprotokoll auf Port 81, vielleicht muss das konfiguriert werden, bevor das Gerät überhaupt Verbindungen auf Port 81 akzeptiert?

Und bist Du überhaupt sicher, dass das andere Protokoll per TCP funktioniert und nicht per UDP?

Hallo Jurs,

erneut Dank für die Antwort.

Was proprietär ist, ist das Kommonikationsprotokoll des Herstellers.

Der Hersteller schreibt da folgendes:

Protocol description (request/respond)
Each inverter has an individual RS485 bus address (valid range: 1 … 220 and 255 in
delivery state), which is required for communication. In order to query the operating values
of an inverter, a TCP connection to port 81 of the inverter is first established. In the
process, the inverter is addressed with its IP address or its name (broken down via
NetBIOS). A request package is then sent to the device. One receives a response
package containing the desired operating data as an answer. Additional requests/
answers can follow before the connection is closed again.

Du hattest irgendwie ein Wireshark-Protokoll gesendet. Was für ein Format hat die Datei? Ich kann da nur eine Binärdatei sehen, die ich mir nicht anzeigen lassen kann.

Die Protokolldatei lässt sich in Wireshark öffnen. Ich habe einen Auszug exportiert und angehängt, die Details fehlen da leider.

Mußt Du das Kommunikationsprotokoll vielleicht am Gerät konfigurieren/einstellen? Du schreibst, das Gerät kann sowohl HTTP auf Port 80 als auch das Firmenprotokoll auf Port 81, vielleicht muss das konfiguriert werden, bevor das Gerät überhaupt Verbindungen auf Port 81 akzeptiert?

Auf den Port 80 komme ich mit dem Firefox und auch mit dem Arduino (wenn ich mich zuvor per Firefox authentifiziert habe). Auf den Port 81 komme ich mit der PC Software des Herstellers (daher stammt auch das Wireshark Protokoll).
Zum Einstellen habe ich da nichts gefunden außer der RS485 bus Adresse, aber die hat ja nichts mit TCP zu tun, oder?

Viele Grüße

wr_test.csv (1.48 KB)

jurs: Ich habe es dann mit (0, HEX) probiert, da kam die Fehlermeldung nicht mehr, aber auch vom Server immer noch keine Antwort. Andererseits gibt es z.B. bei (62, HEX) anstatt (0x62) auch eine Meldung (invalid conversion from 'int' to 'const uint8_t*').

Du verstehst glaube ich nicht ganz was da gemacht wird. (62, HEX) ist ein String und damit ein char*! Damit schickst du wieder mehrere ASCII Zeichen. Du willst doch aber nicht ASCII senden, sondern Binärdaten, oder?

Eine andere Möglichkeiten ist das ganze in einem Rutsch zu senden:

byte buffer[] = { 0x62, 0xff, 0x03, 0xff, 0x00, 0x45, 0x58, 0x00 };
client.write(buffer, sizeof(buffer);

Was vielleicht auch geht ist das:

client.print("\x62\xff\x03\xff\x00\x45\x58\x00");

Das ist ein String, aber man gibt explizit die ASCII Codes vor.

Das ist aber rein vom Sender her. Ob das Gerät darauf reagiert ist was anderes.

karl_napp: Die Protokolldatei lässt sich in Wireshark öffnen. Ich habe einen Auszug exportiert und angehängt, die Details fehlen da leider.

OK, da geht es um hin und her gesendete Daten. Soweit kommst Du aber offenbar gar nicht, da Du kein "Connect" auf Port 81 zustande bekommst.

karl_napp: Auf den Port 80 komme ich mit dem Firefox und auch mit dem Arduino (wenn ich mich zuvor per Firefox authentifiziert habe). Auf den Port 81 komme ich mit der PC Software des Herstellers (daher stammt auch das Wireshark Protokoll). Zum Einstellen habe ich da nichts gefunden außer der RS485 bus Adresse, aber die hat ja nichts mit TCP zu tun, oder?

Wenn Du vom PC aus mit der Herstellersoftware auf das Gerät zugreifen kannst, ohne dass etwas umkonfiguriert werden muß, dann muss nichts umkonfiguriert werden.

Die Frage ist nur: Weshalb bekommst Du vom PC aus sowohl auf Port 80 (Firefox) als auch Port 81 (Herstellersoftware) einen Connect, aber vom Arduino bekommst Du nur auf Port 80 (HTTP-Port) einen Connect, aber nicht auf Port-81.

Nachtrag/Nachfrage: Du beendest doch die Herstellersoftware auf dem PC, bevor Du den Zugriff mit dem Arduino probierst? Denn wenn die Herstellersoftware auf dem PC die Verbindung auf Port-81 ständig aufgebaut läßt, aber das Gerät nur eine TCP-Verbindung zur Zeit bedienen kann, wäre Port-81 auf dem Gerät immer dann blockiert, wenn bereits eine andere Verbindung auf Port-81 offen ist.

Hallo Jurs,

Nachtrag/Nachfrage: Du beendest doch die Herstellersoftware auf dem PC, bevor Du den Zugriff mit dem Arduino probierst? Denn wenn die Herstellersoftware auf dem PC die Verbindung auf Port-81 ständig aufgebaut läßt, aber das Gerät nur eine TCP-Verbindung zur Zeit bedienen kann, wäre Port-81 auf dem Gerät immer dann blockiert, wenn bereits eine andere Verbindung auf Port-81 offen ist.

Ja, die Herstellersoftware habe ich vorher beendet.
Ich habe aber inzwischen herausgefunden, dass der Arduino vom Server ein “connection establish request” anfordert (siehe Anhang). Das macht die Herstellersoftware nicht. Vermutlich bleibt die Quittierung deswegen aus. Ob man das in der Ethernet Lib ausschalten kann?

Viele Grüße

karl_napp:
Ich habe aber inzwischen herausgefunden, dass der Arduino vom Server ein “connection establish request” anfordert (siehe Anhang). Das macht die Herstellersoftware nicht.

TCP ohne vorher eine Verbindung zu aufzubauen?

Sorry, da bin ich überfragt. Solch ein Internetprotokoll kenne ich nicht.

Hello again,

ich habe geschafft eine Antwort vom Server zu bekommen. Man muss einfach nach client.connect das Request Datenpaket direkt hinterherschicken.

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
byte server[] = { 192,168,2,11 };    // Wechselrichter

// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192,168,2,20);

// Initialize the Ethernet client library
// with the IP address and port of the server 
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;

void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(9600);

  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    // try to congifure using IP address instead of DHCP:
    Ethernet.begin(mac, ip);
  }
  // give the Ethernet shield a second to initialize:
    delay(1000);
    
     client.connect(server, 81);
 
     byte buffer[] = { 0x62, 0xff, 0x03, 0xff, 0x00, 0x45, 0x58, 0x00 };
     client.write(buffer, sizeof(buffer));

     Serial.println("frage ab...");
}

void loop()
{
    byte c = client.read();
    Serial.println(c);
    delay(1000);
    
   /* if (!client.connected()) {
    Serial.println();
    Serial.println("trennen.");
    client.stop();

    // do nothing forevermore:
    while(true);
  } */
}

Als Antwort bekomme ich aber ständig nur 255. Das ist vermutlich das erste byte vom Response Package, das laut Doku 11 bytes lang ist. Wie muss man das programmieren um alle 11 bytes zu erhalten?

Viele Grüße
Karl Napp

karl_napp: Als Antwort bekomme ich aber ständig nur 255. Das ist vermutlich das erste byte vom Response Package, das laut Doku 11 bytes lang ist. Wie muss man das programmieren um alle 11 bytes zu erhalten?

Nein, 255 ist der Integerwert "-1" wenn Du einen leeren Eingangspuffer abfragst und aus einem leeren Puffer lesen möchtest (Errorwert), und diesen Integer-Wert auf ein "byte" typecastest.

Das bedeutet "kein Wert zum Lesen vorhanden".