// 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 example attempts to connect to the public leshan lwm2m test server at
//! with the endpoint name "coap_zero_example".
//! After registration, it can be controlled at
//! .
//!
//! It will provide one instance of a time object
//! (3333, )
//! with the only mandatory field "current time". This object can be accessed at
//! .
//!
//! The example will run and reply to a GET on the current time resource.
//! Other requests will be answered with [ServerErrorCode::NotImplemented].
//!
//! After 120 seconds, the logical lwm2m connection will be closed (no update implemented in this example),
//! but the example will still run until terminated by hand.
use std::{
fs::File,
io::Read,
time::{Duration, SystemTime},
};
use coap_zero::{
endpoint::{CoapEndpoint, TransmissionParameters},
message::{
codes::RequestCode,
options::{CoapOption, CoapOptionName},
},
};
use embedded_timers::clock::{Clock, ClockError, Instant};
use heapless::Vec;
use std_embedded_nal::Stack;
#[derive(Debug)]
pub struct Random;
impl embedded_hal::blocking::rng::Read for Random {
type Error = std::io::Error;
fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
File::open("/dev/urandom")?.read_exact(buf)
}
}
#[derive(Debug)]
struct SystemClock;
impl Clock for SystemClock {
fn try_now(&self) -> Result {
SystemTime::now().elapsed().map_err(|_| ClockError::Unknown)
}
}
static CLOCK: SystemClock = SystemClock;
fn main() {
simple_logger::SimpleLogger::new().env().init().unwrap();
let mut stack = Stack::default();
let mut receive_buffer = [0_u8; coap_zero::DEFAULT_COAP_MESSAGE_SIZE];
let mut endpoint: CoapEndpoint<
'_,
Stack,
Random,
SystemClock,
8, // OptionCount
32, // OptionSize
128, // IncomingCommunicationBuffer
128, // OutgoingCommunicationBuffer
//{ coap_zero::DEFAULT_COAP_MESSAGE_SIZE }, // ReceiveBuffer
> = CoapEndpoint::try_new(
TransmissionParameters::default(),
Random {},
&CLOCK,
&mut receive_buffer,
)
.unwrap();
endpoint
.connect_to_url(&mut stack, "coap://leshan.eclipseprojects.io:5683")
.unwrap();
let uri_path = CoapOption {
name: CoapOptionName::UriPath,
value: b"rd",
};
let content_format = CoapOption {
name: CoapOptionName::ContentFormat,
value: &[0x28_u8],
};
let query_lwm2m_version = CoapOption {
name: CoapOptionName::UriQuery,
value: b"lwm2m=1.0",
};
let query_endpoint_name = CoapOption {
name: CoapOptionName::UriQuery,
value: b"ep=coap_zero_example",
};
let query_lifetime = CoapOption {
name: CoapOptionName::UriQuery,
value: b"lt=120",
};
let mut options_vec: heapless::Vec, { coap_zero::DEFAULT_MAX_OPTION_COUNT }> =
heapless::Vec::new();
options_vec.push(uri_path).unwrap();
options_vec.push(content_format).unwrap();
options_vec.push(query_lwm2m_version).unwrap();
options_vec.push(query_endpoint_name).unwrap();
options_vec.push(query_lifetime).unwrap();
let payload = b"3333/0>";
endpoint
.outgoing()
.schedule_con(
RequestCode::Post,
options_vec,
Some(&payload[..]),
Duration::from_secs(5),
)
.unwrap();
loop {
std::thread::sleep(Duration::from_millis(25));
let (in_event, _outgoing, _endpoint) = endpoint.process(&mut stack).unwrap();
use coap_zero::endpoint::incoming::IncomingEvent::*;
match in_event {
Ok(Request(_confirmable, message)) => {
log::info!("incoming state: Received");
let uri_path_segments: std::vec::Vec = message
.options_iter()
.unwrap()
.map(|o| o.unwrap())
.filter(|o| o.name == CoapOptionName::UriPath)
.collect();
// We need to check if we get a Get for 3333/0/5506
// Others will be answered with 5.01 (Not Implemented)
if uri_path_segments.len() == 3
&& uri_path_segments[0].value == b"3333"
&& uri_path_segments[1].value == b"0"
&& uri_path_segments[2].value == b"5506"
{
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
let payload = format!("{}", now.as_secs());
let content_format: CoapOption<'_> = CoapOption {
name: CoapOptionName::ContentFormat,
value: &[0_u8], // Text
};
let mut additional_options: heapless::Vec<
CoapOption<'_>,
{ coap_zero::DEFAULT_MAX_OPTION_COUNT },
> = heapless::Vec::new();
additional_options.push(content_format).unwrap();
endpoint
.incoming()
.schedule_response(
coap_zero::message::codes::SuccessCode::Content.into(),
additional_options,
Some(payload.as_bytes()),
)
.unwrap();
} else {
endpoint
.incoming()
.schedule_response(
coap_zero::message::codes::ServerErrorCode::NotImplemented.into(),
Vec::new(),
None,
)
.unwrap();
}
}
Ok(Nothing) => {}
Ok(ev) => {
log::info!("Event received: {ev:?}");
}
Err(e) => {
log::warn!("Error: {e:?}");
}
}
}
}