UDP packets to multiple IPs

I'm trying to send UDP packets to multiple IP addresses from a single Arduino Uno with an Ethernet shield.

Sending to a single address works great, I see about 0 - 2ms between received messages (which I'm assuming is network latency).

Calling UDP.beginPacket/endPacket twice in the loop, however, gives me ~400ms between received messages. Occasionally it will speed up to ~5ms for a second or two, then drop back to 400ms.

The code I'm pasting uses the OSC library, but I get the same results without OSC, using UDP.write.

I'm having a hard time understanding what might be going on here...any insight is appreciated!

#include <OSCMessage.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SPI.h>    
#include <OSCMessage.h>
EthernetUDP Udp;

//the Arduino's IP
IPAddress ip(192, 168, 1, 9);

//destination IPs
IPAddress ip1(192, 168, 1, 2);
IPAddress ip2(192, 168, 1, 17);
const unsigned int outPort = 9999;

 byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE };
  
void setup() {
  Ethernet.begin(mac,ip);
  Udp.begin(8888);
}

void loop(){
  
    OSCMessage msg("/analog/0");
    msg.add((int32_t)analogRead(0));
    
    Udp.beginPacket(ip1, outPort);
    msg.send(Udp);
    Udp.endPacket();
 
    Udp.beginPacket(ip2, outPort);
    msg.send(Udp);
    Udp.endPacket();
    msg.empty();
}

I haven't tried sending to two devices using UDP. Can you tell in your code where the 400ms delay is happening?

I recommend starting the serial port at a high baud rate, like 115200, and sending a character to determine where the program is. I used the numbers 1 and 2 followed by an asterisk to determine where the delay is happening. A 400ms delay should be pretty obvious.

void loop(){
 
    OSCMessage msg("/analog/0");
    msg.add((int32_t)analogRead(0));
   
    Serial.print("1");
    Udp.beginPacket(ip1, outPort);
    msg.send(Udp);
    Serial.print("*");
    Udp.endPacket();
    Serial.print("*");
 
    Serial.print("2");
    Udp.beginPacket(ip2, outPort);
    msg.send(Udp);
    Serial.print("*");
    Udp.endPacket();
    Serial.println("*");
    msg.empty();
}

You should get something like this as output. Where is the delay between characters?
12
12

edit: What are the two receiving devices? Arduinos or PCs?

Right now the two receiving devices are PCs, although the receivers ultimately will include Raspberry Pis & other Arduinos.

I couldn't tell much from just printing characters, so I added timers after every event. Here's what it looks like sending to just one IP:

begin loop: 0
after msg: 0
after beginPacket1: 1
after send1: 2
after endPacket1: 1
after msg.empty: 1
begin loop: 1
after msg: 1
after beginPacket1: 1
after send1: 2
after endPacket1: 1
after msg.empty: 1

etc.

Here's what it looks like when sending to two IPs:

begin loop: 0
after msg: 1
after beginPacket1: 0
after send1: 2
after endPacket1: 34
after beginPacket2: 0
after send2: 2
after endPacket2: 202
after msg.empty: 0
begin loop: 0
after msg: 0
after beginPacket1: 1
after send1: 2
after endPacket1: 200
after beginPacket2: 0
after send2: 2
after endPacket2: 202
after msg.empty: 1
begin loop: 0
after msg: 0
after beginPacket1: 1
after send1: 1
after endPacket1: 200
after beginPacket2: 0
after send2: 2
after endPacket2: 201
after msg.empty: 0

etc.

So it appears that adding a second IP results in Udp.endPacket() taking 200ms to execute each time it's called.

Here's the sketch I'm running now:

#include <OSCMessage.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SPI.h>    
#include <OSCMessage.h>
EthernetUDP Udp;

//the Arduino's IP
IPAddress ip(192, 168, 1, 9);

//destination IPs
IPAddress ip1(192, 168, 1, 2);
IPAddress ip2(192, 168, 1, 17);
const unsigned int outPort = 9999;
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE };
unsigned long time, start;

  
void setup() {
  Ethernet.begin(mac,ip);
  Udp.begin(8888);
  Serial.begin(115200);
}

void loop(){
    start = millis();
    Serial.print("begin loop: ");
    time = millis() - start;
    Serial.println(time);
    start = millis();
    
    OSCMessage msg("/analog/0");
    msg.add((int32_t)analogRead(0));
    Serial.print("after msg: ");
    time = millis() - start;
    Serial.println(time);
    start = millis();
    
    Udp.beginPacket(ip1, outPort);
    Serial.print("after beginPacket1: ");
    time = millis() - start;
    Serial.println(time);
    start = millis();    
    
    msg.send(Udp);
    Serial.print("after send1: ");
    time = millis() - start;
    Serial.println(time);
    start = millis();    
    
    Udp.endPacket();
    Serial.print("after endPacket1: ");
    time = millis() - start;
    Serial.println(time);
    start = millis();

    Udp.beginPacket(ip2, outPort);
    Serial.print("after beginPacket2: ");
    time = millis() - start;
    Serial.println(time);
    start = millis();    
    
    msg.send(Udp);
    Serial.print("after send2: ");
    time = millis() - start;
    Serial.println(time);
    start = millis();    
    
    Udp.endPacket();
    Serial.print("after endPacket2: ");
    time = millis() - start;
    Serial.println(time);
    start = millis();

    msg.empty();
    Serial.print("after msg.empty: ");
    time = millis() - start;
    Serial.println(time);
}

Also, sending two packets per loop (as in the above code) but using the same IP for each doesn't result in any delay, it's only when the two packets are sending to different IPs that the 200ms delay appears after Udp.endPacket()...

Then it is the sendUdp function in /utility/socket.cpp that is having trouble. The Udp.endPacket() function sends the packet by calling sendUdp, and it appears to be taking 200ms to do it if sending to different IPs.

If that is the case, and you have the sockets to spare, I would start 2 UDP sockets, and use one socket to send to ip1 and the other to send to ip2. That is if you are not using the sockets for anything else.

Hey this works great for me.
I wonder if you have experienced a similar problem that I'm coming across which is when loosing connection to one of the devices the whole thing stops running.
From reading a few other posts I'm led to understand that Udp.beginPacket() attempts to send a packet 3 times...

I'm running a Primary and Backup kind of scenario where I always want to be attempting to send OSC messages to both devices so shutting down the send command if it failed really isn't an option.

I don't suppose you've come across this and found a work around?

It may try more times than that.

You might want to try setting the retransmission count on the w5100. The default value is 8.

#include <utility/w5100.h>

   // in setup() after Ethernet.begin(), call this function.
   W5100.setRetransmissionCount(1);

There will still be a 200ms delay if the packet is not accepted by the next device en route to the destination.

Hey Surfer Tim,

Thanks so much for your reply.
I'm not using an Ethernet Shield, currently using a Arduino Ethernet Leonardo (Arduino - Home) I know I would much rather have got something from Arduino.cc but my work colleague got it me so at the moment just working with what I've got.

Would there be a way of setting the retransmission settings for one of these?

You should be able to change it on the w5500 also. I have not tried it tho.

#include <utility/w5500.h>

   // in setup() after Ethernet.begin(), call this function.
   w5500.setRetransmissionCount(1);

Well I think that certainly helped, but I'm still experiencing a lag on the loop when a device is no longer connected.

I usually don't recommend this, but you might want to play with the retransmission time. The parameter is in 100us intervals. The default is 2000 (200ms). Try this first. It cuts the timeout value in half. See if that helps you. Call this function after the setRetransmissionCount call.

  w5500.setRetransmissionTime(1000);

This worked a treat. Thanks Surfer Tim