[Solved/Workaround] Spark Core can't send UDP broadcast packets without cloud connection

The firmware does generate multicast packets every time it starts up, in a half-hearted attempt at playing nicely with other bonjour children, I suspect - so multicast is certainly possible. However, there are all kinds of subtleties like 224.x.x.x vs 239.x.x.x that can come into play with multicast. The devil is truly in the details.

I will not have a chance to test anything for about 10 days, so someone else will need to lead the way with testing and validation.

Some folks appear to have had trouble UDP broadcast on 255.255.255.255 but had success on 168.0.1.255 type addresses. I don't know why this would be--the core software does not appear to look at IP addresses at all before passing them to the TI part.

The Spark software itself does a multicast UDP announcement to 224.0.1.187 on port 5683 and I know for sure that it works since I had some code looking for these "I'm awake!" messages at one time. I see @AndyW mentions these above as I am typing this!

1 Like

I don’t really have any flexibiliy in the implementation. I have to send the multicast packet to 239.255.255.250:1900, if I want to interface with the Sony camera API.

I’ll have to run a few more tests, but it appears that the camera itself reserves an IP and a port, which doesn’t vary. But it’s definitely a hack

I found this link to someone who appears to have successfully setup SSDP on the CC3000.
http://forum.soldersplash.co.uk/viewtopic.php?f=15&t=90
which appears to draw from this example from the WiFi DipCortex. I’ll have to take a closer look at the code, but assuming I can get bare metal access to the CC000, it looks like it should work.
http://developer.mbed.org/users/SolderSplashLabs/code/WiFiDip-KitchenSink/

I can’t say I have a lot of experience with the Spark Core’s UDP library. I’ll take a look, but perhaps some should compare the two libraries and see what the WiFi Dipcortex people are doing differently

1 Like

I just wanted to add that you should be sure you are looking for packets with wireshark when the core powers up, since the usually syndrome is that the core sends 5-6 packets and then goes into the ~5 second timeout.

UDP broadcast on 255.255.255.255 works like a charm for me.

@benwis Tweaked your code a bit and it works like a charm, I can even see it on Wireshark. Two significant changes I did:

  1. kept core autoconnecting to cloud
  2. Disabled WiFi AP configuration

So it connects to my predefined (and single) WiFi at home. It waits for serial console, so send it an Enter and it starts. The code is below:

It sends query packet once and then it listens forever. I was surprised how many devices talk on the network. Hope it helps :smile:

2 Likes

@laml It definitely helps. The packet definitely sends correctly, and that’s a good step forward… I guess I’ll have to see if it only works when the cloud is autoconnected. Maybe a call to spark.disconnect(); immediately will allow successful code execution while still functioning.

Strange that it doesn’t seem to work in manual mode.

Does anyone know what would cause the manual mode to not work? It connects to the WiFi the same, is it something to do with not enough spark.process(); calls? I would think since the UDP code is local, that would be the problem, unless automatic mode does more than I know about. Maybe some garbage collection or initialization routine.

EDIT: I’ve added as many process calls as I can think to, here’s the sample code:


Connects to the wifi successfully, checks for legitimate IP, and uses same packet sending code as the example above, except no indication of a packet actually being sent. Added a lot of Spark.process() calls.

Anyone know of a different way to leave it in automatic, but make it skip the cloud connection?

1 Like

Hi @benwis

I was helping another user with UDP broadcast issues and tried to just modify his short program to broadcast on 239.255.255.250:1900 with the cloud on. It works fine in the sense that I see the packets on wireshark. Since I don’t have the protocol payload correct, it comes over as a non SSDP packet but filtering on UDP I see it correctly.

I would put your wireshark filter to “udp” and look for your core’s source address.

What did not work was trying to use tcpdump or nc to look at those packets presumably since my host OS is already listening on that port.

Here’s the code–I am using a serial LCD on Serial1 to look at debug messages.

[Minor edit–forgot to change port–works on 1900 as well.]


UDP udp;

IPAddress remoteIP(239,255,255,250);
int remotePort = 1900;
int count = 0;

unsigned char TxMsg[5] = { 'H', 'E', 'L', 'L', 'O'};

