[Solved] UDP broadcast occasionally resets system

I want to build a racing timer. I built a prototype with a button to simulate breaking an IR beam. Every 1 second it sends a keep-alive broadcast and when the beam if broken it sends another small message. Problem is that even just sending the keep alive every one second makes the core reset occasionally. I researched that not receiving your own broadcast causes a buffer overflow, so I read my own broadcast, then I tried Udp.stop to flush buffers. Still it causes a core reset. Here’s the code.

#include "application.h"
// Define the pins we're going to call pinMode on
int led = D1;        // This LED will flash every 1 second as to signify that a keepalive USP broadcast was sent.
int onboardLED = D7; // This built-in, tiny LED will flash when a beam break is sent
int BUTTON = D0;     // This is the input for the button pressed.
uint8_t buf[2];      // buffer to send beam break
uint8_t ping[2];     // buffer to send keepalive
UDP Udp;             // UDP control

char discard[64];    // buffer to throw away the broadcasts we have received.
// This routine runs only once upon reset
void setup() {
  pinMode(led, OUTPUT);                             // keepalive LED indicater
  pinMode(onboardLED, OUTPUT);                      // signal break was sent
  pinMode(BUTTON, INPUT_PULLDOWN);                  // signal when button pressed signaling beam break
  attachInterrupt(BUTTON, beamBroken, RISING);      // This sets microswitch as an Interrupt.
  Udp.begin (6948);                                 // use this port
  ping[0] = 0x1;                                    // Initialize keep alive...
  ping[1] = 0x1;                                    //          buffer
}

// This routine gets called repeatedly, like once every 5-15 milliseconds.
// Spark firmware interleaves background CPU activity associated with WiFi + Cloud activity with your code. 
// Make sure none of your code delays or blocks for too long (like more than 5 seconds), or weird things can happen.
void loop() {
    Udp.beginPacket(IPAddress(255,255,255,255),6948);   // Create a broadcast UDP packet
    Udp.write(ping,2);                                  // It contains the two bites of the keep alive
    Udp.endPacket();                                    // End the packet - I beleive it is sent at this time.
    delay(250);                                         // Wait 1/4 of a second to make sure the message went
    digitalWrite(led, HIGH);                            // Turn on the LED to indicate the keep alive was sent
    digitalWrite(onboardLED, LOW);                      // Make sure the LED that indicates a button press is off
    // Udp.stop is another way to flush the buffer
    Udp.stop();                                         // This should flush the buffer that would have the keep alive and button press delivered back to us
    SPARK_WLAN_Loop();                                  // Allow background task to run
    delay(250);                                         // wait another 1/4 second to let things flush out
    Udp.begin(6948);                                    // Start Udp once again.
    delay(500);                                         // Wait 1/2 second to complete sending a keep alive every 1 second
    digitalWrite(led, LOW);                             // Turn OFF the LED that indicates a keep alive send
    //
    // THis code is commented out... I tried this to fush the broadcast and the system kept resetting anyway
    //
    // Flush the buffer since the packet was a broadcast, it needs to read it's own packet
    // int32_t retries = 0;
    // int32_t bytesrecv = Udp.parsePacket();
    // while(bytesrecv == 0 && retries < 1000) {
    //     bytesrecv = Udp.parsePacket();
    //     retries++;
    // }
    
    // if (bytesrecv>0) {
    //     Udp.read(discard,bytesrecv);
    //     digitalWrite(onboardLED, HIGH);
    // }
}

//
// This function sends a broadcast message each time the button is pressed
void beamBroken(){
    buf[0] = 0x1;
    buf[1] = 0x2;
    Udp.beginPacket(IPAddress(255,255,255,255),6948);
    Udp.write(buf,2);
    Udp.endPacket();
}

Hey @harley

The probem is that you’re trying to send the UDP packet from the interrupt handler. This won’t work as you expect - for example, your main loop could also be trying to do a write - and I’m sure you know what happens when two people try to talk at once!

The fix is to not directly write to UDP on the interrupt handler, but to set a flag, and then write to UDP from the main loop.

volatile boolean beamBrokenFlag = false;

void setup() {
    // as before, but change handler
    attachInterrupt(BUTTON, beamBrokenHandler, RISING);    
    // ... etc
}

void beamBrokenHandler() {
   beamBrokenFlag = true;
}

