Help with UDP getting all characters in packet and filling variable


#1

Hi All,

I’m trying to receive a packet on an Argon and save it to a variable.
I can easily get the variable c below, but I want to get the whole packet.
In my trial case I’m sending a packet:
char* packet = “12345”;

What do I need to do to change the code below to iterate thru all the characters in the packet and get it back into a new variable, instead of reading the first Character and discarding the rest??

  • Or - alternatively, can someone point me to an example of receiving UDP data that is longer, and getting that data into a form that I can manipulate it?

void loop() {
// Check if data has been received
if (Udp.parsePacket() > 0) {

// Read first char of data received
char c = Udp.read();

// Ignore other chars
while(Udp.available())
  Udp.read();

// Store sender ip and port
IPAddress ipAddress = Udp.remoteIP();
int port = Udp.remotePort();

// Echo back data to sender
Udp.beginPacket(ipAddress, port);
Udp.write(c);
Udp.endPacket();

}
}


#2

Here is some stripped down code I use to receive UDP packets and then parse them. I’m not including the parser code, just the UDP reception. I receive the UDP message into a character array jsonString. Once received into the jsonString buffer, you should be able to do anything you want. In my case, I run it through a json parser. I made my buffer size 1500 because that is the typical MTU setting on a network. You can adjust size as needed but 1500 is quite large.

char jsonString[1500];
uint16_t UDPMaxPacketSize = 1500;   //Network MTU size.

SerialLogHandler logHandler(115200, LOG_LEVEL_TRACE);

uint16_t UDPPort = 8023;    //UDP listening port.
uint16_t UDPMaxPacketSize = 1500;   //Network MTU size.
IPAddress MCAddress;   //Multicast Address for Receiving Commands

setup() {
    waitUntil(Particle.connected); //Network must be connected before calling Udp.begin() per photon reference.
    
    IPAddress myIP = WiFi.localIP();
    Log.warn("Local IP: %s; SSID: %s; RSSI: %i", myIP.toString().c_str(), WiFi.SSID(), WiFi.RSSI());
    
    //Start UDP for receiving commands via wi-fi.
    Log.warn("Starting UDP on Port %d.", UDPPort);
    Udp.begin(UDPPort);
    Log.warn("Joining Multicast at address: %s", MCAddress.toString().c_str());
    Udp.joinMulticast(MCAddress);
}

loop() {
    bool rxError = true;
    
    int size = Udp.receivePacket((byte*)jsonString, UDPMaxPacketSize-1);
    if (size > 0 && size < UDPMaxPacketSize) {
        jsonString[size] = 0;
        rxError = false;
    } else if (size < 0) {
        Log.error("UDP receive size error: size = %i", size);
        rxError = true;

        // need to re-initialize on error
        Udp.begin(s.UDPPort);
    } 
    
    //Message receive was success... parse it.
    if (!rxError) {
        Log.trace("UDP message received from %i : %i:", Udp.remoteIP(), Udp.remotePort());
        Log.trace(jsonString);
       
        //do something with the jsonString
    }
}

#3

@ninjatill -
Thanks, I’ll see if I can’t fold that in and make it work!
J


#4

@ninjatill’s advice shows you how you get to your endgoal, but I sense some more fundamental and basic questions need to be addressed too.

If you what to iterate over multiple bytes and store them in a string (aka character array) you’d do something along this line

void loop() {
  if (Udp.parsePacket() > 0) {
    char buf[128];                                  // buffer to hold the string 
    int  idx = 0;
    memset(buf, 0, sizeof(buf));                    // clear the buffer
    while(Udp.available() && idx < sizeof(buf) - 1) // only read till buffer is full
      buf[idx++] = Udp.read();                      // store newly read byte at location idx and increment idx afterwards

    // other stuff needed to clean up 
  }
}

That’s how you could do it byte by byte, but reading all available data at once (as @ninjatill does in his code) is preferable.


#5

@ninjatill,

In the code below, it comments that if there is an error, we need to re-initialize the UDP instance.
This happens every loop if there is no UDP data received, so every milli-second or so.
When I have the “Udp.begin(UDPPort);” in the ‘else if’ - I miss a lot of packets, but when I comment that line out, my reception is much more reliable.

What is the need for the reinitialization?

Is the code problematic without it?

    bool rxError = true;
    
    int size = Udp.receivePacket((byte*)jsonString, UDPMaxPacketSize-1);
    if (size > 0 && size < UDPMaxPacketSize) {
        jsonString[size] = 0;
        rxError = false;
    } else if (size < 0) {
        Log.error("UDP receive size error: size = %i", size);
        rxError = true;

        // need to re-initialize on error
        Udp.begin(UDPPort);
    } 
    
    //Message receive was success... parse it.
    if (!rxError) {
        Log.trace("UDP message received from %i : %i:", Udp.remoteIP(), Udp.remotePort());
        Log.trace(jsonString);
       
        //do something with the jsonString
    }
}

#6

I can’t say. I’m sure I coppied that code from an example somewhere. Do you also see the log message about the packet size error?


#7

That reset UDP code came from the example in the docs:

https://docs.particle.io/reference/device-os/firmware/electron/#receivepacket-

Maybe @rickkas7 can explain why? Or @ScruffR, @peekay123?


#8

That code is not the same as the example in the docs. -1 is returned if there is no data. You should not call udp.begin() in that case.

When an actual error code < -1 occurs, then it’s not a bad idea to call udp.begin() again, though it’s actually better to monitor the connected state of WiFi and call it when reconnected, not on error.


#9

OK, great, that makes me feel better, and I don’t miss many packets.
I’ll just call udp.begin when < -1is returned. I’ll look into monitoring the wifi later.

Thanks,
J