RIOT icon indicating copy to clipboard operation
RIOT copied to clipboard

net/unicoap: Unified and Modular CoAP stack: Parser and Message APIs (pt 1)

Open carl-tud opened this issue 8 months ago • 10 comments

This PR is the first in a series to introduce unicoap, a unified and modular CoAP implementation for RIOT. An overview of all PRs related to unicoap is presented in https://github.com/RIOT-OS/RIOT/issues/21389, including reasons why unicoap is needed.

What does this PR include?

  • New RIOT module unicoap, including config/Kconfig support
  • Message API of unicoap, including parsing/serializing support for RFC 7252
  • Unittests for options and parser
  • Example application showcasing the message APIs as well as parsing and serializing with unicoap
  • Structured documentation, including an article on internals and a walk-through of the sample application

The new API aims to reduce the need for in-depth protocol knowledge and minimizes necessary boilerplate. CoAP options can now be inserted in any order, which was not possible before. For example,

// Allocate options buffer with capacity of 200 bytes
UNICOAP_OPTIONS_ALLOC(options, 200);

// Use typed accessors for predefined options
unicoap_options_set_content_format(&options, UNICOAP_FORMAT_TEXT);

// Add multiple instances of repeatable options
unicoap_options_add_uri_queries_string(&options, "unit=C&tolerance=2&flag=1");

// Use generic accessors for custom options
uint8_t value[] = { 0xc0, 0xff, 0xee };
unicoap_options_add(&options, 64999, value, sizeof(value));

unicoap_message_t message;
unicoap_request_init_string_with_options(&message, UNICOAP_METHOD_POST, "Hello, World!", &options);

More in the documentation (CI build currently unavailable).

Lines of code (C code in sources and headers, Makefiles): ≈ 3500 plus, ≈ 5000 lines of SVG, ≈ 700 in Markdown files, ≈ 3700 lines of comments

Performance Analysis

The following figures compare unicoap with nanoCoAP in terms of execution time. Even though unicoap is more flexible, it is not necessarily slower.

Click to expand the analysis:


In this experiment, we measured the time to execute get, add/insert, and remove, given a message with a specified number of options. The results show the average time over multiple runs using the same parameter set relative to the number of existing options.

For each of the operations get, add/insert, and remove, we differentiate between a trivial case (i.e., the option is located at the end of the options buffer) and a complex case (i.e., the option is located in between other options).

The step effects occurring in (a) – (d) are artifacts of the 1 microsecond visualization resolution.

Getter

The average time needed to retrieve an option value grows linearly with the number of options present in the buffer, see (a) and (b). Both implementations require slightly less time when the option is present in the middle, see (b), because of the linear search, i.e., the algorithm finds an option in the middle earlier than at the end. The growth rate of unicoap is marginally higher in both cases because of sanity checks in unicoap.

Setter

In nanoCoAP (coap_opt_add_opaque method), when adding options, the timing is independent of the number of options already present. In unicoap, the timing scales linearly, see (c). The reason for this behavior is the following. nanoCoAP requires options to be inserted in-order and thus does not need to check whether there are adjacent options. unicoap, however, does not introduce such a requirement and, therefore, must first iterate over the list of options until the correct insertion slot is found. Moreover, unicoap implements safeguard checks to prevent adding options without sufficient buffer capacity.

Figure (d) shows the results when trailing options change and must be moved. These results do not contain data for nanoCoAP because inserting options out of order is not supported.

Remove

When removing CoAP options from message buffers, unicoap outperforms nanoCoAP, see Figures (e) and (f). On average, unicoapis faster by more than 45 microseconds. The cause of the outliers visible in Figure (f) has not been identified yet.

Parser

unicoap performs slightly better than nanoCoAP, in particular in cases of multiple options, see Figure (g).

carl-tud avatar Apr 08 '25 16:04 carl-tud

Murdock results

:heavy_check_mark: PASSED

5e353a3967e43f17cd9d360c3d2b0189c91dcf4d net/unicoap: add documentation

Success Failures Total Runtime
10522 0 10522 17m:27s

Artifacts

riot-ci avatar Apr 08 '25 18:04 riot-ci

@carl-tud hey, the images in the performance analysis are 404 to me.

I also took the audacity to refactor your PR description a bit, to increase its accessibility.

Teufelchen1 avatar Apr 11 '25 09:04 Teufelchen1

@carl-tud hey, the images in the performance analysis are 404 to me.

I think they are still in your private repository.

miri64 avatar Apr 11 '25 09:04 miri64

@miri64 @Teufelchen1 Thanks, they should be visible now.

carl-tud avatar Apr 11 '25 11:04 carl-tud

Could you split out the basic message and option parsing plus unit testing into a single PR? It would be sufficient to start with just URI-Path and maybe some numeric Option (Max-Age?) in that PR.

While I like this idea, I am not sure this would make things better. The bulk of this PR is documentation, which would still be part of the basic message PR. If we go for a split, I would rather go for an opaque (byte sequence) options to keep in this PR, as they should be the easiest ones to create and parse.

miri64 avatar Apr 11 '25 13:04 miri64

How long can you / do you intend to provide maintenance for this code?

Teufelchen1 avatar Apr 14 '25 10:04 Teufelchen1

How long can you / do you intend to provide maintenance for this code?

Definitely over the summer until October. After that, I think I am going to need some help (1–2 people) maintaining it. Nevertheless, if someone was willing to help from the get-go, then that would also be great.

carl-tud avatar Apr 22 '25 08:04 carl-tud

Do you happen to have a size (RAM/ROM) comparison with either nanocoap or gcoap?

Teufelchen1 avatar Apr 24 '25 10:04 Teufelchen1

(reviewed up to and excluding unicoap/doc)

mguetschow avatar Apr 30 '25 12:04 mguetschow

Do you happen to have a size (RAM/ROM) comparison with either nanocoap or gcoap?

Please see the RAM/ROM comparison in https://github.com/RIOT-OS/RIOT/issues/21389 :)

carl-tud avatar May 12 '25 18:05 carl-tud

(Possibly) Unresolved Review Points: https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2038214528 ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2067050803~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2067063534~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2067073241~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2067218117~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2068462870~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2068515596~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2068517891~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2068518783~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2081957711~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2120561042~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2081968057~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2084237583~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2084243366~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2096119604~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2123992817~~ ~~https://github.com/RIOT-OS/RIOT/pull/21390#discussion_r2124093187~~

Ping @mguetschow

crasbe avatar Jun 30 '25 13:06 crasbe

What's the status, can I squash?

carl-tud avatar Jul 07 '25 09:07 carl-tud

What's the status, can I squash?

Yes, please do!

mguetschow avatar Jul 07 '25 14:07 mguetschow

ready

carl-tud avatar Jul 07 '25 17:07 carl-tud

I ran make -C examples/networking/coap/unicoap-message generate-Makefile.ci locally and this is the Makefile.ci it generated:

BOARD_INSUFFICIENT_MEMORY := \
    arduino-duemilanove \
    arduino-nano \
    arduino-uno \
    atmega328p \
    atmega328p-xplained-mini \
    atmega8 \
    #

crasbe avatar Jul 08 '25 08:07 crasbe

@carl-tud when you add the examples/networking/coap/unicoap-message/Makefile.ci file, you can squash the commit directly.

crasbe avatar Jul 08 '25 09:07 crasbe

Okay, will do, but tomorrow

carl-tud avatar Jul 08 '25 19:07 carl-tud

@crasbe thanks. done.

carl-tud avatar Jul 08 '25 20:07 carl-tud

Congrats! :tada:

Teufelchen1 avatar Jul 10 '25 07:07 Teufelchen1