void loop() {
   if (beamBrokenFlag) {
     beamBrokenFlag = false;   // reset flag
     beamBroken();
   }
   // rest of loop as it was.
}

Don’t I Wish that was the issue…

I removed the interrupt portion and just kept sending keep alives to the broadcast address. No interrupts. The code is basically just the original blink LED with a UDP broadcast. Eventually, it causes the blue status light to blink and flutter so I’m assuming that’s a reset.

Here’s the updated code… If I use a unicast IP address, it works (except it doesn’t receive anything). I’m wondering if I should just change the port number established at Udp.begin to be different than the one I’m broadcasting to. That should also keep the buffer from getting filled.

// Define the pins we’re going to call pinMode on
int led = D1; // This LED will flash every 1 second as to signify that a keepalive USP broadcast was sent.
int BUTTON = D0; // This is the input for the button pressed.
uint8_t ping[2]; // buffer to send keepalive
UDP Udp; // UDP control

void setup() {
pinMode(led, OUTPUT); // keepalive LED indicater
pinMode(onboardLED, OUTPUT); // signal break was sent
Udp.begin (6948); // use this port
ping[0] = 0x1; // Initialize keep alive…
ping[1] = 0x1; // buffer
}

void loop() {
Udp.beginPacket(IPAddress(255,255,255,255),6948); // Create a broadcast UDP packet
Udp.write(ping,2); // It contains the two bites of the keep alive
Udp.endPacket();
//
delay(250); // Wait 1/4 of a second to make sure the message went
digitalWrite(led, HIGH); // Turn on the LED to indicate the keep alive was sent
digitalWrite(onboardLED, LOW); // Make sure the LED that indicates a button press is off
//
SPARK_WLAN_Loop(); // Allow background task to run
// Udp.begin(6948); // Start Udp once again.
int32_t retries = 0;
int32_t bytesrecv = Udp.parsePacket();
while(bytesrecv == 0 && retries < 100) {
bytesrecv = Udp.parsePacket();
retries++;
}
if (bytesrecv>0) {
Udp.read(discard,bytesrecv);
digitalWrite(onboardLED, HIGH);
}
//
delay(750); // Wait 3/4 second to complete sending a keep alive every 1 second
digitalWrite(led, LOW); // Turn OFF the LED that indicates a keep alive send
digitalWrite(onboardLED, LOW); // Turn OFF the LED that indicates a keep alive send
}

There is an active discussion over in this thread about how doing broadcast or TX only UDP causes problems. Maybe we should talk about it over there so it is all in one place.

My experience is that you need to check the return values from UDP calls since Udp.write() will return -1 on failure. If you catch this, your code can restart the UDP endpoint since it is typically hosed at that point. Flashing cyan means that you lost the cloud connection and it retrying. I think this is because you have run out of sockets in the TI CC3000 WiFi chip (there are only seven total) but that has not been proven and there are other possibilities.

Checking return values will make your code more robust but it still fails and recovers with a long delay.

Yep, let’s definitely move over there - at first the code looked clearly like an interrupt problem, but with that removed, it’s pretty much the same test case.

Nope… I found the answer I needed. For my application, the correction is attach for reading to a UDP port different than that you are sending to. (See the code below).

Before I go on, I want to explain why I’m using UDP broadcast - an inherently bad way to send wireless messages…

The application is to sell two timer gates that wirelessly connect to an application on an iPhone or iPad. These are racing gates and the timing should be accurate to the nearest 100th of a second. The gates are rigged with IR senders and receivers such that when the IR beam is broken a signal is sent to the app on the iPhone/iPad. One of the timing gates is the starting gate and the other the finishing timing gate. When the beam is broken on the starting gate, the iPhone/iPad starts counting seconds and when the beam is broken on the finishing gate, the timer is stopped and the elapsed time displayed.

A competitive product is selling for about 10 times what I would like to sell this product for. That company builds the entire system, where the concept I have utilzes what you already own.

This product is used in remote locations where WiFi connectivity is not available.

Further, the product must be simple to operate. That means that no IP addressing schemes can be used and no configuration necessary. Further, the iPhone/iPad can change at any time. Anyone with an iPhone/iPad can download the app and begin using the system. Since timing is very important, the timing gates must constantly send an keep-alive message so the iPhone/iPad knows it’s still working. If it misses too many keep alives, it should inform the app user.

