Forum: Mikrocontroller und Digitale Elektronik RS232 zu Ethernet Gateway mit Arduino


von Manfred S. (Firma: Manfred) (xfred343)


Lesenswert?

Ich stehe hier vor einem komischen Problem. Irgendwie dürfte die 
serielle Schnittstelle sich beim Arduino gemeinsam mit Ethernet 
eigenartig verhalten.
Es sollen Daten auf Port 23 (Telnet) ausgegeben werden, allerdings ein 
serieller Befehl mit einem TCP/IP-Paket.

Ohne dem Trick mit millis() sendet der Arduino ein Paket  mit dem ersten 
Buchstaben und teilt das TCP/IP-Paket auch sonst in mehrere Pakete auf.
Selbst ein Delay hilft da nicht weiter.
1
    if (x>0){
2
       delay(50); //geht trotz Warten nicht
3
       cmdServer.write(g_buf,x);
4
       x=0;
5
    }

Mit dem millis() gehts hingegen
1
    if (x>0 && millis()>blnWait){
2
       cmdServer.write(g_buf,x);
3
       x=0;
4
    }

Mit wireshark sieht man exakt 1 Paket.
Hat irgendwer eine Idee, warum das so ist?
1
#include <SPI.h>
2
#include <Ethernet.h>
3
#define port 23 // Port Telnet
4
byte mac[] = {0x90, 0xA2, 0xDA, 0x10, 0x5C, 0xFF};
5
IPAddress g_ip(192, 168, 0, 180);
6
IPAddress g_gateway(192, 168, 0, 1);
7
IPAddress g_sub(255, 255, 255, 0);
8
char g_buf[255];
9
10
EthernetServer cmdServer(port);
11
void setup() {
12
  Ethernet.begin(mac,g_ip,g_gateway,g_sub);
13
  Ethernet.setRetransmissionCount(1);  //total number of transmission attempts 
14
  Ethernet.setRetransmissionTimeout(500);  // set the Ethernet controller's timeout to 500 ms
15
  cmdServer.begin();
16
  Serial.begin(9600);
17
}
18
void loop() {
19
  static int x=0;
20
  static char c;
21
  static EthernetClient client;
22
  static unsigned long blnWait;
23
  if (!client.connected()){
24
    client = cmdServer.available();
25
    if (client){
26
      client.setConnectionTimeout(300);  // set the timeout duration 
27
    }
28
  }
29
  else{
30
    if (Serial.available()){
31
      blnWait=millis()+50;
32
      if (x<255)
33
         g_buf[x++]=Serial.read();
34
    }    
35
    if (x>0 && millis()>blnWait){
36
       cmdServer.write(g_buf,x);
37
       x=0;
38
    }
39
  }
40
}

von Patrick (Gast)


Lesenswert?

Kann es sein, dass client.connected() auch Werte ungleich null annimmt, 
die sowas wie "Verbindungsaufbau aktiv" aussagen, die du statt 
"!client.connected() auswerten musst? Kannst dir den Wert ja mal auf die 
serielle Schnittstelle spucken lassen.

von Patrick (Gast)


Lesenswert?

Quatsch, der wird einfach noch nicht fertig sein mit dem serialread - 
sobald er ein Zeichen empfangen hat, schickt er das los - dein millis() 
überbrückt sozusagen die Zeit des seriellen Empfangs aller Zeichen des 
Befehls, bevor per TCP/IP verschickt wird. Könntest du so lassen, oder 
du bildest dir ein anderes Kriterium für "Empfang abgeschlossen".

von jo mei (Gast)


Lesenswert?

Manfred S. schrieb:
> Es sollen Daten auf Port 23 (Telnet) ausgegeben werden, allerdings ein
> serieller Befehl mit einem TCP/IP-Paket.

Habe die Problemstellung nicht wirklich verstanden aber hier
meine Gedanken zu deinem potenteillen Problem:

