Skip to content

net/unicoap: Unified and Modular CoAP Stack: Messaging and Minimal Server (pt 2)#21582

Merged
mguetschow merged 16 commits into
RIOT-OS:masterfrom
carl-tud:unicoap-02-server-minimal
Jul 19, 2026
Merged

net/unicoap: Unified and Modular CoAP Stack: Messaging and Minimal Server (pt 2)#21582
mguetschow merged 16 commits into
RIOT-OS:masterfrom
carl-tud:unicoap-02-server-minimal

Conversation

@carl-tud

@carl-tud carl-tud commented Jul 7, 2025

Copy link
Copy Markdown
Contributor

This PR is the second 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 #21389, including reasons why unicoap is needed and a performance analysis.

What does this PR include?

  • RFC 7252 messaging implementation
  • Implementation of the CoAP over UDP and CoAP over DTLS drivers, plus an additional zero-copy optimization for UDP
  • Basic server functionality, including XFA resource definitions and helpful debug logs
  • A sample server application
  • Structured documentation, including a tutorial from the first line of code to running the example and testing it using the included client script
  • Support for running unicoap on a thread of your choice, e.g., the main thread.

The new API is more flexible. CoAP endpoints are abstracted into a unicoap_endpoint_t structure and transport-specific settings are controlled by flags. For example, this is a simple resource that responds with "Hello, World!".

// Define request handler for /hello
static int handle_hello_request(unicoap_message_t* message, 
    const unicoap_aux_t* aux, unicoap_request_context_t* ctx, void* arg) {
    // The aux parameter provides access to auxiliary information, such as local and remote endpoints,
    // transport-specific data and internal properties such as the message token.

    // Retrieve remote (client) endpoint and log string description of protocol number.
    printf("/hello/world resource invoked over %s\n", unicoap_string_from_proto(aux->remote->proto));

    // Craft response using convenience initializer. Vectored payloads are supported, too.
    unicoap_response_init_string(message, UNICOAP_STATUS_CONTENT, "Hello, World!");

    // Send response.
    return unicoap_send_response(message, ctx);
}

// Statically define resource, but dynamic registrations are also possible.
UNICOAP_RESOURCE(helloworld) {
    // When you specify a path, you now need to specify each component separately.
    // The UNICOAP_PATH macro checks if you passed garbage at compile time.
    .path = UNICOAP_PATH("hello", "world"), // /hello/world,
    
    // Instruct unicoap to send confirmable messages when communicating over UDP or DTLS.
    // This flag abstracts the transport-specific message type.
    .flags = UNICOAP_RESOURCE_FLAG_RELIABLE,
    
    // Specify what methods to allow. In unicoap, there are no duplicate defines for method flags.
    .methods = UNICOAP_METHODS(UNICOAP_METHOD_GET, UNICOAP_METHOD_PUT),
    
    // Optionally, you can also restrict the resource to a set of transports.
    .protocols = UNICOAP_PROTOCOLS(UNICOAP_PROTO_DTLS, UNICOAP_PROTO_UDP),
    
    .handler = handle_hello_request,
    .handler_arg = NULL
};

More in the documentation (CI build sometimes not available, e.g., due to failing tests).


Open discussion points

why does Github not allow to see all unresolved comments and/or to pin comments?!?

listed in #21582 (comment)

Declaration of AI-Tools / LLMs usage:

actual intelligence

@github-actions github-actions Bot added Area: network Area: Networking Area: doc Area: Documentation Area: tests Area: tests and testing framework Area: build system Area: Build system Area: CoAP Area: Constrained Application Protocol implementations Area: sys Area: System Area: examples Area: Example Applications Area: Kconfig Area: Kconfig integration labels Jul 7, 2025
@carl-tud carl-tud changed the title net/unicoap: Messaging and Minimal Server (pt 2) net/unicoap: Unified and Modular CoAP stack: Messaging and Minimal Server (pt 2) Jul 7, 2025
@crasbe crasbe added Type: new feature The issue requests / The PR implemements a new feature for RIOT CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Jul 7, 2025
@riot-ci

riot-ci commented Jul 7, 2025