void setup() {
    udp.begin(5000);
    Serial1.begin(9600);
}

void loop() {
    Serial1.print("Run ");
    Serial1.print(count++);
    udp.beginPacket(remoteIP,remotePort);
    udp.write(TxMsg,5);
    udp.endPacket();
    delay(1000);
}

@bko So I’ve run both yours and @laml example programs, and both work fine. So we know it’s capable of sending multicast packets.

What I haven’t been able to achieve is sending multicast UDP packets while the cloud is disabled, as in SYSTEM_MODE(MANUAL);
I won’t have an internet connection on the network 1000m up.

I don’t know much about the firmware itself, but I suspect one of these things to be occuring.

  1. Some weird connection between cloud code and multicast
  2. Some garbage collection or process call that’s disabled in MANUAL mode
  3. Something weird with the Spark.process call.

I’ve tried testing 3 by adding tons of the calls as in the above post. I’ve also sent the same packet to the router on the network in manual mode, and received responses. So that just leaves 1.

Something in the UDP library specifically relating to multicast only works when Automatic mode is enabled. I think it’s a bug

Here’s my latest code. Change the IP address to your router’s IP address, put in some credentials, and you’re off to the races. Packets sent and received. Comment that IP address line, uncomment the one above for SSDP. Zilch.
Load @laml’s example above, with the same code with cloud enabled. Packets in and out.

Can anyone shed some light on why that would be so?

SYSTEM_MODE(MANUAL);
// UDP Port used for two way communication
unsigned int localPort = 1900;
#define MAX_SIZE 1024
char buffer[MAX_SIZE];

unsigned int once = 0;

unsigned int ssdpport = 1900;
//This is the multicast broadcast IP
// IPAddress ip( 239, 255, 255, 250 );

//This is my router's ip address
IPAddress ip( 192, 168, 8, 1 );
// An UDP instance to let us send and receive packets over UDP
UDP Udp;

void setup() {
    Spark.disconnect();
    Serial.begin(9600);
    WiFi.on();
    // Connects to a network with a specified authentication procedure.
    // Options are WPA2, WPA, or WEP.
    WiFi.clearCredentials();
    WiFi.setCredentials("deathstarplans", "notinthemaincomputer", WPA);
    WiFi.connect();
    // while(!Serial.available()) Spark.process();

    while (!WiFi.ready()) {
        Serial.println("Waiting for WiFi...");
        Spark.process();
        delay(1000);
    }
}

void loop() {
    Spark.process();
    if ((!once) && (WiFi.ready() == true)) {
       IPAddress addr = WiFi.localIP();

      if ((addr[0] != 0 ||addr[1] != 0 || addr[2] != 0 || addr[3] != 0) && (once == 0)) {
     Serial.println("UDP has begun");
        once = 1;
        Udp.begin(localPort);
        Spark.process();
        Serial.println(WiFi.localIP());
         //Sends SSDP Packet to multicast address
        Udp.beginPacket(ip, ssdpport);
        Spark.process();
        Udp.write("M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: 5\r\nST: ssdp:all\r\n\r\n");
        Spark.process();
        Udp.endPacket();
        Spark.process();
       }
      

       
    }
    //Module to parse received SSDP packet
    Spark.process();
    int rcvd = Udp.parsePacket();
    if (rcvd > 0) {
        Serial.print("Received UPD packet, length: ");
        Serial.println(rcvd);
        Serial.println(buffer);
        // Read first char of data received
        Udp.read(buffer, MAX_SIZE);
            Spark.process();
        if (rcvd > MAX_SIZE) {
            Serial.println("Too large packet");
            while (Udp.available())
                Udp.read();
                  Spark.process();
        }
    }
}
1 Like

Me too: UDP broadcasts and SYSTEM_MODE(MANUAL) - #2 by psb777 - Troubleshooting - Particle - but a very awkward workaround is that if you initially connect and then disconnect to the cloud then UDP broadcasts do work. It is reported that if you UDP broadcast to the network specific broadcast address e.g. 192.168.1.255 instead of 255.255.255.255 then this works without ever having a cloud connection - but I haven't tried that myself yet.