TCP und "ein Paket" ist ein Widerspruch in sich da man die
Einheit eines Paketes das man zu Sendung beauftragt auf der
TCP-Ebene nie garantiert bekommt dass man es unzerstückelt
(besser: unfragmentiert) beim Emfänger erhält.

TCP ist ein Stream bei dem man garantiert bekommt (Fehler-
kontrolle) dass Daten korrekt übertragen werden. Für die
kontrolle der Zerstückelung (Fragmentierung) und der
erforderliche "Wiederzusammenbau" ist per User-Protokoll
der User selbst verantwortlich.

von Manfred S. (Firma: Manfred) (xfred343)


Lesenswert?

Bei dem Stream magst du Recht haben, aber einzelne Pakete - noch dazu 
bis maximal 255 Byte - werden dabei nicht in Teilpakete zerlegt. Die 
werden über ein fehlerfreies Verfahren (am Ende ein ACK) schon 
durchgereicht.

Auf der anderen Seite (PC) ist mein Programm, das über das 
winsock-Steuerelement dann die Befehle entgegen nimmt, das geht z.B. mit 
den Modbus-Komponenten von Beckhoff ganz problemlos - hier kommt auch 
immer nur 1 Paket und ich mag nicht extra für den Arduino meine 
winsock-Klasse umbauen.

Aber vielleicht gibts ja eine Möglichkeit statt dem millis() gleich 
direkt rauszubekommen, ob Serialread() abgeschlossen ist?

von jo mei (Gast)


Lesenswert?

Manfred S. schrieb:
> Aber vielleicht gibts ja eine Möglichkeit statt dem millis() gleich
> direkt rauszubekommen, ob Serialread() abgeschlossen ist?

Ja, indem du das Steuerzeichen auswertest das du als Abschluss
deines Komandos bzw Datenpakets erwartest. Typischer Fall wäre
CR/LF bei Text. Oder sehe ich da etwas zu einfach? Ja, das
eigene Protokoll bleibt auf jeden Fall in User-Verantwortung.

von foobar (Gast)


Lesenswert?

> Bei dem Stream magst du Recht haben, aber einzelne Pakete - noch dazu
> bis maximal 255 Byte - werden dabei nicht in Teilpakete zerlegt. Die
> werden über ein fehlerfreies Verfahren (am Ende ein ACK) schon
> durchgereicht.

Na, dann setzt mal nen Router mit ner kleinen MTU (z.B. 96) zwischen die 
beiden Rechner - der Empfänger bekommt die Daten dann in schönen kleinen 
40- bis 50-Byte-Blöcken.  Aus einem 200-Byte-write werden dann z.B. vier 
50-Byte-reads.  Ist ein oft zu sehender Fehler in Anwendungen, dass sie 
davon ausgehen, dass das gesamte "Paket" oder die "Befehlszeile" mit 
einem read angeliefert wird - im LAN klappt das meist, im WAN geht's 
schnell schief.

Wer bei TCP Pakete braucht, muß das auf Anwenderebene implementieren - 
das Protokoll gibt das nicht her.

von portwalker (Gast)


Lesenswert?

foobar schrieb:
> Wer bei TCP Pakete braucht, muß das auf Anwenderebene implementieren -
> das Protokoll gibt das nicht her.

Hier ist von TCP/ IP die Rede und nicht nur von TCP.

Da muss sich kein “ Userlayer” um irgendwas kümmern!

Also Bullshit das Gequassel.

von jo mei (Gast)


Lesenswert?

portwalker schrieb:
> Hier ist von TCP/ IP die Rede und nicht nur von TCP.

Kann ich aus den Infos die der TO liefert nicht erkennen.

Einfach mal "TCP/IP" sprechen und nichts dazu liefern, dann
muss ich mal TCP annehmen. So sieht sein Sourcen-Fragment
auch aus. Lasse mich aber gerne belehren.

von jo mei (Gast)


Lesenswert?

portwalker schrieb:
> Also Bullshit das Gequassel.

foobar schrieb:
> Wer bei TCP Pakete braucht, muß das auf Anwenderebene implementieren -
> das Protokoll gibt das nicht her.