Copy link
Copy Markdown

Murdock results

✔️ PASSED

ec20943 examples/unicoap_server: prevent CI builds for little-memory boards

Success Failures Total Runtime
11146 0 11146 12m:28s

Artifacts

@carl-tud
carl-tud force-pushed the unicoap-02-server-minimal branch from da014c7 to 4d00949 Compare July 9, 2025 14:37
@github-actions github-actions Bot removed the Area: tests Area: tests and testing framework label Jul 9, 2025
@carl-tud
carl-tud marked this pull request as ready for review July 9, 2025 14:42
@carl-tud

carl-tud commented Jul 9, 2025

Copy link
Copy Markdown
Contributor Author

@LasseRosenow
Does this fit your use case?

/**
* @brief Adds UDP socket for client and server functionality
* @pre Socket must be pre-allocated and must remain allocated until the UDP driver has been
* deinitialized or the socket is closed by you.
*
* @param[in,out] socket Pre-allocated socket to use
* @param[in] local Initialized endpoint, does not need to outlive this function call
*
* You can call this function at any time after `unicoap` has been initialized.
*/
# if IS_USED(MODULE_UNICOAP_DRIVER_UDP) || defined(DOXYGEN)
int unicoap_transport_udp_add_socket(sock_udp_t* socket, sock_udp_ep_t* local);

Comment thread examples/networking/coap/unicoap_server/Makefile.ci

@mguetschow mguetschow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the next PR! Great code quality as usual! Reviewed up to and including unicoap/docs.

Comment thread examples/networking/coap/unicoap_server/Makefile Outdated
Comment thread examples/networking/coap/unicoap_server/app.config Outdated
Comment thread examples/networking/coap/unicoap_server/client.py
Comment thread examples/networking/coap/unicoap_server/client.py
Comment thread examples/networking/coap/unicoap_server/main.c Outdated
Comment thread sys/net/application_layer/unicoap/docs/server.doc.md
Comment thread sys/net/application_layer/unicoap/docs/server.doc.md
Comment thread sys/net/application_layer/unicoap/docs/server.doc.md Outdated
Comment thread sys/net/application_layer/unicoap/docs/server.doc.md Outdated
Comment thread sys/net/application_layer/unicoap/docs/server.doc.md Outdated

@crasbe crasbe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A first look-through. Mikolai already commented about the indentation of the preprocessor commands, but I only saw that once I went through it 😅

There are probably some functions that slipped through, but I'll do a second round when most of the comments are resolved. It's becoming a bit crowded.

Comment thread sys/include/net/unicoap/transport.h Outdated
Comment thread sys/include/net/unicoap/application.h Outdated
Comment thread sys/include/net/unicoap/application.h Outdated
Comment thread sys/include/net/unicoap/application.h Outdated
Comment thread sys/include/net/unicoap/application.h Outdated
Comment thread sys/include/net/unicoap/application.h
Comment thread sys/include/net/unicoap/application.h
Comment thread sys/include/net/unicoap/application.h Outdated
Comment thread sys/include/net/unicoap/application.h
Comment thread sys/include/net/unicoap.h

@mguetschow mguetschow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Now this is (only?) missing review of */transport.c, server.c, and state.c.

Comment thread sys/net/application_layer/unicoap/drivers/rfc7252/dtls/doc.md Outdated
Comment thread sys/net/application_layer/unicoap/drivers/rfc7252/common/messaging/messaging.c Outdated
Comment thread sys/net/application_layer/unicoap/drivers/rfc7252/common/messaging/messaging.c Outdated
Comment thread sys/net/application_layer/unicoap/include/private/messaging.h Outdated
Comment thread sys/net/application_layer/unicoap/include/private/messaging.h Outdated
Comment thread sys/net/application_layer/unicoap/include/private/messaging.h Outdated
Comment thread sys/net/application_layer/unicoap/include/private/messaging.h Outdated
Comment thread sys/net/application_layer/unicoap/include/private/messaging.h

@mguetschow mguetschow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I've now gone once through all files, consider this a first round and feel free to start pushing fixups :)

