UDP broadcast only works when connected to the cloud, why?

I have got a program that sends a UDP broadcast every few seconds. I wanted to add my network credentials in code and connect to WiFi only, not the cloud. So, I changed to SYSTEM_MODE(SEMI_AUTOMATIC), added my network credentials and connect to WiFi. All good, breathing green LED, appears to be broadcasting, but I get nothing in my receive app anymore. I added connect to the cloud to my code and it all works again. Why is this? Am I missing something in terms of the cores interaction with the cloud?

Here is my code;

SYSTEM_MODE(SEMI_AUTOMATIC);

unsigned int localPort = 6767;
IPAddress bcastip(255, 255, 255, 255);

UDP Udp;

void setup()
{
    pinMode(D7, OUTPUT); // DEV BOARD LED
    
    Serial.begin(9600);
    digitalWrite(D7, HIGH);          // sets the LED on
    delay(5000);
    digitalWrite(D7, LOW);           // sets the LED off
    Serial.println("Serial begin");
    
    pinMode(D0,OUTPUT); // short to D1 to enable Spark.connect
    digitalWrite(D0, HIGH);
    pinMode(D1,INPUT_PULLDOWN); // short to D0 to enable Spark.connect
    
    for (int i = 0; i < 10; i++)
    {
        digitalWrite(D7, HIGH);
        delay(50);
        digitalWrite(D7, LOW);
        delay(50);
    }
    
    Serial.println("Setting WiFi credentials");
    WiFi.setCredentials("xxxxxxxxxx", "xxxxxxxxxx");
    Serial.println("Connecting to WiFi ...");
    WiFi.connect();

    for (int i = 0; i < 5; i++)
    {
        digitalWrite(D7, HIGH);          // sets the LED on
        delay(250);                       // waits for 200mS
        digitalWrite(D7, LOW);           // sets the LED off
        delay(250);
    }
    
    // Print your device IP Address via serial
    Serial.println(WiFi.localIP());
}

void loop()
{
    Serial.println("Starting UDP");
    Udp.begin(localPort);
    
    Serial.println("Broadcasting");
    Udp.beginPacket(bcastip, localPort);
    Udp.write("Hello");
    Udp.endPacket();
    
    Serial.println("Stopping UDP");
    Udp.stop();
    
    digitalWrite(D7, HIGH);          // sets the LED on
    delay(250);                       // waits for 200mS
    digitalWrite(D7, LOW);           // sets the LED off
    delay(250);
    
    delay(2000);
    
    if (digitalRead(D1) == HIGH)
    {
        Serial.println("connecting to cloud");
        Spark.connect();
    }
}

@rob_f, I’m not sure if this does apply to or does cause your problem, but AFAIK SEMI_AUTOMATIC does connect the Core to your WiFi but not to the cloud, so you attempt to “mess” with WiFi while still online and this might be the actual cause of your problem.

Have you tried MANUAL mode? In this mode, I think, you have to take care of WiFi connection the way you do it.
Or you could first WiFi.disconnect(), then WiFi.setCredentials() and after WiFi.connect() test for WiFi.ready() to come true.

    Udp.beginPacket(bcastip, localPort);
    Udp.write("Hello");
    Udp.endPacket();

    Serial.println("Stopping UDP");
    Udp.stop();

This is a bit of a race condition since Udp.endPacket() pushes the packet to the TI CC3000 but does not block until it is sent, while Udp.stop() clobbers the socket right away. Maybe you should try moving the stop() after some of the delay.

@ScruffR Thanks for the suggestion, it seems the documentation is a bit wooly in this area. It only talks about not connecting to the cloud in Semi and Manual, but after a bit of testing, the same applies for WiFi too. So in both those modes, you control when to connect both WiFi and the Cloud. The difference between manual and semi appears to be that in Manual, you not only have to connect but you also have to manage comms with the cloud, whereas in semi mode once you initiate a cloud connection, the core takes care of this by communicating with the cloud inbetween runs of your loop().

@bko It runs just fine when connected to the cloud, but if you connect to WiFi only, it does not. I did add a delay after the broadcast and before the stop, but the behaviour is the same. I take your point that maybe that portion of the code is not ideal, but the issue is connecting to the cloud seems to be a necessity and I do not understand why. Not only does it not broadcast when not connected to the cloud, but the performance appears sluggish and stuttery too, i.e. both the serial debug I use and the flash of the blue D7 LED are not running in the time you would expect, they are seemingly randomly slowed down by 10secs ish.

1 Like

Update, I have changed the broadcast ip address with varying success;

255.255.255.255 does not work with only WiFi
192.168.255.255 does not work with only WiFi
192.168.1.255 DOES work without a cloud connection.

Not sure why unless someone can give an explanation, but for now, for me, this is acceptable.

1 Like

That’s great news!

