Goal: learn about the effects on performance when packing more than a single data point per UDP packet.

The code used here is available at GitLab.

Experiment with multiple data point per UDP packet

Experiment setup

Arduino Nano 33 IoT + Raspberry Pi 4 Rev. B used in the tests
  • Experiment was run using this script.

    • simple python script
    • uses subprocess to run netcat in the background to capture all the UDP packets sent by Arduino
    • communicates with Arduino using pyserial, ONLY to launch tests
  • Packets received by Raspberry Pi (using netcat) was parsed using this script.

Arduino code

This is based on the same code from “Method 3” of the previous post.

A struct is used to represent a single data point. At the moment there is no need for msg_type, but probably a mechanism will be needed to identify what kind of measurement this data point represents (for example: position, temperature, angle, G force, etc.).

struct message {
    unsigned int msg_type;
    unsigned int value;
    unsigned int timestamp;
};

Then a big array is defined, to hold MULTI_ARR_SIZE data points:

#define MULTI_ARR_SIZE 128

message msg_buff_multi_array[MULTI_ARR_SIZE];

The experiment consisted in sending packets with different amount of data points, ranging from 1 Data point per packet, up to MULTI_ARR_SIZE data points per packet:

for (unsigned int data_points = 1; data_points <= MULTI_ARR_SIZE; len++) {
    for (int i=0; i<1000; i++) {
        send_multiple_datapoint(ip, data_points);
    }
}

The send_multiple_datapoint() function is very simple:

  • stores in msg_type the amount of data points included in the packet (this value is used in the Raspberry Pi to make sure the packet contains all the data that was sent from the Arduino, and plays a key role in some of the findings…)
  • the value is not important, but in a real world application, it would be the value read from some sensor
void send_multiple_datapoint(char *ip, unsigned int data_points) {
    Udp.beginPacket(ip, 4545);
    
    msg_buff_multi_array[0].msg_type = data_points;
    msg_buff_multi_array[0].value = counter;
    msg_buff_multi_array[0].timestamp = micros();
    
    Udp.write((byte *) &msg_buff_multi_array, sizeof(message) * len);
    
    Udp.endPacket();
}

Here only 1 data point is fill with data, and the other data points won’t have any value, but that’s ok for now, since the idea is to measure how the amount of data affects performance, so the focus is on the size of the data, and not in how much time the data acquisition takes.

Real source code

Real code used to run these tests can be found at udp.cpp.

Results

Data points per packetPackets per secondData points per second
1726726
5032716,361
10020920,995
12517822,253

As expected, when a UDP packet contains more than data points, it gets bigger, and the amount of packets per seconds is inversely proportional to the packet size.

Also as expected, in terms of data points per seconds, it is much more efficient to include multiple data points in each UDP packet.

Chart: packets per seconds / data points per second

chart.png

Transmission errors

So far, so good. More than 20,000 data points per seconds looks good for our needs. But, are in fact all those packets reaching the Raspberry Pi?

Let’s add information about errors to the same table:

Data points per packetPackets per secondData points per secondUdp.endPacket() errorsPackets received by RPI
172672619981
5032716,36101000
10020920,99501000
12517822,25301000
12617722,3050?
12717622,3430?
12817522,3860?

When analyzing the packets received by the Raspberry Pi, the script failed when trying to parse packets containing more than 126 data points per packet.

The problem was: only the first 1,500 bytes of the packets were received.

After adjusting the script that parsed the packets, by assuming that packets bigger than 1500 bytes are going to be truncated, everything worked again.

Data points per packetPackets per secondData points per secondUdp.endPacket() errorsPackets received by RPIComplete packets received by RPI
172672619981981
5032716,361010001000
10020920,995010001000
12517822,253010001000
12617722,305010000
12717622,343010000
12817522,386010000

1500 bytes limit

This will require further investigation. Potential causes:

  • limitation of hardware (wifi or Arduino)?
  • limitation of wifi library?
  • configuration?
  • MTU?
  • lack of support for IP fragmentation?