Go Down

Topic: UDP Write anomaly (Read 902 times) previous topic - next topic

eddy9

Hello,

Ok, so I ran into another issue implementing UDP on the Arduino mega 2560. My program sends periodic messages over UDP to another device (to the PC or another Arduino Mega). The problem appears when initially the Arduino board tries to send messages periodically while nothing is connected to the Ethernet shield RJ connector. As soon as the receiving device is connected, the next time it send a message, it sends all of the previous messages that it tried to send before the receiving device was connected. So for example, it tries to send the string "hi" 3 times while nothing is connected to the RJ connector, the next time it sends the string "hi" when the receiving device is connected it sends "hihihihi" instead. It almost looks like every time it tries to send a packet, it appends it to the previous packet that was not delivered. I figured a way around this by checking Udp.endPacket() to see if it returns 0, then I would do Udp.stop() and then Udp.begin(). This appears to clear the write buffer.
Here are my questions:
Why does Udp behave as I described above?
What is the proper way to correct his behavior? Knowing that I really cannot guarantee that there will always be a device connected the Ethernet Shield to receive UDP packets at power up.
Is my solution to the problem appropriate, that is, is it going to cause some issues down the road since I am calling Udp.begin() in the main loop instead of Setup()?

Here is sample code that causes the problem

Code: [Select]

#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008

byte mac[] = {
 0x90, 0xA2, 0xDA, 0x0D, 0x50, 0x57}; //Ethernet shield MAC Address
IPAddress IP(169, 254, 0, 7);
IPAddress IP_Remote(169, 254, 0, 1);           // IP address for Remote Arduino
unsigned int localPort = 6101;                 // local port to listen on
unsigned int Port_Remote = 6180;        // Remote Arduino remote port


const int LEDPIN = 2;
volatile byte seconds = 0;
volatile byte count = 0;

// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
EthernetClient client;


char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,

void setup()
{
 pinMode(4,OUTPUT); // for the MEGA 2560 to disable SD


 pinMode(LEDPIN, OUTPUT);
 // initialize Timer1
 cli();          // disable global interrupts
 TCCR1A = 0;     // set entire TCCR1A register to 0
 TCCR1B = 0;     // same for TCCR1B

 // set compare match register to desired timer count: (for 1 sec use 15624)
 OCR1A = 46874; // set for 3 seconds but this is too long. prefere to use 1 second

 // turn on CTC mode:
 TCCR1B |= (1 << WGM12);

 // Set CS10 and CS12 bits for 1024 prescaler:
 TCCR1B |= (1 << CS10);
 TCCR1B |= (1 << CS12);

 // enable timer compare interrupt:
 TIMSK1 |= (1 << OCIE1A);

 // enable global interrupts:
 sei();


 // start the Ethernet and UDP:
 Ethernet.begin(mac,IP);
 Udp.begin(localPort);

 // start the Serial Port:
 Serial.begin(9600);

}

void loop()
{

 digitalWrite(4,HIGH); // for the MEGA 2560 to disable SD

 // if there is nothing connected to the Ehternet shield, then Udp.write() requests get buffered

 if ((count >=1 && seconds <=4) || (count >=1 && seconds >=7))
 {
   Serial.print("Send ping, ");
   Udp.beginPacket(IP_Remote, Port_Remote);
   Serial.print(" Number of Bytes: ");
   Serial.print(Udp.write("ping"));
   Serial.print(" ,Send successful? ");
   if (!Udp.endPacket())
   {
     // What are the implications of stopping Udp and then starting it back again in the main loop?
     // It appears that stopping Udp and starting it back again clears the write buffer

     //Udp.stop();                // stop EthernetUDP
     //Udp.begin(localPort);      // start EhternetUDP
     Serial.println(0);
   }
   else
   {
     Serial.println(1);
   }
   count = 0;
 }
 int packetSize = Udp.parsePacket();
 if(packetSize)
 {
   Serial.print("Rcvd packet size of: ");
   Serial.print(packetSize);
   Udp.read(packetBuffer,packetSize);
   packetBuffer[packetSize] = '\0';
   Serial.print(", Contents:");
   Serial.println(packetBuffer);
   if (String(packetBuffer) == "s")
   {
     Serial.print("Send ping, ");
     Udp.beginPacket(IP_Remote, Port_Remote);
     Serial.print(" Number of Bytes: ");
     Serial.print(Udp.write("ping"));
     Serial.print(" ,Send successful? ");
     Serial.println(Udp.endPacket());

   }
 }
 delay(50);
}

ISR(TIMER1_COMPA_vect)
{

 // Toggle Led to see code running
 digitalWrite(LEDPIN, !digitalRead(LEDPIN));
 seconds++;
 count++;
 if (seconds > 10)
 {
   cli();          // disable global interrupts
   TCCR1A = 0;     // set entire TCCR1A register to 0
   TCCR1B = 0;     // same for TCCR1B
   sei();
 }

}


Thanks

Cybernetician

#1
Jan 12, 2013, 06:02 am Last Edit: Jan 12, 2013, 06:04 am by Cybernetician Reason: 1
Why does Udp behave as I described above?

Answer: Udp is unreliable connection-less protocol. UDP sockets or Datagram sockets are different from the TCP sockets in a number of ways. The most important difference is that UDP sockets are not connection oriented. A UDP server does not have to listen for and accept client connections, and a UDP client does not have to connect to a server.

What is the proper way to correct his behavior? Knowing that I really cannot guarantee that there will always be a device connected the Ethernet Shield to receive UDP packets at power up.