Each timing gate is built and shipped with a unique number (hopefully, I’ll figure out how to get the Ethernet address and use it). So, when the app on the iPhone starts looking for keep alives. The user is directed to break the beam of the starting timing gate so the app knows to associate that unique number with the starting gate. Then they are instructed to break the beam of the finishing gate so the app can associate the number of the finishing gate. Once this is performed, the application logic knows how to start the timer then the starting gate beam is broken and stop it when the finishing gate beam if broken.

So, why UDP broadcast??? First, this is an ad hoc network and doesn’t require a WiFi device (hopefully, I’ll figure out how to make the SPARK work without a network). The timers can’t know what subnet they are in, so we can use broadcast so that everyone hears. Second, if the iPhone/iPad isn’t wirelessly connected or the WiFi is turned off, the IP address it responds to is that attached to the cell network. If a unicast IP address was used, and there was an error, these messages would be sending packets to the cellular network and would charge against the users data plan. UDP broadcasts are never forwarded so the IP packet will remain in the local network. I have experimented and I know that if the WiFi is turned off and the data plan of the iPhone is turned off, UDP will not work.

Now… on to testing without a WiFi connection to the SPARK… anyone know how to make WiFi work without a WiFi access point or router?

Here’s the test code that works to do what I outlined above. Every 1 second a keep alive packet (value 1) is sent. If a button is hit that raises the signal on pin D0 a “beam break packet” value2 is sent.

OH… I almost forgot, this code uses an old fashion semaphore so that the keep alive and the beam break are not sent simultaneously. A variable starts at zero. To get the semaphore you increment the variable and check to see if it’s 1. If so, you got the semaphore and can send the packet. So if the break was encountered during the transmission of the keep alive, it waits and when the keep alive has been sent, it checks the semaphore and sends the break for the interrupt routine. If the beam break occurred and the interrupt routing gets the semaphore, the break packet is sent and the keep alive is ignored until the next iteration of the loop.

OK… here’s the code…

// Define the pins we're going to call pinMode on
int led = D1;        // This LED will flash every 1 second as to signify that a keepalive USP broadcast was sent.
int onboardLED = D7; // This built-in, tiny LED will flash when a beam break is sent
int BUTTON = D0;     // This is the input for the button pressed.
int sendSemaphore;
uint8_t buf[2];      // buffer to send beam break
uint8_t ping[2];     // buffer to send keepalive
UDP Udp;             // UDP control

// This routine runs only once upon reset
void setup() {
  pinMode(led, OUTPUT);                             // keepalive LED indicater
  pinMode(onboardLED, OUTPUT);                      // signal break was sent
  pinMode(BUTTON, INPUT_PULLDOWN);                  // signal when button pressed signaling beam break
  attachInterrupt(BUTTON, beamBroken, RISING);      // This sets microswitch as an Interrupt.
  Udp.begin (8496);                                 // use this port must differe from sending port
  ping[0] = 0x1;                                    // Initialize keep alive...
  ping[1] = 0x1;                                    //          buffer
  sendSemaphore = 0;
}

// This routine gets called repeatedly, like once every 5-15 milliseconds.
// Spark firmware interleaves background CPU activity associated with WiFi + Cloud activity with your code. 
// Make sure none of your code delays or blocks for too long (like more than 5 seconds), or weird things can happen.
void loop() {
    sendSemaphore = sendSemaphore + 1;
    if ( sendSemaphore == 1 ) {
        Udp.beginPacket(IPAddress(255,255,255,255),6948);   // Create a broadcast UDP packet
        Udp.write(ping,2);                                  // It contains the two bites of the keep alive
        Udp.endPacket();                                    // End the packet - I beleive it is sent at this time.
        if ( sendSemaphore == 2 ) {
            buf[0] = 0x1;
            buf[1] = 0x2;
            Udp.beginPacket(IPAddress(192,168,1,2),6948);
            Udp.write(buf,2);
            Udp.endPacket();
            sendSemaphore = sendSemaphore - 1;
        }
        sendSemaphore = sendSemaphore - 1;
    }
    delay(250);                                         // Wait 1/4 of a second to make sure the message went
    digitalWrite(led, HIGH);                            // Turn on the LED to indicate the keep alive was sent
    digitalWrite(onboardLED, LOW);                      // Make sure the LED that indicates a button press is off
    delay(750);                                         // Wait 3/4 second to complete sending a keep alive every 1 second
    digitalWrite(led, LOW);                             // Turn OFF the LED that indicates a keep alive send
    digitalWrite(onboardLED, LOW);                             // Turn OFF the LED that indicates a keep alive send

}

