Skip to content

Conversation

@eigen-value
Copy link
Collaborator

@eigen-value eigen-value commented Oct 7, 2025

UDP class
This PR is also an API proposal of the following CPU-side methods:

  • udp/connect
  • udp/connectMulticast
  • udp/close
  • udp/write
  • udp/awaitRead
  • udp/read

mod: beginPacket sets remotes' IP/port
feat: implemented remaining methods
impr: threading
mod: read stops if no remaining bytes
mod: peek works only per-udp-packet
impr: stop calls only if connected
@eigen-value eigen-value requested a review from cmaglie October 7, 2025 15:07
@eigen-value eigen-value linked an issue Oct 7, 2025 that may be closed by this pull request
@eigen-value eigen-value added the enhancement New feature or request label Oct 7, 2025
@eigen-value eigen-value marked this pull request as ready for review November 10, 2025 15:07
@CLAassistant
Copy link

CLAassistant commented Nov 12, 2025

CLA assistant check
All committers have signed the CLA.

@github-actions
Copy link

Memory usage change @ 79f5565

Board flash % RAM for global variables %
arduino:zephyr:unoq 🔺 0 - +4 0.0 - 0.0 🔺 0 - +8 0.0 - 0.0
Click for full report table
Board examples/client
flash
% examples/client
RAM for global variables
% examples/clientSSL
flash
% examples/clientSSL
RAM for global variables
% examples/hci
flash
% examples/hci
RAM for global variables
% examples/monitor
flash
% examples/monitor
RAM for global variables
% examples/server
flash
% examples/server
RAM for global variables
% examples/simple_bridge
flash
% examples/simple_bridge
RAM for global variables
% examples/test
flash
% examples/test
RAM for global variables
% examples/udp_ntp_client
flash
% examples/udp_ntp_client
RAM for global variables
%
arduino:zephyr:unoq 4 0.0 8 0.0 4 0.0 8 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 N/A N/A N/A N/A
Click for full report CSV
Board,examples/client<br>flash,%,examples/client<br>RAM for global variables,%,examples/clientSSL<br>flash,%,examples/clientSSL<br>RAM for global variables,%,examples/hci<br>flash,%,examples/hci<br>RAM for global variables,%,examples/monitor<br>flash,%,examples/monitor<br>RAM for global variables,%,examples/server<br>flash,%,examples/server<br>RAM for global variables,%,examples/simple_bridge<br>flash,%,examples/simple_bridge<br>RAM for global variables,%,examples/test<br>flash,%,examples/test<br>RAM for global variables,%,examples/udp_ntp_client<br>flash,%,examples/udp_ntp_client<br>RAM for global variables,%
arduino:zephyr:unoq,4,0.0,8,0.0,4,0.0,8,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,N/A,N/A,N/A,N/A

src/udp_bridge.h Outdated
}