von Manfred S. (Firma: Manfred) (xfred343)


Lesenswert?

Danke für die durchaus interessanten RÜckmeldungen, als Belohnung gibts 
mein jetzt fertiges RS232 zu TCP/IP Gateway - vielleicht kanns ja jemand 
brauchen.

Es ist schon im Einsatz und zwar an der Servicestelle eines Fröling 
Pelletskessels H3200, funktioniert alles problemlos.

Der Vorteil gegenueber einer Serial2TCPIP Loesung in Github ist, dass 
meine die Antworten puffert und nicht jedes Zeichen gleich auf den Weg 
schickt..

Serial1 ist übrigens für den Atmega (Arduino Mega 2560) bestimmt und 
dort auf PIN18(RX) und PIN19(TX)
1
#include <SPI.h>
2
#include <Ethernet.h>
3
#define port 1600 // Port
4
byte mac[] = {0x90, 0xA2, 0xDA, 0x10, 0x5C, 0xFF};
5
IPAddress g_ip(192, 168, 8, 72);
6
IPAddress g_gateway(192, 168, 8, 1);
7
IPAddress g_sub(255, 255, 255, 0);
8
char g_buf[255];
9
10
EthernetServer cmdServer(port);
11
void setup() {
12
  //Watchdog stellen
13
  cli();                            // Disable the Interrupts
14
  WDTCSR = (1 << WDCE) | (1 << WDE); // Enable the WD Change Bit
15
  WDTCSR = (1 << WDE) | (1 << WDP3) | (1 << WDP0); //  Enable WDT Interrupt, 8 sec (WDP3=1, WDP0=1)
16
  sei();
17
  Ethernet.begin(mac,g_ip,g_gateway,g_sub);
18
  Ethernet.setRetransmissionCount(1);  //total number of transmission attempts 
19
  Ethernet.setRetransmissionTimeout(500);  // set the Ethernet controller's timeout to 500 ms
20
  cmdServer.begin();
21
  Serial1.begin(57600);
22
}
23
void loop() {
24
  static uint8_t x=0;
25
  static char c;
26
  static EthernetClient client;
27
  static unsigned long blnWait;
28
  __asm__("wdr\n\t");   //Watchdog-Reset  
29
  if (!client.connected()){
30
    client = cmdServer.available();
31
    if (client){
32
      client.setConnectionTimeout(300);  // set the timeout connect/stop
33
    }
34
  }
35
  else{
36
    if (client.available()){
37
        c = client.read();
38
        Serial1.write(c);
39
    }
40
    if (Serial1.available()){
41
      blnWait=millis()+50;
42
      if (x<255)
43
         g_buf[x++]=Serial1.read();
44
      else
45
        Serial1.read(); //restliche Zeichen überlesen
46
    }    
47
    if (x>0 && millis()>blnWait){
48
       client.write(g_buf,x);
49
       delay(10);
50
       x=0;
51
    }
52
  }
53
}

: Bearbeitet durch User
von jo mei (Gast)


Lesenswert?

Manfred S. schrieb:
> als Belohnung gibts
> mein jetzt fertiges RS232 zu TCP/IP Gateway - vielleicht kanns ja jemand
> brauchen.

Ich betrachte dies eher als Strafe als als Lösung. Das ist für
jeden anderen Anwender unbrauchbar.

Eine Datenübertragung die auf (WD-)Reset und Daten-Timeouts und
Puffer-Überlauf als Kern des Protokolls baut .....   danke.

von Johannes S. (Gast)


Lesenswert?

Manfred S. schrieb:
> Der Vorteil gegenueber einer Serial2TCPIP Loesung in Github ist, dass
> meine die Antworten puffert und nicht jedes Zeichen gleich auf den Weg
> schickt..

das ist bei TCP eigentlich per default so und wird durch den Nagle 
Algorithmus gemacht, da werden kleine Häppchen bis 200 ms gesammelt und 
erst dann geschickt.
Wenn man das nicht möchte kann man das per Socketoption TCP_NODELAY 
abschalten, das wird vermutlich in deinem EthernetClient gesetzt sein.