//
// This function sends a broadcast message each time the button is pressed
void beamBroken(){
    sendSemaphore = sendSemaphore + 1;
    if ( sendSemaphore == 1 ) {
        digitalWrite(onboardLED, HIGH); 
        buf[0] = 0x1;
        buf[1] = 0x2;
        Udp.beginPacket(IPAddress(192,168,001,002),6948);
        Udp.write(buf,2);
        Udp.endPacket();
        sendSemaphore = sendSemaphore - 1;
    }
}

This code will not work, for the same reason as the original code didn’t. The “semaphore” doesn’t do what you think it does, and doesn’t protect against both loop and the ISR from trying to send a packet at the same time. At the very least you will need to use atomic operations for the increment and decrement and mark the counter as volatile.

Please take my advice and move the UDP code from the interrupt handler. Rather than trying to make this system send the UDP packet as fast as possible, it is better to record timestamps when the beam is broken in the interrupt routine and have those sent on the main loop.

Then I guess I can’t utilize this hardware for my project. My intention was timing for dog agility. Often, dogs complete the course in the same second with only 10th’s of a second separating them. The competitive product costs $1800, so I can still find other hardware that will work and meet my profit margins. Sorry this can’t work… On to my next hardware. Anyone know where I can get someone to design a wireless product that can send UDP broadcast keep alive messages and another UDP broadcast packet as soon as an IR beam breaks?

Looks like a product called Pinoccio can do what I’m looking for.

Hi @harley

I think Spark will work fine for you application. You have to separate the idea of measuring the time from the reporting of the results. If you think that way, then you can time events accurately to the millisecond.

1 Like

My project involved two, independent wireless gates; one for starting and one for finishing. To coordinate the two timers within the desired accuracy would require a synchronization protocol such as IEEE 1588, over complicating the project.

I agree with @bko - the spark will work once the correct approach is taken.

With any wifi product, you will not get the accuracy you need by sending merely a “pulse” over wifi that coincides with start/finish events. Network latency, congestion, interference can introduce delays in any product working with wifi.

By putting timestamps in the messages and synchronizing the time on all spark cores involved, you can have a reliable and accurate system even with network latency and congestion.

In my case, the “network” of wireless devices is limited to the two timing gates and the iPhone/iPad. The events are held in parks or large arenas. That eliminates the issue of network congestion and interference. The proximity of the timing gates should have similar latency between the two devices and the iPhone/iPad.

All that said… I do understand exactly what you are talking about if I were to use a single timing gate. The TOD clock could accurately record the start and stop times with a timestamp and then all of the wireless network issues are mitigated. I’m unsure how the timestamp could work with two independent timers. Could you please tell me your thoughts on two independent timing gates coordinating using timestamps?

Also, why won’t the semaphore work as implemented in the code? Both the interrupt and the main loop share the same memory location for the variable. Both increment the semaphore then check to see if the new value is as expected. The only error that could occur is if the main loop is interrupted between the fetch of the value and the value comparison. That’s pretty small. Back in my “real-time” days when I wrote operating systems, this is exactly how semaphores were implemented with the exception that interrupts were disabled before the increment and enabled after the comparison.

That's exactly the reason. Yes, small liklihood, but possible. Compared to the costs of failure, the fix is easy enough (disable interrupts or use atomic operations) - you wouldn't want this happening when the device is used for real.

Also the variable needs to be marked as volatile or the main loop and the ISR won't see each other's changes (e.g. the compiler is free to store the variable in a register for as long as it wants.) volatile stop this from happening and ensures writes go directly to memory, and reads are from memory (and not the previous value held in the register.)

You guys are my heroes!!! It is working in single timer mode. Apparently, the volatile helped remove some of the irregularity out of the system. ABSOLUTELY no resets, so I think the different UDP port for send and receive fixed the problem.

I have two unrelated questions…

  1. is there a language reference? Where can I read about it?
  2. can the SPARK send and receive wireless messages without a WiFi access point or router? Does it have a default IP address?