diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml
index 1487d1f62..c00738b4c 100644
--- a/.github/workflows/build-deploy-docs.yml
+++ b/.github/workflows/build-deploy-docs.yml
@@ -48,7 +48,7 @@ jobs:
- name: Build rustdoc docs
run: |
- cargo doc -p riot-rs --features no-boards,bench,threading,random,csprng,hwrng
+ cargo doc -p riot-rs --features no-boards,bench,threading,random,csprng,hwrng,coap
echo "" > target/doc/index.html
mkdir -p ./_site/dev/docs/api && mv target/doc/* ./_site/dev/docs/api
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a2346e9a2..7b0be0c01 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -141,7 +141,7 @@ jobs:
args: --verbose --locked --features no-boards -p riot-rs -p riot-rs-boards -p riot-rs-chips -p riot-rs-debug -p riot-rs-embassy -p riot-rs-macros -p riot-rs-random -p riot-rs-rt -p riot-rs-threads -p riot-rs-utils
- name: rustdoc
- run: RUSTDOCFLAGS='-D warnings' cargo doc -p riot-rs --features no-boards,bench,external-interrupts,threading,random,csprng,hwrng
+ run: RUSTDOCFLAGS='-D warnings' cargo doc -p riot-rs --features no-boards,bench,external-interrupts,threading,random,csprng,hwrng,coap,net,usb-ethernet
- name: rustfmt
run: cargo fmt --check --all
diff --git a/Cargo.lock b/Cargo.lock
index 6341c55f6..c74119844 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -453,20 +453,13 @@ dependencies = [
"coap-scroll-ring-server",
"coapcore",
"embassy-executor",
- "embassy-futures",
"embassy-net",
"embassy-time",
- "embedded-io-async",
- "embedded-nal-async",
"embedded-nal-coap",
"heapless 0.8.0",
- "hexlit",
- "lakers",
- "lakers-crypto-rustcrypto",
"riot-rs",
"riot-rs-boards",
"scroll-ring",
- "smoltcp",
"static-alloc",
]
@@ -601,6 +594,9 @@ dependencies = [
"coap-message-implementations",
"coap-message-utils",
"coap-numbers",
+ "embassy-futures",
+ "embedded-nal-async",
+ "embedded-nal-coap",
"heapless 0.8.0",
"hexlit",
"lakers",
@@ -608,6 +604,7 @@ dependencies = [
"liboscore",
"liboscore-msgbackend",
"minicbor 0.23.0",
+ "rand_core",
]
[[package]]
@@ -3406,6 +3403,7 @@ dependencies = [
"linkme",
"riot-rs-bench",
"riot-rs-boards",
+ "riot-rs-coap",
"riot-rs-debug",
"riot-rs-embassy",
"riot-rs-macros",
@@ -3455,6 +3453,23 @@ dependencies = [
"stm32f4xx",
]
+[[package]]
+name = "riot-rs-coap"
+version = "0.1.0"
+dependencies = [
+ "coap-handler",
+ "coapcore",
+ "embedded-io-async",
+ "embedded-nal-async",
+ "embedded-nal-coap",
+ "hexlit",
+ "lakers",
+ "lakers-crypto-rustcrypto",
+ "riot-rs-embassy",
+ "riot-rs-random",
+ "smoltcp",
+]
+
[[package]]
name = "riot-rs-debug"
version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index 21be2bd67..5eab87262 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,6 +11,7 @@ members = [
"src/riot-rs-boards/nrf52",
"src/riot-rs-boards/nrf52840dk",
"src/riot-rs-chips",
+ "src/riot-rs-coap",
"src/riot-rs-debug",
"src/riot-rs-macros",
"src/riot-rs-random",
diff --git a/examples/coap/Cargo.toml b/examples/coap/Cargo.toml
index 51d18f8a0..5b7daef03 100644
--- a/examples/coap/Cargo.toml
+++ b/examples/coap/Cargo.toml
@@ -13,10 +13,11 @@ workspace = true
embassy-executor = { workspace = true, default-features = false }
embassy-net = { workspace = true, features = ["udp"] }
embassy-time = { workspace = true, default-features = false }
-embedded-io-async = "0.6.1"
heapless = { workspace = true }
riot-rs = { path = "../../src/riot-rs", features = [
"override-network-config",
+ "coap",
+ # to be removed later once coap pulls them in
"random",
"csprng",
] }
@@ -24,29 +25,14 @@ riot-rs-boards = { path = "../../src/riot-rs-boards" }
coapcore.path = "../../src/lib/coapcore/"
-# for the udp_nal mod
-embedded-nal-async = "0.7"
-# actually patched with https://github.com/smoltcp-rs/smoltcp/pull/904 but
-# patch.crates-io takes care of that
-smoltcp = { version = "0.11", default-features = false }
embedded-nal-coap = "0.1.0-alpha.2"
coap-request = "0.2.0-alpha.2"
coap-message = "0.3.2"
-embassy-futures = "0.1.1"
coap-message-demos = { version = "0.4.0", default-features = false }
coap-request-implementations = "0.1.0-alpha.4"
-lakers = { version = "0.6.0", default-features = false }
-lakers-crypto-rustcrypto = "0.6.0"
coap-handler = "0.2.0"
coap-handler-implementations = "0.5.0"
-hexlit = "0.5.5"
static-alloc = "0.2.5"
coap-scroll-ring-server = "0.2.0"
scroll-ring = "0.1.1"
-
-[features]
-default = ["proto-ipv4"] # shame
-# actually embedded-nal features, we have to match them here while developing udp_nal in here
-proto-ipv4 = []
-proto-ipv6 = []
diff --git a/examples/coap/src/main.rs b/examples/coap/src/main.rs
index 1d4c42620..b3efd412c 100644
--- a/examples/coap/src/main.rs
+++ b/examples/coap/src/main.rs
@@ -3,15 +3,9 @@
#![feature(type_alias_impl_trait)]
#![feature(used_with_arg)]
-use riot_rs::{debug::log::*, embassy::network};
+use core::fmt::Write;
-use embassy_net::udp::{PacketMetadata, UdpSocket};
-use embedded_nal_coap::TransportError;
-
-// Moving work from https://github.com/embassy-rs/embassy/pull/2519 in here for the time being
-mod udp_nal;
-
-use coapcore::seccontext;
+use riot_rs::embassy::embassy_net;
// because coapcore depends on it temporarily
extern crate alloc;
@@ -20,36 +14,6 @@ use static_alloc::Bump;
#[global_allocator]
static A: Bump<[u8; 1 << 16]> = Bump::uninit();
-#[riot_rs::task(autostart)]
-async fn coap_run() {
- let stack = network::network_stack().await.unwrap();
-
- // FIXME trim to CoAP requirements
- let mut rx_meta = [PacketMetadata::EMPTY; 16];
- let mut rx_buffer = [0; 4096];
- let mut tx_meta = [PacketMetadata::EMPTY; 16];
- let mut tx_buffer = [0; 4096];
-
- let socket = UdpSocket::new(
- stack,
- &mut rx_meta,
- &mut rx_buffer,
- &mut tx_meta,
- &mut tx_buffer,
- );
-
- info!("Starting up CoAP server");
-
- // Can't that even bind to the Any address??
- // let local_any = "0.0.0.0:5683".parse().unwrap(); // shame
- let local_any = "10.42.0.61:5683".parse().unwrap(); // shame
- let unconnected = udp_nal::UnconnectedUdp::bind_multiple(socket, local_any)
- .await
- .unwrap();
-
- run(unconnected).await;
-}
-
// FIXME: So far, this is necessary boiler plate; see ../README.md#networking for details
#[riot_rs::config(network)]
fn network_config() -> embassy_net::Config {
@@ -62,104 +26,88 @@ fn network_config() -> embassy_net::Config {
})
}
-// Rest is from coap-message-demos/examples/std_embedded_nal_coap.rs
+// This is adjusted from coap-message-demos/examples/std_embedded_nal_coap.rs
-/// This function works on *any* UdpFullStack, including embedded ones -- only main() is what makes
-/// this use POSIX sockets. (It does make use of a std based RNG, but that could be passed in just
-/// as well for no_std operation).
-async fn run(mut sock: S)
-where
- S: embedded_nal_async::UnconnectedUdp,
-{
+// FIXME: Why doesn't scroll_ring provide that?
+#[derive(Clone)]
+struct Stdout<'a, const N: usize>(&'a scroll_ring::Buffer);
+impl<'a, const N: usize> Write for Stdout<'a, N> {
+ fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
+ self.0.write(s.as_bytes());
+ Ok(())
+ }
+}
+
+#[riot_rs::task(autostart)]
+async fn run() {
use coap_handler_implementations::{HandlerBuilder, ReportingHandlerBuilder};
let log = None;
let buffer = scroll_ring::Buffer::<512>::default();
- // FIXME: Why doesn't scroll_ring provide that?
- struct Stdout<'a>(&'a scroll_ring::Buffer<512>);
- impl<'a> core::fmt::Write for Stdout<'a> {
- fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
- self.0.write(s.as_bytes());
- Ok(())
- }
- }
let mut stdout = Stdout(&buffer);
- use core::fmt::Write;
writeln!(stdout, "We have our own stdout now.").unwrap();
writeln!(stdout, "With rings and atomics.").unwrap();
- use hexlit::hex;
- const R: &[u8] = &hex!("72cc4761dbd4c78f758931aa589d348d1ef874a7e303ede2f140dcf3e6aa4aac");
- let own_identity = (
- &lakers::CredentialRPK::new(lakers::EdhocMessageBuffer::new_from_slice(&hex!("A2026008A101A5010202410A2001215820BBC34960526EA4D32E940CAD2A234148DDC21791A12AFBCBAC93622046DD44F02258204519E257236B2A0CE2023F0931F1F386CA7AFDA64FCDE0108C224C51EABF6072")).expect("Credential should be small enough")).expect("Credential should be processable"),
- R,
- );
-
- let mut handler = coap_message_demos::full_application_tree(log)
+ let handler = coap_message_demos::full_application_tree(log)
.at(
&["stdout"],
coap_scroll_ring_server::BufferHandler::new(&buffer),
)
.with_wkc();
- let mut handler = seccontext::OscoreEdhocHandler::new(own_identity, handler, stdout, || {
- lakers_crypto_rustcrypto::Crypto::new(riot_rs::random::crypto_rng())
- });
-
- info!("Server is ready.");
-
- let coap = embedded_nal_coap::CoAPShared::<3>::new();
- let (client, server) = coap.split();
-
- // going with an embassy_futures join instead of an async_std::task::spawn b/c CoAPShared is not
- // Sync, and async_std expects to work in multiple threads
- embassy_futures::join::join(
- async {
- server
- .run(&mut sock, &mut handler, &mut riot_rs::random::fast_rng())
- .await
- .expect("UDP error")
- },
- run_client_operations(client),
- )
- .await;
+ writeln!(stdout, "Server is ready.").unwrap();
+
+ riot_rs::coap::coap_task(handler, Client(stdout.clone()), &mut stdout).await;
}
-/// In parallel to server operation, this function performs some operations as a client.
-///
-/// This doubles as an experimentation ground for the client side of embedded_nal_coap and
-/// coap-request in general.
-async fn run_client_operations(
- client: embedded_nal_coap::CoAPRuntimeClient<'_, N>,
-) {
- // shame
- let addr = "10.42.0.1:1234";
- let demoserver = addr.clone().parse().unwrap();
-
- use coap_request::Stack;
- info!("Sending GET to {}...", addr);
- let response = client
- .to(demoserver)
- .request(
- coap_request_implementations::Code::get()
- .with_path("/other/separate")
- .processing_response_payload_through(|p| {
- info!("Got payload {:?}", p);
- }),
- )
- .await;
- info!("Response {:?}", response.map_err(|_| "TransportError"));
-
- let req = coap_request_implementations::Code::post().with_path("/uppercase");
-
- info!("Sending POST...");
- let mut response = client.to(demoserver);
- let response = response.request(
- req.with_request_payload_slice(b"Set time to 1955-11-05")
- .processing_response_payload_through(|p| {
- info!("Uppercase is {}", core::str::from_utf8(p).unwrap())
- }),
- );
- let response = response.await;
- info!("Response {:?}", response.map_err(|_| "TransportError"));
+struct Client(W);
+
+impl Client {
+ async fn run_logging(
+ mut self,
+ client: embedded_nal_coap::CoAPRuntimeClient<'_, 3>,
+ ) -> Result<(), &'static str> {
+ // shame
+ let demoserver = "10.42.0.1:1234"
+ .parse()
+ .map_err(|_| "Error parsing demo server address")?;
+
+ use coap_request::Stack;
+ writeln!(self.0, "Sending GET to {}...", demoserver).unwrap();
+
+ let response = client
+ .to(demoserver)
+ .request(
+ coap_request_implementations::Code::get()
+ .with_path("/other/separate")
+ .processing_response_payload_through(|p| {
+ writeln!(
+ self.0,
+ "Got payload {:?} length {}",
+ &p[..core::cmp::min(10, p.len())],
+ p.len()
+ )
+ .unwrap();
+ }),
+ )
+ .await
+ .map_err(|_| "Error while trying to GET /other/separate")?;
+ writeln!(self.0, "Response {:?}", response).unwrap();
+
+ Ok(())
+ }
+}
+
+impl coapcore::ClientRunner<3> for Client {
+ /// In parallel to server operation, this function performs some operations as a client.
+ ///
+ /// This doubles as an experimentation ground for the client side of embedded_nal_coap and
+ /// coap-request in general.
+ async fn run(self, client: embedded_nal_coap::CoAPRuntimeClient<'_, 3>) {
+ let mut stdout = self.0.clone();
+ match self.run_logging(client).await {
+ Ok(_) => writeln!(stdout, "Client process completed").unwrap(),
+ Err(e) => writeln!(stdout, "Client process erred out: {e}").unwrap(),
+ }
+ }
}
diff --git a/src/lib/coapcore/Cargo.toml b/src/lib/coapcore/Cargo.toml
index 939ad7e0e..d6864658b 100644
--- a/src/lib/coapcore/Cargo.toml
+++ b/src/lib/coapcore/Cargo.toml
@@ -17,7 +17,14 @@ workspace = true
# public
coap-handler = "0.2.0"
coap-message = "0.3.2"
+# public because we take a credential
lakers = { version = "0.6.0", default-features = false }
+# public because the callback provides a type from there
+embedded-nal-coap = "0.1.0-alpha.2"
+# public because we take a socket
+embedded-nal-async = "0.7"
+# public because we take a RngCor
+rand_core = "0.6.4"
# private
arrayvec = { version = "0.7.4", default-features = false }
@@ -32,3 +39,4 @@ liboscore-msgbackend = { git = "https://gitlab.com/oscore/liboscore/", features
], rev = "e7a4ecd037cbb9c7f085047fec5896f4bdc68d50" }
minicbor = "0.23.0"
heapless = "0.8.0"
+embassy-futures = "0.1.1"
diff --git a/src/lib/coapcore/src/lib.rs b/src/lib/coapcore/src/lib.rs
index 049a19300..0b0470546 100644
--- a/src/lib/coapcore/src/lib.rs
+++ b/src/lib/coapcore/src/lib.rs
@@ -5,11 +5,9 @@
//! the application as an `embedded-nal` socket, and processes CoAP along with its security
//! components OSCORE and EDHOC before passing on authorized requests to the application.
//!
-//! The crate is under heavy development: Its API is in flux, and so far it does not yet provide
-//! the CoAP server itself, but merely a middleware. (Providing the full CoAP will be a requirement
-//! for at least as long as the OSCORE component is tightly coupled to a particular implementation
-//! of [`coap-message`]).
+//! The crate is under heavy development.
#![no_std]
+#![feature(lint_reasons)]
// Might warrant a standalone crate at some point
//
@@ -17,3 +15,39 @@
// anyway)
pub mod oluru;
pub mod seccontext;
+
+// This is a trait because I haven't managed to work out any lifetimes for a single client
+// callback; tried
+// pub async fn coap_task(
+// sock: &mut impl embedded_nal_async::UnconnectedUdp,
+// handler: &mut impl coap_handler::Handler,
+// rng: &mut impl rand_core::RngCore,
+// run_client_operations: ClientOps,
+// ) where
+// for<'a> ClientOps: Fn(embedded_nal_coap::CoAPRuntimeClient<'a, 3>) -> ClientFuture,
+// ClientFuture: core::future::Future