von jo mei (Gast)


Lesenswert?

Johannes S. schrieb:
> Wenn man das nicht möchte kann man das per Socketoption TCP_NODELAY
> abschalten

Sein Ethernet Client ist ein Arduino mit Ethernet Shield und das
ganze mit der Arduino Ethernet Klasse gesteuert.

Da gibt es keinen Nagle! Jedenfalls keinen beeinflussbaren. Und
Socketoptionen schon gleich gar nicht.

Auf dem Arduino läuft nämlich weder Windows noch Linux. Auch
nicht auf dem Arduino-Ethernet-Controller.

von Manfred S. (Firma: Manfred) (xfred343)


Lesenswert?

> Ich betrachte dies eher als Strafe als als Lösung. Das ist für
> jeden anderen Anwender unbrauchbar.
>
Wo bleibt dann deine geniale Lösung?
Bitte her damit

von jo mei (Gast)


Lesenswert?

Manfred S. schrieb:
> Wo bleibt dann deine geniale Lösung?

Soweit kommt's noch. Ich soll dir ein Protokoll schreiben von
dem du selbst nicht weisst was es tun soll.

Die Spezifikation deiner Aufgabenstellung lautet: machen sie mal.
Auf diese Art ist wohl der neue Berliner Flughafen entstanden.

von c-hater (Gast)


Lesenswert?

jo mei schrieb:

> TCP ist ein Stream bei dem man garantiert bekommt (Fehler-
> kontrolle) dass Daten korrekt übertragen werden.

Und darüber hinaus: in der richtigen Reihenfolge!

> Für die
> kontrolle der Zerstückelung (Fragmentierung) und der
> erforderliche "Wiederzusammenbau" ist per User-Protokoll
> der User selbst verantwortlich.

Was komplett überflüssig ist, wenn die Daten wirklich als Stream benutzt 
werden und nicht irgendeine Paketierung erwartet wird.

Das dürfte der Grundfehler (des Empfängers!!!) sein.

Eine COM-TCP-Bridge braucht sich um so'n Scheiß garnicht zu kümmern. Es 
ist schließlich sowieso hoffnungslos, einen TCP-Stream, der (zumindest 
theoretisch) über unzählige Zwischenstationen mit vollkommen unbekannter 
möglicher maximaler Paketgröße laufen kann, irgendwie sinnvoll bezüglich 
der Paketgröße steuern zu wollen. Das gibt das Protokoll schlicht nicht 
her.

von Stefan F. (Gast)


Lesenswert?

Mir ist noch ein anderer Neben-Schauplatz aufgefallen:

Manfred S. schrieb:
> blnWait=millis()+50;
> if (millis()>blnWait)

Ich glaube das funktioniert beim Timerüberlauf nicht wie erwartet. 
Schreibe besser

> start=millis();
> if (millis()-start>50)

Der Trick liegt in der Subtraktion, die liefert trotz Timer-Überlauf 
noch das richtige Ergebnis.

von Manfred S. (Firma: Manfred) (xfred343)


Lesenswert?

Stefan ⛄ F. schrieb:
> Mir ist noch ein anderer Neben-Schauplatz aufgefallen:
>
> Manfred S. schrieb:
>> blnWait=millis()+50;
>> if (millis()>blnWait)
>
> Ich glaube das funktioniert beim Timerüberlauf nicht wie erwartet.
> Schreibe besser
>
>> start=millis();
>> if (millis()-start>50)
>
> Der Trick liegt in der Subtraktion, die liefert trotz Timer-Überlauf
> noch das richtige Ergebnis.

Ja, stimmt genau, das hatte ich schon wieder vergessen, das ist auch in 
jenem Moment richtig wo der Timerüberlauf passiert.. bei der Gelegenheit 
werd ich die Variable gleich in lngWait umbenennen Danke

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.