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
bytemac[]={0x90,0xA2,0xDA,0x10,0x5C,0xFF};
5
IPAddressg_ip(192,168,0,180);
6
IPAddressg_gateway(192,168,0,1);
7
IPAddressg_sub(255,255,255,0);
8
charg_buf[255];
9
10
EthernetServercmdServer(port);
11
voidsetup(){
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
voidloop(){
19
staticintx=0;
20
staticcharc;
21
staticEthernetClientclient;
22
staticunsignedlongblnWait;
23
if(!client.connected()){
24
client=cmdServer.available();
25
if(client){
26
client.setConnectionTimeout(300);// set the timeout duration
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.
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".
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.
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?
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.
> 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.
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.
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.
portwalker schrieb:> Also Bullshit das Gequassel.foobar schrieb:> Wer bei TCP Pakete braucht, muß das auf Anwenderebene implementieren -> das Protokoll gibt das nicht her.
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
bytemac[]={0x90,0xA2,0xDA,0x10,0x5C,0xFF};
5
IPAddressg_ip(192,168,8,72);
6
IPAddressg_gateway(192,168,8,1);
7
IPAddressg_sub(255,255,255,0);
8
charg_buf[255];
9
10
EthernetServercmdServer(port);
11
voidsetup(){
12
//Watchdog stellen
13
cli();// Disable the Interrupts
14
WDTCSR=(1<<WDCE)|(1<<WDE);// Enable the WD Change Bit
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.
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.
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.
> 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
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.
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.
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.
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