uint8_t beginMulticast(IPAddress ip, uint16_t port) override {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Why do some methods have this extra blank line at the beginning? Personally, I'd like to remove it because it's really odd to have this double standard; the blanks in the middle of a method may make sense, but at least we could spare the blank at the beginning of the function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in:
1037bd9

src/udp_bridge.h Outdated
k_mutex_lock(&udp_mutex, K_FOREVER);

String hostname = ip.toString();
const bool ok = _connected || bridge->call(UDP_CONNECT_MULTI_METHOD, hostname, port).result(connection_id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concerns as for the begin(port)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see
1037bd9


if (_connected) {
String msg;
_connected = !bridge->call(UDP_CLOSE_METHOD, connection_id).result(msg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The udp/close will always close the connection and drop the connection_id on the Linux side.

Suggested change
_connected = !bridge->call(UDP_CLOSE_METHOD, connection_id).result(msg);
bridge->call(UDP_CLOSE_METHOD, connection_id).result(msg);
_connected = false;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just to make sure the bridge->call went through without errors on the other side

src/udp_bridge.h Outdated
Comment on lines 153 to 164

if (!connected()) return 0;

MsgPack::arr_t<uint8_t> payload;

for (size_t i = 0; i < size; ++i) {
payload.push_back(buffer[i]);
}

size_t written;
const bool ok = bridge->call(UDP_WRITE_METHOD, connection_id, _targetHost, _targetPort, payload).result(written);
return ok? written : 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this isn't the intended use of this API. Once you use beginPacket, all subsequent write calls will add data to that packet, and it will be sent in one go as a single UDP packet when endPacket is called.
The call to udp/write must be done in the endPacket method.

In other words, the following:

udp.beginPacket("1.2.3.4", 1234);
udp.write("hello", 5);
udp.endPacket();

must be equivalent to:

udp.beginPacket("1.2.3.4", 1234);
udp.write("hel", 3);
udp.write("l", 1);
udp.write("o", 1);
udp.endPacket();

Copy link
Collaborator Author

@eigen-value eigen-value Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This depends on how the other side handles the udp/write method (ie always requiring IP and port).
Besides if the udpBridge library is meant to hold the UDP packet in a buffer up until the user is ready to send, then such buffer should be the max size of a UDP packet (64k)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we extend the UDP API on the router to handle multiple writes? (as we did for the reading side)

Comment on lines +299 to +302

// if (async_res.error.code > NO_ERR) {
// _connected = false;
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// if (async_res.error.code > NO_ERR) {
// _connected = false;
// }

Comment on lines 75 to 82
k_mutex_lock(&udp_mutex, K_FOREVER);

String hostname = "0.0.0.0";
const bool ok = _connected || bridge->call(UDP_CONNECT_METHOD, hostname, port).result(connection_id);
_connected = ok;
if (_connected) _port = port;

k_mutex_unlock(&udp_mutex);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I read this correctly, a double call to begin(port) will return 1 (aka success) even if the second operation would be a no-op:

udp.begin(3000); // returns 1
udp.begin(4444); // returns 1 as well, but 0 is expected

I would expect a return value of 0 in the second call. Additionally, the internal field _port is updated to the value 4444, while the UDP listener on the Linux side remains unchanged.

Suggested change
k_mutex_lock(&udp_mutex, K_FOREVER);
String hostname = "0.0.0.0";
const bool ok = _connected || bridge->call(UDP_CONNECT_METHOD, hostname, port).result(connection_id);
_connected = ok;
if (_connected) _port = port;
k_mutex_unlock(&udp_mutex);
k_mutex_lock(&udp_mutex, K_FOREVER);
bool ok = false;
if (!_connected) {
String hostname = "0.0.0.0";
ok = bridge->call(UDP_CONNECT_METHOD, hostname, port).result(connection_id);
if (ok) {
_port = port;
_connected = true;
}
}
k_mutex_unlock(&udp_mutex);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right. It should be fixed here:
1037bd9

int parsePacket() override {
k_mutex_lock(&udp_mutex, K_FOREVER);

while (_remaining) read(); // ensure previous packet is read
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessary; the RPC API can skip the current UDP packet being read, even if it is not fully consumed.
You can simply set _remaining to 0, with some buffer cleanup if necessary, which should be much quicker than processing the entire packet.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to read() is meant just for flushing in this context.
Again this is a problem of sync between the two sides.
If MCU is not interested in a packet anymore and such packet was not entirely received via RPCs then the MCU should have a method to signal this to the other side.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling udp/awaitRead on the router will automatically drop the unread packet on the Linux side.
You can partially read a packet and await the next one; the piece that you didn't read is lost, but this is allowed on the router API.

Comment on lines +176 to +177
RpcResult async_res = bridge->call(UDP_AWAIT_READ_METHOD, connection_id, read_timeout);
const bool ret = _connected && async_res.result(packet_meta);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
RpcResult async_res = bridge->call(UDP_AWAIT_READ_METHOD, connection_id, read_timeout);
const bool ret = _connected && async_res.result(packet_meta);
const bool ret = _connected && bridge->call(UDP_AWAIT_READ_METHOD, connection_id, read_timeout).result(packet_meta);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes no difference.
async_res could be used later for error handling

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit weird to create an async_res object that may not be consumed later, if _connected is false the && operation is short-circuited and the result method is not called.

@github-actions
Copy link

Memory usage change @ 1037bd9

Board flash % RAM for global variables %
arduino:zephyr:unoq 🔺 0 - +4 0.0 - 0.0 🔺 0 - +8 0.0 - 0.0
Click for full report table
Board examples/client
flash
% examples/client
RAM for global variables
% examples/clientSSL
flash
% examples/clientSSL
RAM for global variables
% examples/hci
flash
% examples/hci
RAM for global variables
% examples/monitor
flash
% examples/monitor
RAM for global variables
% examples/server
flash
% examples/server
RAM for global variables
% examples/simple_bridge
flash
% examples/simple_bridge
RAM for global variables
% examples/test
flash
% examples/test
RAM for global variables
% examples/udp_ntp_client
flash
% examples/udp_ntp_client
RAM for global variables
%
arduino:zephyr:unoq 4 0.0 8 0.0 4 0.0 8 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 0 0.0 N/A N/A N/A N/A
Click for full report CSV
Board,examples/client<br>flash,%,examples/client<br>RAM for global variables,%,examples/clientSSL<br>flash,%,examples/clientSSL<br>RAM for global variables,%,examples/hci<br>flash,%,examples/hci<br>RAM for global variables,%,examples/monitor<br>flash,%,examples/monitor<br>RAM for global variables,%,examples/server<br>flash,%,examples/server<br>RAM for global variables,%,examples/simple_bridge<br>flash,%,examples/simple_bridge<br>RAM for global variables,%,examples/test<br>flash,%,examples/test<br>RAM for global variables,%,examples/udp_ntp_client<br>flash,%,examples/udp_ntp_client<br>RAM for global variables,%
arduino:zephyr:unoq,4,0.0,8,0.0,4,0.0,8,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,0,0.0,N/A,N/A,N/A,N/A

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a class for UDP client

4 participants