1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
// Copyright Open Logistics Foundation
//
// Licensed under the Open Logistics Foundation License 1.3.
// For details on the licensing terms, see the LICENSE file.
// SPDX-License-Identifier: OLFL-1.3
//! 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
//!
//! - [RFC 7252 - The Constrained Application Protocol (CoAP)](https://www.rfc-editor.org/rfc/rfc7252)
//! - [RFC 7641 - Observing Resources in the Constrained Application Protocol (CoAP)](https://www.rfc-editor.org/rfc/rfc7641)
//! - [RFC 7959 - Block-Wise Transfers in the Constrained Application Protocol (CoAP)](https://www.rfc-editor.org/rfc/rfc7959)
//! - [RFC 8132 - PATCH and FETCH Methods for the Constrained Application Protocol (CoAP)](https://www.rfc-editor.org/rfc/rfc8132.html)
//!
//! # 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.
//!
//! ```
//! # use coap_zero::endpoint::incoming::IncomingEvent;
//! # use coap_zero::endpoint::outgoing::OutgoingEvent;
//! # use coap_zero::endpoint::{CoapEndpoint, EndpointEvent, TransmissionParameters};
//! # use coap_zero::message::codes::RequestCode;
//! # use core::time::Duration;
//! # use embedded_nal::SocketAddr;
//! # use embedded_timers::clock::{Clock, ClockError, Instant};
//! # use heapless::Vec;
//! # #[derive(Debug)]
//! # struct Rng;
//! # impl embedded_hal::blocking::rng::Read for Rng {
//! # type Error = std::io::Error;
//! # fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
//! # buf[0] = 42;
//! # Ok(())
//! # }
//! # }
//! # #[derive(Debug)]
//! # struct StackError;
//! # struct Socket;
//! # struct Stack;
//! # impl embedded_nal::UdpClientStack for Stack {
//! # type UdpSocket = Socket;
//! # type Error = StackError;
//! # fn socket(&mut self) -> Result<Socket, StackError> {
//! # Ok(Socket)
//! # }
//! # fn connect(
//! # &mut self,
//! # _socket: &mut Socket,
//! # _remote: SocketAddr,
//! # ) -> Result<(), StackError> {
//! # Ok(())
//! # }
//! # fn send(
//! # &mut self,
//! # _socket: &mut Socket,
//! # _buffer: &[u8],
//! # ) -> Result<(), nb::Error<StackError>> {
//! # Ok(())
//! # }
//! # fn receive(
//! # &mut self,
//! # _socket: &mut Socket,
//! # _buffer: &mut [u8],
//! # ) -> Result<(usize, SocketAddr), nb::Error<StackError>> {
//! # Err(nb::Error::WouldBlock)
//! # }
//! # fn close(&mut self, _socket: Socket) -> Result<(), StackError> {
//! # Ok(())
//! # }
//! # }
//! # impl embedded_nal::Dns for Stack {
//! # type Error = StackError;
//! # fn get_host_by_name(
//! # &mut self,
//! # _hostname: &str,
//! # _addr_type: embedded_nal::AddrType,
//! # ) -> nb::Result<embedded_nal::IpAddr, Self::Error> {
//! # use core::str::FromStr;
//! # Ok(embedded_nal::IpAddr::from_str("1.1.1.1").unwrap())
//! # }
//! # fn get_host_by_address(
//! # &mut self,
//! # _addr: embedded_nal::IpAddr,
//! # ) -> nb::Result<heapless::String<256>, Self::Error> {
//! # Ok(heapless::String::from("rust-lang.org"))
//! # }
//! # }
//! # #[derive(Debug)]
//! # struct SystemClock;
//! # impl Clock for SystemClock {
//! # fn try_now(&self) -> Result<Instant, ClockError> {
//! # Ok(Instant::new(0, 0))
//! # }
//! # }
//! 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:?}");
//! }
//! }
//!
//! # break; // Finish test
//! }
//! ```
//!
//! # Examples
//!
//! Multiple examples are provided. They use [coap.me](https://coap.me/) as coap test server. The lwm2m example shows the usage in the lightweight m2m context using the [Leshan](https://leshan.eclipseprojects.io) 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>
/// Default for how many options can be send in a CoAP Message
pub const DEFAULT_MAX_OPTION_COUNT: usize = 8;
/// Default for maximum value size \[Bytes\] of CoAP Options
pub const DEFAULT_MAX_OPTION_SIZE: usize = 32;
/// Default buffer size \[Bytes\] for a CoAP Message according to <https://www.rfc-editor.org/rfc/rfc7252#section-4.6>
pub const DEFAULT_COAP_MESSAGE_SIZE: usize = 1152;