WifiNINA: client connection breaks UDP? (on Nano 33 IoT)

Hello all! (apologies if this is the wrong area!)

I am using a Nano 33 IoT to build a clock that syncs to NTP, but also hosts a small webpage for configuration, both via the WifiNINA library (and derived from its demos).

It is successfully connecting to WiFi, sending and receiving NTP UDP packets, and starting up the server. The trouble I’m having is, as soon as a client has connected to the server, the UDP instance stops sending (or receiving?) the NTP packets.

Here is a testing sketch that I’ve reduced to just the wifi/UDP/server/client code (it’s also on Github here):

#include <SPI.h>
#include <WiFiNINA.h>
#include <WiFiUdp.h>

#include "secrets.h" //supply your own
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)

unsigned int localPort = 2395; // local port to listen for UDP packets
IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP

WiFiServer server(80);


void setup() {
  Serial.begin(9600);
  while(!Serial); //only works on 33 IOT
  startWiFi();
}

unsigned long millisLast = 0;
void loop() {
  //Every 5 seconds, make a UDP call to NTP
  if(millis()-millisLast > 5000) { millisLast+=5000; startNTP(); }
  //Every loop, check for a UDP response and a client connection
  checkForNTPResponse();
  checkForServerClients();
}


void startWiFi(){
  if(WiFi.status()==WL_NO_MODULE) Serial.println("Communication with WiFi module failed!");
  else if(WiFi.firmwareVersion()<WIFI_FIRMWARE_LATEST_VERSION) Serial.println("Please upgrade the firmware");
  else { //hardware is ok
    Serial.print(F("Attempting to connect to SSID: ")); Serial.println(ssid);
    WiFi.begin(ssid, pass); //hangs while connecting
    if(WiFi.status()==WL_CONNECTED){ //did it work?
      Serial.println("Connected!");
      Serial.print("SSID: "); Serial.println(WiFi.SSID());
      Serial.print("IP address: "); Serial.println(WiFi.localIP());
      Serial.print("Signal strength (RSSI):"); Serial.print(WiFi.RSSI()); Serial.println(" dBm");
      server.begin();
    } else { //it didn't work
      Serial.println("Wasn't able to connect.");
    } //end it didn't work
  } //end hardware is ok
} //end fn startWiFi

bool ntpStartLast = 0;
void startNTP(){ //Called at intervals to check for ntp time
  if(WiFi.status()!=WL_CONNECTED) return;
  if(ntpStartLast!=0 && millis()-ntpStartLast < 3000) return; //do not send more than one request within 3 seconds
  Serial.println();
  ntpStartLast = millis();
  Udp.flush(); //does this do any good? ...no
  Udp.stop(); //does this do any good? ...no
  Serial.print(millis()); Serial.println(" Sending UDP packet to NTP server.");
  Serial.print("Udp.begin: "); Serial.println(Udp.begin(localPort)); //open connection
  memset(packetBuffer, 0, NTP_PACKET_SIZE); // set all bytes in the buffer to 0
  // Initialize values needed to form NTP request
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  Serial.print("Udp.beginPacket: "); Serial.println(Udp.beginPacket(timeServer, 123)); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Serial.print("Udp.endPacket: "); Serial.println(Udp.endPacket());
} //end fn startNTP

void checkForNTPResponse(){ //Called on every cycle to see if there is an ntp response to handle
  if(!ntpStartLast) return;
  if(!Udp.parsePacket()) return;
  // We've received a packet, read the data from it
  Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
  unsigned long ntpTime = (packetBuffer[40] << 24) | (packetBuffer[41] << 16) | (packetBuffer[42] << 8) | packetBuffer[43];
  Serial.print(millis()); Serial.print(" Received UDP packet from NTP server. Time is "); Serial.println(ntpTime);
  Udp.stop();
  ntpStartLast = 0;
} //end fn checkNTP


void checkForServerClients(){
  if(WiFi.status()!=WL_CONNECTED) return;
  WiFiClient client = server.available();
  if(client){
    Serial.println(); Serial.print(millis()); Serial.println(" Got client.");
    client.flush(); //does this do any good?
    client.stop();
  }
}

It generates serial output like this, showing two successful NTP syncs, my client connection, and three failed syncs after that:

Attempting to connect to SSID: Riley
Connected!
SSID: Riley
IP address: 192.168.1.66
Signal strength (RSSI):-12 dBm

8138 Sending UDP packet to NTP server.
Udp.begin: 1
Udp.beginPacket: 1
Udp.endPacket: 1
8219 Received UDP packet from NTP server. Time is 3792111355

10001 Sending UDP packet to NTP server.
Udp.begin: 1
Udp.beginPacket: 1
Udp.endPacket: 1
10344 Received UDP packet from NTP server. Time is 3792111357

12289 Got client.

12300 Got client.

12308 Got client.

15001 Sending UDP packet to NTP server.
Udp.begin: 1
Udp.beginPacket: 1
Udp.endPacket: 1

20001 Sending UDP packet to NTP server.
Udp.begin: 1
Udp.beginPacket: 1
Udp.endPacket: 0

25001 Sending UDP packet to NTP server.
Udp.begin: 1
Udp.beginPacket: 1
Udp.endPacket: 0

After the client connection, endPacket is successful once, but it looks like parsePacket never sees a response, and endPacket fails thereafter.

I’d be fine with UDP not working while the server/client stuff is active, but I don’t see a way to stop/restart the server, or a way to reset the UDP instance. I tried adding flush() and stop() (lines 58–59) before starting a new packet, to try and ensure any previous UDP connection is terminated first, but that didn’t help.

I checked the firmware version and that seems ok (1.2.3 PASSED). Next I’m thinking of trying to use Wireshark to see whether the request packets are being sent at all, after the client connection.

Thank you in advance for any ideas anyone may have!

For anyone who stumbles across this in future – I think I found a solution.

Previously I was calling Udp.begin() and Udp.stop() for every packet sent – but if I begin() it only once and never stop() it, everything seems to work great – even across multiple WiFi and AP starts and stops.

Here is the updated testing sketch, which adds support for starting/stopping WiFi/AP via serial commands. It generates output like this, showing that UDP packets are sent/received on WiFi even after clients connect to the server:

Please enter 'a' for AP, 'w' for WiFi, 'd' to disconnect.

25101 Creating access point
SSID: Clock
Access the admin page by browsing to http://192.168.4.1
25373 WiFi status has changed to WL_AP_LISTENING (7)
37971 WiFi status has changed to WL_AP_CONNECTED (8)
41507 Got client.
41527 Got client.
41697 Got client.
50120 WiFi status has changed to WL_IDLE_STATUS (0)

70466 Attempting to connect to SSID: Riley
75486 Connected!
SSID: Riley
Signal strength (RSSI):-28 dBm
Access the admin page by browsing to http://192.168.1.66
75487 WiFi status has changed to WL_CONNECTED (3)

75489 Sending UDP packet to NTP server.
Udp.beginPacket: 1
Udp.endPacket: 1
75643 Received UDP packet from NTP server. Time is 3792770515
75666 Got client.
75840 Got client.
75886 Got client.

80002 Sending UDP packet to NTP server.
Udp.beginPacket: 1
Udp.endPacket: 1
80246 Received UDP packet from NTP server. Time is 3792770520