UDP broadcast to multiple lights - how to handle confirmation without introducing delay

Some of this was previously discussed in Sending LAN packet using Arduino, but was never really answered, so here goes:

The hardware:
Node ESP32 development board, running standard Arduino libs

The intention:
One trigger event performs an action on multiple lights (ie: a group) instantly. If any action fails, it needs to be retried.

The problem:
Using UDP, requiring a response (or an ack) introduces a noticeable delay in the subsequent requests as each request waits for a response before timeout.
Not requiring a response can end up with lights in the wrong state due to a dropped packet.

I’ve got a couple of ideas for solutions, but just wondering if @delfick-employee or @daniel_hall have any guidance on a “best practice” here?

My current thoughts on options are:

  1. Never require a response, but use a GETSTATE call after the fact to ensure the change has taken place
  2. Refactor the code handling the response to make it non-blocking
  3. Investigate Async UDP calls

NB: Currently I’m not worrying about the lights’ IPs and just sending broadcasts using the header.target to match the correct mac address. Is there any foreseeable problem with this approach? Would specifying an IP result in a reduced delay, or greater chance of success?


Broadcast should work fine as long as your network isn’t really busy. You can use wireshark to confirm the broadcast packets are being received by your devices.

Also, I would set ack_required to True and res_required to false. Send out all the messages and then determine which messages need to be resent based on what acks you don’t get.

Unfortunately I’m unfamiliar with arduino and micropython so I don’t really have any tips on achieving that.

1 Like

Thanks. I’m actually no longer using uPython for this, back on the standard C implementation (which is a learning curve).

The issue is that waiting and listening for the ack is a blocking operation (no async UDP in its current form), so I’d like to keep it down to a few hundred microseconds. Roughly how quickly should it be received, assuming a reasonably good network?

On a perfect network you should get a reply within 10ms minimum.

My photons framework is setup so first retry is after 200ms and that seems to be a reasonable number.

(Note that this number is very unscientific and just comes from sometimes having debug logs and seeing how often I get retries)

Another option is to send the packet multiple times, ignore the response and hope for the best. This approach seems to be used on the “rapid” color change method in the popular “lifxlan” Python module.

Snippet from https://github.com/mclarkk/lifxlan/blob/master/lifxlan/light.py:

    def set_color(self, color, duration=0, rapid=False):
        if len(color) == 4:
                if rapid:
                    self.fire_and_forget(LightSetColor, {"color": color, "duration": duration}, num_repeats=1)
                    self.req_with_ack(LightSetColor, {"color": color, "duration": duration})
            except WorkflowException as e:

The fire_and_forget implementation you can find in https://github.com/mclarkk/lifxlan/blob/master/lifxlan/device.py from line ~456 onwards.

P.S. If you get this working, it’d be great if you’d be willing to share it. I’ve also been planning an ESP32 light controller but haven’t had time to do anything yet and don’t know when will I do. Some base code to start working with would greatly improve the chance of getting something done. :smile:

Absolutely I’ll be sharing it. Thanks for the tip. I’ve taken a few looks at @mclark’s excellent library in the past.

I’m leaning towards optimistically set (aka fire_and_forget, maybe two or three times), then use getState to check, but need to play around with it a bit more first.