Comment thread sys/net/application_layer/unicoap/drivers/rfc7252/dtls/transport.c Outdated
Comment thread sys/net/application_layer/unicoap/drivers/rfc7252/dtls/transport.c Outdated
Comment thread sys/net/application_layer/unicoap/drivers/rfc7252/dtls/transport.c Outdated
Comment thread sys/net/application_layer/unicoap/drivers/rfc7252/dtls/transport.c Outdated
Comment thread sys/net/application_layer/unicoap/drivers/rfc7252/dtls/transport.c Outdated
Comment thread sys/net/application_layer/unicoap/state.c Outdated
Comment thread sys/net/application_layer/unicoap/state.c Outdated
Comment thread sys/net/application_layer/unicoap/state.c
Comment thread sys/net/application_layer/unicoap/state.c Outdated
Comment thread sys/net/application_layer/unicoap/state.c Outdated
Comment thread sys/net/application_layer/unicoap/docs/server-tutorial.doc.md Outdated
@mguetschow

Copy link
Copy Markdown
Contributor

One test datapoint:

$ python3 examples/networking/coap/unicoap_server/client.py -m GET -u "coap://[fe80::5c64:bbff:fe11:9231%tap0]/" 
usage: client.py -m <GET|PUT|POST|DELETE|PATCH|iPATCH|FETCH> -u <URI> [--type <NON|CON>] [--observe] [-p <PAYLOAD>]
DEBUG:asyncio:Using selector: EpollSelector
using NON GET request
DEBUG:coap-server:Server ready to receive requests
DEBUG:coap-server:Sending request - Token: 1d4c, Remote: <UDP6EndpointAddress [fe80::5c64:bbff:fe11:9231%tap0] (locally ::%tap0)>
DEBUG:coap-server:Sending message <aiocoap.Message at 0x7fbaf33c74d0: NON GET (MID 43720, token 1d4c) remote <UDP6EndpointAddress [fe80::5c64:bbff:fe11:9231%tap0] (locally ::%tap0)>>
DEBUG:coap-server:Incoming message <aiocoap.Message at 0x7fbaf33ded90: CON 4.00 Bad Request (MID 4769, token 1d4c) remote <UDP6EndpointAddress [fe80::5c64:bbff:fe11:9231%tap0] (locally fe80::5c64:bbff:fe11:9230%tap0)>>
DEBUG:coap-server:Received Response: <aiocoap.Message at 0x7fbaf33ded90: CON 4.00 Bad Request (MID 4769, token 1d4c) remote <UDP6EndpointAddress [fe80::5c64:bbff:fe11:9231%tap0] (locally fe80::5c64:bbff:fe11:9230%tap0)>>
DEBUG:coap-server:Response <aiocoap.Message at 0x7fbaf33ded90: CON 4.00 Bad Request (MID 4769, token 1d4c) remote <UDP6EndpointAddress [fe80::5c64:bbff:fe11:9231%tap0] (locally fe80::5c64:bbff:fe11:9230%tap0)>> matched to request <Pipe at 0x7fbaf33dec90 around <aiocoap.Message at 0x7fbaf33c74d0: NON GET (MID 43720, token 1d4c) remote <UDP6EndpointAddress [fe80::5c64:bbff:fe11:9231%tap0] (locally ::%tap0)>> with 2 callbacks (thereof 1 interests)>
DEBUG:coap-server:Sending empty ACK: acknowledging incoming response
DEBUG:coap-server:Sending message <aiocoap.Message at 0x7fbaf331e990: ACK EMPTY (MID 4769, empty token) remote <UDP6EndpointAddress [fe80::5c64:bbff:fe11:9231%tap0] (locally fe80::5c64:bbff:fe11:9230%tap0)>>
response: 4.00 Bad Request
b''
2025-07-15 09:53:50,226 # coap.messaging.rfc7252: received <NON REQ mid=43720 token=1D4C code=0.01 GET payload=(0 bytes) options=(0; 0 bytes)>
2025-07-15 09:53:50,226 # coap.messaging: received in channel:
2025-07-15 09:53:50,226 #       remote=UDP <sock_tl_ep port=5600 netif=7 ipv6=fe80::5c64:bbff:fe11:9230>
2025-07-15 09:53:50,227 #       local=UDP <sock_tl_ep port=5683 netif=0 ipv6=fe80::5c64:bbff:fe11:9231>
2025-07-15 09:53:50,227 # coap.server: /greeting: found
2025-07-15 09:53:50,227 # coap.server: invoking handler
2025-07-15 09:53:50,227 # app: GET /greeting, 0 bytes
2025-07-15 09:53:50,227 # error: could not get 'name' query: -2 (No such file or directory)
2025-07-15 09:53:50,227 # coap.server: sending response 4.00 from return value
2025-07-15 09:53:50,227 # coap.messaging: sending in channel:
2025-07-15 09:53:50,227 #       remote=UDP <sock_tl_ep port=5600 netif=7 ipv6=fe80::5c64:bbff:fe11:9230>
2025-07-15 09:53:50,227 #       local=UDP <sock_tl_ep port=5683 netif=0 ipv6=fe80::5c64:bbff:fe11:9231>
2025-07-15 09:53:50,227 # coap.messaging.rfc7252: sending <CON RESP mid=4769 token=1D4C code=4.00 Bad Request payload=(0 bytes) options=(0; 0 bytes)>
2025-07-15 09:53:50,227 # coap.transport.udp: sendv: 6 bytes
2025-07-15 09:53:50,227 # coap.messaging.rfc7252: <carbon_copy size=6>
2025-07-15 09:53:50,227 # coap.messaging.rfc7252: received <ACK EMPTY mid=4769 token= code=0.00 Empty payload=(0 bytes) options=(0; 0 bytes)>
2025-07-15 09:53:50,228 # coap.messaging.rfc7252: [MID 4769] received ACK, stopping retransmission
2025-07-15 09:53:50,228 # coap.messaging.rfc7252: [MID 4769] transmission ended

@carl-tud carl-tud removed the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Jul 17, 2025
@carl-tud
carl-tud force-pushed the unicoap-02-server-minimal branch from 9a0b4e3 to 9bb6179 Compare July 18, 2026 15:51
@carl-tud
carl-tud force-pushed the unicoap-02-server-minimal branch 2 times, most recently from adf3a6d to 534a07b Compare July 18, 2026 16:03
@mguetschow

mguetschow commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

IETF Hackathon unicoap testing party results

tested with 823d231876a72ed35246f30ca52eab5c0100bdc6 and some temporary adaptions to the unicoap_server example to enable IP-over-slip

✅ means the test passed successfully, ❌ means the test did not pass, in case of (❌) it is most likely not unicoap's fault

  • server: native (gnrc), client: python - coap ✅ coaps ✅
  • server: native (lwip_ipv6), client: python - coap ✅ coaps ✅
  • server: native (lwip_ipv4), client: python - coap ✅ coaps ✅
  • server: nrf52840dk (gnrc), client: python (slipmux) - coap ✅ coaps ✅
  • server: nrf52840dk (grnc), client: gcoap_dtls on iotlab-m3 - coap ✅ coaps ✅ (needs zone identifier on gcoap side)
  • server: nrf52840dk (lwip_ipv6), client: gcoap_dtls on iotlab-m3 - coap ( ❌) (unicoap blocks indefinitely, tracked in lwip, submac: deadlock on user transmission #21843) coaps ( ❌) (unicoap does not receive anything)
  • server: nrf52840dk (lwip_ipv4), client: gcoap_dtls on iotlab-m3 (lwip_ipv4) - coap ( ❌) (msg send fails immediately) coaps (❌) (msg send fails immediately)
  • server: nrf52840dk (lwip_*), client: python (slipmux) - coap ( ❌) coaps (❌)

notes:

  • gcoap_dtls on both boards also does not work with coaps
  • LWIP does not support slipmux at all

TL;DR:

  • gnrc integration seems to work well on native and on hardware
  • lwip integration is broken on hardware for ipv6, but not a unicoap issue, and untestable for ipv4 on hardware (same problem lwip, submac: deadlock on user transmission #21843)
  • nothing is unicoap's fault, should not block from merging

@mguetschow mguetschow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

After the last round of successful tests, let's gooooo!

@AnnsAnns AnnsAnns left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lgtm didnt read didnt test

@carl-tud

Copy link
Copy Markdown
Contributor Author

lgtm didnt read didnt test

same

@carl-tud
carl-tud force-pushed the unicoap-02-server-minimal branch from 534a07b to 9424aa5 Compare July 18, 2026 18:17
@mguetschow
mguetschow enabled auto-merge July 18, 2026 18:19
@mguetschow
mguetschow added this pull request to the merge queue Jul 18, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 18, 2026
@crasbe

crasbe commented Jul 18, 2026

Copy link
Copy Markdown
Contributor
diff --git a/examples/networking/coap/unicoap_message/Makefile.ci b/examples/networking/coap/unicoap_message/Makefile.ci
index 3dfb5c722c..54417567ab 100644
--- a/examples/networking/coap/unicoap_message/Makefile.ci
+++ b/examples/networking/coap/unicoap_message/Makefile.ci
@@ -1,8 +1,10 @@
 BOARD_INSUFFICIENT_MEMORY := \
     arduino-duemilanove \
+    arduino-leonardo \
     arduino-nano \
     arduino-uno \
     atmega328p \
     atmega328p-xplained-mini \
     atmega8 \
+    nucleo-l011k4 \
     #
diff --git a/examples/networking/coap/unicoap_server/Makefile.ci b/examples/networking/coap/unicoap_server/Makefile.ci
index d3841a2374..fa8fdaf917 100644
--- a/examples/networking/coap/unicoap_server/Makefile.ci
+++ b/examples/networking/coap/unicoap_server/Makefile.ci
@@ -1,5 +1,4 @@
 BOARD_INSUFFICIENT_MEMORY := \
-    airfy-beacon \
     arduino-duemilanove \
     arduino-leonardo \
     arduino-mega2560 \
@@ -13,18 +12,15 @@ BOARD_INSUFFICIENT_MEMORY := \
     blackpill-stm32f103c8 \
     bluepill-stm32f030c8 \
     bluepill-stm32f103c8 \
-    calliope-mini \
     derfmega128 \
     hifive1 \
     hifive1b \
     i-nucleo-lrwan1 \
     im880b \
     mega-xplained \
-    microbit \
     microduino-corerf \
     msb-430 \
     msb-430h \
-    nrf51dongle \
     nucleo-c031c6 \
     nucleo-f030r8 \
     nucleo-f031k6 \
@@ -34,6 +30,7 @@ BOARD_INSUFFICIENT_MEMORY := \
     nucleo-f302r8 \
     nucleo-f303k8 \
     nucleo-f334r8 \
+    nucleo-g031k8 \
     nucleo-l011k4 \
     nucleo-l031k6 \
     nucleo-l053r8 \
@@ -44,6 +41,8 @@ BOARD_INSUFFICIENT_MEMORY := \
     saml11-xpro \
     slstk3400a \
     stk3200 \
+    stm32c0116-dk \
+    stm32c0316-dk \
     stm32f030f4-demo \
     stm32f0discovery \
     stm32f7508-dk \
@@ -52,7 +51,6 @@ BOARD_INSUFFICIENT_MEMORY := \
     stm32mp157c-dk2 \
     telosb \
     weact-g030f6 \

@mguetschow
mguetschow enabled auto-merge July 19, 2026 07:48
@mguetschow
mguetschow added this pull request to the merge queue Jul 19, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 19, 2026
@mguetschow
mguetschow added this pull request to the merge queue Jul 19, 2026
Merged via the queue into RIOT-OS:master with commit 400700a Jul 19, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: None Stated AI was not (stated to be) used in this PR/Issue Area: build system Area: Build system Area: CoAP Area: Constrained Application Protocol implementations Area: doc Area: Documentation Area: examples Area: Example Applications Area: Kconfig Area: Kconfig integration Area: network Area: Networking Area: sys Area: System Area: tests Area: tests and testing framework CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants