Skip to content

Conversation

@mkschreder
Copy link

@mkschreder mkschreder commented Dec 14, 2025

Summary

This PR adds a new way to run Zephyr IP networking over a simple TX/RX UART link using cobs for framing. Zephyr currently does not have a proper serial line network interface with multi instance support because the existing one uses uart pipe.

The interface I'm adding in this PR uses predictable overhead COBS framing that is much better than slip and I'm also adding this as an interface of which you can have many instances. This interface is primarily meant for reliable board to board networking and I'm also adding throughput samples for both UDP and TCP so you can test how this works.

NOTE: this is a big PR and still needs to be cleaned up. Also I'm still working on refactoring the samples and my goal is to make them bare yet robust. I also plan to update docs and describe in detail how this works.

  • A new COBS UART async network driver that exposes a standard Zephyr network interface over UART.
  • A small COBS-based framing layer for serial links so the UART link has clear packet boundaries.
  • New samples to make bring-up and evaluation easy:
    • samples/net/cobs_uart_async (basic interface + net shell)
    • samples/net/cobs_throughput_client / samples/net/cobs_throughput_server (UDP echo-based throughput/latency)
    • samples/net/cobs_throughput_single and samples/net/cobs_throughput_tcp (single-board, cross-wired UART testing)
  • New tests covering both “bytes on the wire” and “sockets on top”:
    • tests/drivers/cobs_uart_async_loopback
    • tests/drivers/cobs_uart_async_sockets
  • Documentation + devicetree binding to make configuration straightforward.

Why this is useful

  • Reliable serial networking: enables IP connectivity on systems that only have a basic UART link available (TX/RX + GND), without requiring hardware flow control.
  • Easier evaluation and regression coverage: provides ready-to-run samples for throughput testing and adds automated tests to keep the full stack (driver → network stack → BSD sockets) working over time.
  • Cleaner framing behavior: COBS framing is both efficient and necessary in this case for packet based communication on a stream-based transport like UART.

What’s included (high level)

  • Driver: drivers/net/cobs_uart_async.c (+ Kconfig + build integration)
  • Serial framing support: new COBS Serial L2 + net_pkt COBS helper APIs
  • Devicetree: binding for declaring one or more COBS UART interfaces
  • Samples + tests: end-to-end examples and validation coverage for real hardware and simulation scenarios

Dependencies

Testing

  • Added unit test and a socket integration samples to validate end-to-end behavior over COBS framed packet link.

Copy link
Member

@jukkar jukkar left a comment

Choose a reason for hiding this comment

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

Nice work! There is a lot of code and I will continue the review later, this was just a first glance of the PR.
Couple of thoughts about this:

  • It might be confusing if this is called SLIP as it typically means certain framing for the packets as seen in https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol. Should we mention the COBS there or something like that?
  • Would it be possible to create a Linux counter part of the protocol so that this could be used from qemu or even from native_sim? So similar way the SLIP can be used.