Could be something in your router too, I suppose. I don’t think the core is doing anything different for those three cases.

1 Like

I have no such issues with using 255.255.255.255 on a similar Arduino setup.

Also, and I’m no expert on routers, networking etc. but why a router issue mean that 255.255.255.255 is fine for the core to broadcast UDP when it is connected to the cloud but not when it’s only connected to WiFi.

Surely it’s more likely to be an issue with the implementation of the UDP protocol within the core or it’s libraries?

I hope you don’t think I’m being awkward, I really love the idea of this core, and the project I’m trying to use it for is an upgrade to an existing project that is a big seller across many countries, but at the moment it seems like it’s maybe a bit in it’s infancy for me to use. I will keep plugging away with it though.

Hi @rob_f

Sure, no problem. I was just saying that the core firmware does not really care what address you are using, so if the problem is with the Spark core, it is likely to be inside the TI CC3000 and not really fixable.

If you want to have a robust program that can handle fault conditions gracefully, you should check all the return values from all the Udp methods calls. Not doing that is asking for strange behavior that can be hard to figure out.

I would like to re-iterate that which some respondents ignore: @rob_f reports that UDP broadcasts to 255.255.255.255 work when the Spark is connected to the Cloud but that they do NOT work when the Spark is not connected to the Cloud.

How this particular discrepancy can possibly be the result of a race condition in the user code I think is, err, unlikely! Why would there be a race condition without the Cloud, but not with the Cloud?

Also how can this particular discrepancy in how the Spark works, the difference between the Cloud being connected or not, possibly be down to the TI CC3000? The TI CC3000 does not know or care whether you’re connected to the Cloud or not. From its POV it’s all TCP and UDP and lower level stuff.

Similarly with the router. The router does not know or care whether you’re connected to the Cloud. It just passes packets. Nothing dependent upon the Cloud happens inside the router to effect whether UDP broadcasts on your local LAN work or not.

I have elsewhere [ https://community.spark.io/t/udp-broadcasts-and-system-mode-manual/7712 ] reported the same issue. I cannot UDP broadcast in MANUAL mode unless I first Spark.connect(). I do first WiFi.connect() and WiFi.ready() returns true.

UDP broadcasts do not work until the Cloud has been up.

1 Like

Good news. UDP broadcasts work in all SYSTEM_MODEs if there is some non-UDP activity first. This discovery by @bko is described here https://community.spark.io/t/spark-core-cant-send-udp-broadcast-packets-without-cloud-connection/8070/20 and the less neat workaround is described here https://community.spark.io/t/how-to-send-udp-broadcasts-in-manual-or-semi-automatic-mode/8144

1 Like

Just for completeness:

As it turns out @psb777 has “answered” these questions himself with this

All these descrepancies turn up because once there was non-UDP traffic and other times not.

But why this is, seems still unclear. But it might well be that the CC3000 is the source, as @bko suggested.
None of the above questions actually contradict this hypothesis.

1 Like

I’m unsure if you agree or not. The only possible conclusion is this. Something in the Spark code which connects to the Cloud is missing from the Spark code which connects to the WiFi. If all that needs be done is that WiFi.connect() calls, last step, WiFi.ping(), then so be it.

I don’t think this is the “only possible conclusion”. I wrote the ping interface and I can’t tell you how or why it could possibly have any effect on UDP–they are completely separate code paths down through the driver. My feeling and intuition tells me this problem is in the TI part not in the driver code but time will tell and I could be wrong.

There is essentially hidden software inside the TI part we cannot see, debug or fully control. The bug could easily be in this hidden software or it could be elsewhere in the Spark code, but the blind nature of the TI part makes it very challenging to figure out which it might be. This is one reason I advocated strongly for any new Spark products to move away from the TI parts.

1 Like

Me neither :wink:

I agree that something is rather fishy here, but I disagree with jumping to conclusions like here

I fully agree with Brian ( @bko ) - even if it’s one possibility, it’s definetly not the only.
Much the same I meant in regards to the three questions above. The questions are valid and do help ruling out some causes and narrow down the remaining. But the implicit conclusions therein were not the only possible.

1 Like

In MANUAL mode:

If you connect to the Cloud, UDP broadcasts to 255.255.255.255 work.
If you do not connect to the Cloud, UDP broadcasts to 255.255.255.255 do not work.
If you do not connect to the Cloud, but first ping, UDP broadcasts to 255.255.255.255 do work.
The ping does something, perhaps to the TI chip, perhaps not, which allows the broadcasts to work.
Connecting to the Cloud does something, perhaps to the TI chip, perhaps not, which allows broadcasts to work.

Likely the intersection of (things the ping does) with (things connecting to the Cloud does) will contain the thing needed to be done in WiFi.connect() so as to get UDP broadcasts working.

I would be happy were the ping always called from WiFi.connect() but this is not the purest leanest best solution to the problem.