#coap #communication #no-alloc #no-std #lwm2m

no-std coap-zero

CoAP protocol implementation for no_std without alloc

1 unstable release

0.3.0 Feb 16, 2024

#475 in Embedded development

OLFL-1.3

240KB
4.5K SLoC

coap-zero

This crate provides a heapless no_std implementation for the CoAP protocol.

It aims to be zero-copy as much as feasible without sacrificing usability too much. The implemented CoAP endpoint implements automatic retransmissions for reliable message transmission (for confirmable messages) and message de-duplication. The endpoint automatically responds to specific messages, e.g. it sends out reset messages when appropriate or retransmits the last response if a duplicated request is detected.

The implementation is fixed to NSTART=1, i.e. only a single incoming request and a single outgoing request may be processed at the same time.

Currently, only the base CoAP specification is implemented. In the future, it is planned to also support CoAP Observe which is required for LwM2M, the PATCH, FETCH and iPATCH methods which significantly improve LwM2M capabilities and Block-Wise transfers which are required for firmware upgrades in LwM2M.

Resources

Usage

The main structure is the coap_zero::endpoint::CoapEndpoint type. It manages the connection to another coap endpoint via an embedded_nal::UdpClientStack (not owned). It contains two separate types, OutgoingCommunication and IncomingCommunication, to handle the two communication paths separately.

All operations are driven by internal state machines. Therefore, all calls are non-blocking and the user has to call the CoapEndpoint::process method repeatedly in order to send/receive CoAP messages. Whenever one of the state machines makes progress, a corresponding event is returned. Some events must be handled by the user in order to not get stuck in a specific wait state. For example, an incoming request must be responded to by the user although this decision may be postponed or may involve sending a reset message.

If only message parsing is required, the message submodule can be used.

Usage Example

For more detailed examples, refer to the examples in the examples folder.

static CLOCK: SystemClock = SystemClock;

let mut stack = Stack;
let mut receive_buffer = [0_u8; coap_zero::DEFAULT_COAP_MESSAGE_SIZE];

let mut endpoint: CoapEndpoint<'_, Stack, Rng, SystemClock> = CoapEndpoint::try_new(
    TransmissionParameters::default(),
    Rng,
    &CLOCK,
    &mut receive_buffer,
)
.unwrap();

endpoint
    .connect_to_url(&mut stack, "coap://coap.me:5683")
    .unwrap();

let outgoing = endpoint.outgoing();
outgoing
    .schedule_con(
        RequestCode::Get,
        outgoing
            .parse_options("coap://coap.me:5683/hello", Vec::new())
            .unwrap(),
        None,
        Duration::from_secs(5),
    )
    .unwrap();

loop {
    let (incoming_event, outgoing_event, endpoint_event) =
        endpoint.process(&mut stack).unwrap();

    match incoming_event.unwrap() {
        IncomingEvent::Nothing => {
            // Whenever nothing else happens, the Nothing event is generated. Ignore it
            // silently.
        }
        IncomingEvent::Request(_confirmable, _message) => {
            // Handle request
        }
        event => println!("Other incoming event: {event:?}"),
    }

    match outgoing_event.unwrap() {
        OutgoingEvent::Nothing => {}
        OutgoingEvent::Success(response) => println!("Request succeeded: {response:?}"),
        OutgoingEvent::Timeout
        | OutgoingEvent::PiggybackedWrongToken
        | OutgoingEvent::ResetReceived => {
            println!("Request failed");
        }
        event => println!("Other outgoing event: {event:?}"),
    }

    match endpoint_event {
        EndpointEvent::Nothing => {}
        EndpointEvent::MsgFormatErr(_err) => {
            // A message format error was detected. This is reported as an event instead of
            // an error because it is caused by the other endpoint.
            println!("endpoint event MsgFormatErr");
        }
        EndpointEvent::Ping => {
            println!("A ping has been received and a response has been sent");
        }
        EndpointEvent::Unhandled(message) => {
            println!("Unhandled message received: {message:?}");
        }
    }

}

Examples

Multiple examples are provided. They use coap.me as coap test server. The lwm2m example shows the usage in the lightweight m2m context using the Leshan public test server.

  • ping - Sends a ping and terminates when the expected reset was received
  • simple_get - Sends a CON Get and receives a piggy-backed response
  • get_separate_response - Sends a CON Get and received a separate response
  • lwm2m - Registers to the leshan server and supplies the time object current time resource. The registration won't update and will be silently closed after 120 seconds.

License

Open Logistics Foundation License
Version 1.3, January 2023

See the LICENSE file in the top-level directory.

Contact

Fraunhofer IML Embedded Rust Group - embedded-rust@iml.fraunhofer.de

Dependencies

~3MB
~60K SLoC