@psb77 have you created an issue on the Spark firmware github? If not, I’ll make one tomorrow. I’ll also try the network specific broadcast IP later. It’s really bothering me that this is a thing

I can’t connect to the Cloud, as the interface will be started up in remote locations without internet. I hope a solution is found soon.

EDIT: Created issue on firmware github

1 Like

I have been playing with this tonight and the only way I can make broadcast UDP work in cloud off mode is by using a subnet broadcast address. Neither 255.255.255.255 nor 239.255.255.250 worked but constructing the address like this did:

IPAddress myIP;
IPAddress remoteIP;
  ...
myIP = WiFi.localIP();
remoteIP = {myIP[0],myIP[1],myIP[2],255};

There is nothing in the user wiring code or the driver that looks for multicast addresses, so my feeling is that something in the TI CC3000 setup must be different.

And the thing that must be different must be done by the code run on the Spark when you call Spark.connect(). But the TI chip knows nothing about the Cloud. So maybe UDP broadcasts don’t work until some TCP connection is established?

This turns out to be correct--adding this to my test program makes it work with 255.255.255.255 broadcasts with the cloud off.

        if (once == true) {
            //Make TCP request
            const char server[] = "www.google.com";
            client.connect(server,80);
            delay(1000);
            client.stop();
            //Start UDP
            udp.begin(listenPort);
            once = false;
...

This is a much better work-around for the problem, but I will keep digging.

3 Likes

OK here’s an even better work-around.

if (once == true) {
    WiFi.ping(WiFi.gatewayIP());
    //Start UDP
    udp.begin(listenPort);
    once = false;

This also allows me to broadcast to 255.255.255.255.

3 Likes

Yes, that's neat. Is there anyway we can get that ping included automatically in WiFi.connect()? Then the issue goes away, for everyone, except possibly those doing SSDP over UDP. @benwis ?

2 Likes

Adding WiFi.ping(WiFi.gatewayIP()); before UDP.begin(); causes everything to work as expected.
Now I’ve just got to pull the Location IP from UDP receive, and send a POST packet with the necessary JSON information.

Thanks @bko @ psb777 @laml. I guess I’ll mark this as solved, with the note that it’s a workaround and definitely not fixed.

2 Likes

Hi,

@bko
I’ve been struggling to get this solution to work for me, until I realised that the ping replies were not getting back to the Core (i.e. it was sending to the network, but signal was not good enough for the core to get replies).

Net result is that something like the following may be more reliable:

if (once == true) {
    if(!WiFi.ping(WiFi.gatewayIP()))
        return; //assuming in loop()
    //Start UDP
    udp.begin(listenPort);
    once = false;
}

Thanks for all the hard work to resolve it!

Hi @wmcelderry

If you can’t ping the default gateway, things are not going to go well with any network activity.

Hi @bko

Yep, it’s not a good sign, but it depends on failure modes that can be tolerated.

In my case, putting in the while loop makes it broadcast reliably after the initial set up. Also my application is all about the local network instead of the Internet, so communicating with the gateway can be flaky and it doesn’t matter to me.

To be clear my issue is not a flaky gateway - it is a Core at the limit of it’s reception from the router, but the router can receive packets from the core more easily it seems - and that’s the way round I happen to want it to work, the core broadcasting to the network. This is also the Dev environment, for others it may not be the deployment environment (for me it is).

I’ve been going nuts trying to understand where my code has been ‘going wrong’ as I’ve had this intermittent fault that has happened to co-occur with certain tests that made no sense. I ended up removing all #defines and the code would appear to run flawlessly with the ping work around, but with the #defs it would not work at all - even though a diff of the bins showed them identical!

Now I know this is pure chance as I can get both to work reliably. I’d love to know the chances of the evidence I collected happening by accident…
Experience tells me the probability of this type of issue occurring is higher than logic tells me it should be, and I reckon that will be a familiar story to many other coders.

All in all, the while loop helped me a lot. I thought I’d mention it in case it helps others…

Thanks again!

W.

1 Like

@benwis At the end - did you manage to discover ActionCam using SSDP and find out it’s IP address to access camera API? Would you mind sharing your code how did finally achieved that? It would be very helpful. Thank you.