Answer: Simple ask your Receiving Device are you connected if yes send me  "I am connected" just kidding 8). Means receiving Device acknowledged the sender by some mechanism on application layer.

Is my solution to the problem appropriate, that is, is it going to cause some issues down the road since I am calling Udp.begin() in the main loop instead of Setup()?

Answer: NO(Actually i don't understand what you want to say in last question).
From Idea To Invention

PeterH


Why does Udp behave as I described above?


It's not conventional UDP behaviour, but comments on another thread make me suspect that the shield is trying to resolve the outgoing address at the start of the packet, and your experience suggests it is buffering the outgoing data until it can transmit it. Neither of these strike me as desirable things to do, but it seems to be implied by the behaviour you're seeing. I would be interested to know whether packets that you sent while the recipient was offline are sent spontaneously when the recipient comes back online, or are only sent when you try to send a subsequent packet to the same recipient.


What is the proper way to correct his behavior? Knowing that I really cannot guarantee that there will always be a device connected the Ethernet Shield to receive UDP packets at power up.


Your workaround seems reasonable to me. I would not like to have the shield buffering and then delivering historical packets after some unknown and uncontrolled delay. Presumably the shields buffer capacity is finite and there's no telling what would happen once the buffer is full.


Is my solution to the problem appropriate, that is, is it going to cause some issues down the road since I am calling Udp.begin() in the main loop instead of Setup()?


My only reservation is that you are triggering it based on the return value from Udp.endPacket(), but the current documentation for the Ethernet library does not document a return value. If the library implementation you're using does actually provide a return value and it indicates that the packet was transmitted, you should be good to go. Does Udp.beginPacket() have a return value in your implementation?
I only provide help via the forum - please do not contact me for private consultancy.

eddy9

Quote
I would be interested to know whether packets that you sent while the recipient was offline are sent spontaneously when the recipient comes back online, or are only sent when you try to send a subsequent packet to the same recipient.



I tested this and found that it only sends the buffered packets the next time it sends a subsequent packet to the same recipient. and also noticed that it would send all "undelivered" packets the next time it tries to send something whether some packets were destined for the receiving device or for other devices (with different IP addresses and port numbers). so if I try to send "Hi" to device 1 and "Hello" to device 2 and neither one is online, when one of the receiving devices comes online and it tries to send a new packet to it containing "What", it would send "HiHelloWhat". This behavior does not seem to change if the receiving devices comes online and sends a packet back first before it receives a subsequent packet.

Quote
My only reservation is that you are triggering it based on the return value from Udp.endPacket(), but the current documentation for the Ethernet library does not document a return value. If the library implementation you're using does actually provide a return value and it indicates that the packet was transmitted, you should be good to go. Does Udp.beginPacket() have a return value in your implementation?


Yes it has a return value even though Arduino website states that there is no return value. I am using the same implementation that was provided with Arduino 1.0.2 version of the IDE available on the website

billroy

You have got to ask yourself how it knows not to send those packets when the destination isn't there.  I'm guessing that ARP for the destination IP fails, since there is no gateway in the configuration, in other words the connection is direct.

One way to test this hypothesis would be to change the sketch to configure a default gateway to route the IP traffic through -- there must be a router on the network, yes?  Packets intended for the other arduino would reliably get to the gateway, unburdening the sending arduino.  Then they would be dropped by the router if it can't deliver them, which I'm guessing is better than the present behavior, and maybe even what you want.

Or I could be barking up the wrong tree entirely.  But it's an easy test.

-br





PeterH


if I try to send "Hi" to device 1 and "Hello" to device 2 and neither one is online, when one of the receiving devices comes online and it tries to send a new packet to it containing "What", it would send "HiHelloWhat".


You're saying that if you submit a packet to one device when it is offline, the shield will subsequently send that packet to a completely different device? That sounds horrendous, and I'd want to look for ways your experiment may have produced misleading results. Is each packet in that test definitely submitted to the shield by the normal sequence of begin packet, write data, end packet?

If you have time to test it with Wireshark or equivalent, I'd be interested to know whether the shield is sending one large UDP datagram containing all the buffered data (from all immediately preceding failed send attempts) or is sending each of the buffered packets in a separate datagram.
I only provide help via the forum - please do not contact me for private consultancy.

eddy9

#6
Jan 15, 2013, 03:52 pm Last Edit: Jan 15, 2013, 03:54 pm by eddy9 Reason: 1
Quote
You're saying that if you submit a packet to one device when it is offline, the shield will subsequently send that packet to a completely different device? That sounds horrendous


I think what is happening is that the shield does not clear the write buffer when a packet is not delivered. So every subsequent undelivered packet gets appended to the buffer (rather than overwrite it). When the next packet is delivered successfully, the shield would send all of the contents of the buffer and then clear it to get ready for the next packet. I haven't looked at the Ethernet libraries to verify this but it sounds a reasonable explanation. Any comments?

PaulS

Quote
I haven't looked at the Ethernet libraries to verify this but it sounds a reasonable explanation.

It does, but, why doesn't beginPacket() clear that crap out? It appears as though there is a separate buffer for each node being communicated with.

PeterH


It appears as though there is a separate buffer for each node being communicated with.


Even worse, if it have understood the symptoms correctly, it appears that there is a common buffer shared between all nodes. I can't think of any scenario in which a datagram correctly submitted for transmission to one node should reasonably be sent to a different node.
I only provide help via the forum - please do not contact me for private consultancy.

Go Up