* Non-destructive: tracks read position without modifying source.
*/
struct cobs_encode_state {
struct net_buf *src_frag; /* Current source fragment being read */
Copy link
Member

Choose a reason for hiding this comment

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

Doxygen requires /**< Current source fragment being read */ style comment in order it to generate the documentation properly.

Copy link
Author

Choose a reason for hiding this comment

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

I will update all comments to follow doxygen format

Copy link
Author

Choose a reason for hiding this comment

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

This will disappear once the cobs MR is merged.


/* Configure IP address */
struct in_addr addr;
net_addr_pton(AF_INET, "192.168.7.1", &addr);
Copy link
Member

Choose a reason for hiding this comment

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

Please use IP address range reserved for documentation, so here you could use 192.0.2.1 for example

Copy link
Author

Choose a reason for hiding this comment

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

Sure. I think I will update them to be ipv6 addresses though.

Comment on lines 14 to 18
#define LOG_MODULE_NAME slip_uart_async
#define LOG_LEVEL CONFIG_SLIP_UART_ASYNC_LOG_LEVEL

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
Copy link
Member

Choose a reason for hiding this comment

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

Instead of this, you could just write

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(slip_uart_async, CONFIG_SLIP_UART_ASYNC_LOG_LEVEL);

which is the style we are mainly using in zephyr.

scan_count++;
if (byte == COBS_DELIMITER) {
/* Start of frame */
LOG_INF("✓ Found COBS delimiter (0x00), starting frame (scanned %d bytes)", scan_count);
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure if it is a good idea to print non ascii chars here, it does not give much benefit and might cause some garbage to be printed.
Also this looks like a debug print than an info message. Usually there is no need to print this kind of messages unless you are debugging things.

Copy link
Author

Choose a reason for hiding this comment

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

I think this is gone now.

Comment on lines +393 to +483
/* Generate MAC address (00-00-5E-00-53-xx per RFC 7042) */
ctx->mac_addr[0] = 0x00;
ctx->mac_addr[1] = 0x00;
ctx->mac_addr[2] = 0x5E;
ctx->mac_addr[3] = 0x00;
ctx->mac_addr[4] = 0x53;
ctx->mac_addr[5] = sys_rand8_get();
Copy link
Member

@jukkar jukkar Dec 14, 2025

Choose a reason for hiding this comment

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

I am not sure we should use this MAC address space here, the values specified in the RFC are meant for documentation but as this is a driver that might be used in real products, then this is perhaps not a good thing. Could we specify that mac address in DT?

Comment on lines 24 to 34
#ifndef CONFIG_SLIP_THROUGHPUT_PACKET_SIZE
#define CONFIG_SLIP_THROUGHPUT_PACKET_SIZE 512
#endif

#ifndef CONFIG_SLIP_THROUGHPUT_SERVER_IP
#define CONFIG_SLIP_THROUGHPUT_SERVER_IP "192.168.7.1"
#endif

#ifndef CONFIG_SLIP_THROUGHPUT_DURATION_SEC
#define CONFIG_SLIP_THROUGHPUT_DURATION_SEC 60
#endif
Copy link
Member

Choose a reason for hiding this comment

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

I don't recommend re-defining the config variables but do it like this instead

#if defined(CONFIG_SLIP_THROUGHPUT_PACKET_SIZE)
#define SLIP_THROUGHPUT_PACKET_SIZE CONFIG_SLIP_THROUGHPUT_PACKET_SIZE
#else
#define SLIP_THROUGHPUT_PACKET_SIZE 512
#endif

Why the Kconfig variable would be undefined, why do we even need to check this?

/* Set receive timeout */
timeout.tv_sec = 1;
timeout.tv_usec = 0;
ret = zsock_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
Copy link
Member

Choose a reason for hiding this comment

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

Please check the code and do not mix POSIX and network namespaced symbols. We recently merged a PR that adds a prefix to various network symbols in order to avoid conflicts with POSIX.
So here use ZSOCK_SOL_SOCKET and ZSOCK_SO_RCVTIMEO
If you want to check the usage of the correct APIs, you can set CONFIG_NET_NAMESPACE_COMPAT_MODE=n to disable the compatibility mode and make sure correct symbols are used.

Copy link
Author

Choose a reason for hiding this comment

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

These samples will be cleaned up as I mentioned in the note in PR description.


source "Kconfig.zephyr"

config SLIP_THROUGHPUT_PACKET_SIZE
Copy link
Member

Choose a reason for hiding this comment

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

We have been trying to namespace the network sample specific Kconfig symbols so that it is clear where they come from. So the symbols should be called

config NET_SAMPLE_SLIP_THROUGHPUT_PACKET_SIZE

etc


# Packet size configuration (can be overridden)
CONFIG_SLIP_THROUGHPUT_PACKET_SIZE=512
CONFIG_SLIP_THROUGHPUT_SERVER_IP="192.168.7.1"
Copy link
Member

Choose a reason for hiding this comment

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

Please use the IP address space we use in the configs by default. So the address in this case should be 192.0.2.1
User can always change this in command line when compiling the application if needed.

- IPv6: fe80::2

Default test parameters (can be overridden via Kconfig or shell):
- Server IP: 192.168.7.1
Copy link
Member

Choose a reason for hiding this comment

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

Please change this

@mkschreder mkschreder force-pushed the feature/slip_cobs_uart_async branch 2 times, most recently from 577b850 to 19731e2 Compare December 15, 2025 02:18
@mkschreder mkschreder force-pushed the feature/slip_cobs_uart_async branch from 19731e2 to 1be77a0 Compare December 15, 2025 02:42
@mkschreder
Copy link
Author

Nice work! There is a lot of code and I will continue the review later, this was just a first glance of the PR. Couple of thoughts about this:

  • It might be confusing if this is called SLIP as it typically means certain framing for the packets as seen in https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol. Should we mention the COBS there or something like that?
  • Would it be possible to create a Linux counter part of the protocol so that this could be used from qemu or even from native_sim? So similar way the SLIP can be used.

Renaming to cobs_uart_async! Thanks for the heads up!

@mkschreder mkschreder changed the title drivers: net: cobs slip l2 network interface with async uart transport drivers: net: cobs l2 network interface with async uart transport Dec 15, 2025
@mkschreder mkschreder force-pushed the feature/slip_cobs_uart_async branch from 1be77a0 to bb5dc74 Compare December 15, 2025 02:54
Many transports deliver data in small, arbitrary chunks (for example,
UART RX buffers). In these cases, treating COBS encoding/decoding as a
single “whole buffer in / whole buffer out” operation is awkward.

This paves the way for being able to add highly efficient cobs-based
interfaces that operate on chunks instead of whole buffers. We avoid
creating a new buffer just to hold incomplete frame and instead maintain
state as we are receiving more data.

Encoding is a bit more complex since cobs requires lookahead when
encoding, but this is not a problem because we have access to full data
when we encode so we can do lookahead when needed.
@mkschreder mkschreder force-pushed the feature/slip_cobs_uart_async branch 2 times, most recently from 7f655d4 to fc2aa0e Compare December 15, 2025 03:31
Add a new way to run Zephyr IP networking over a simple TX/RX UART link
using cobs for framing. Zephyr currently does not have a proper serial
line network interface with multi instance support because the existing
one uses uart pipe.
@mkschreder mkschreder force-pushed the feature/slip_cobs_uart_async branch 2 times, most recently from f94aa44 to 4cf9037 Compare December 15, 2025 03:42
@sonarqubecloud
Copy link

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants