diff --git a/examples/rust-gcoap/Cargo.lock b/examples/rust-gcoap/Cargo.lock new file mode 100644 index 0000000000..024687b936 Binary files /dev/null and b/examples/rust-gcoap/Cargo.lock differ diff --git a/examples/rust-gcoap/Cargo.toml b/examples/rust-gcoap/Cargo.toml new file mode 100644 index 0000000000..58d66cf76a --- /dev/null +++ b/examples/rust-gcoap/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rust-gcoap" +version = "0.1.0" +authors = ["Christian Amsüss "] +edition = "2018" +resolver = "2" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +riot-wrappers = { version = "^0.7", features = [ "with_coap_message", "with_coap_handler" ] } + +coap-message-demos = { git = "https://gitlab.com/chrysn/coap-message-demos/", default-features = false } +coap-handler-implementations = "0.1" +riot-coap-handler-demos = { git = "https://gitlab.com/etonomy/riot-module-examples/", features = [ "vfs" ] } diff --git a/examples/rust-gcoap/Makefile b/examples/rust-gcoap/Makefile new file mode 100644 index 0000000000..1cdf7b43c1 --- /dev/null +++ b/examples/rust-gcoap/Makefile @@ -0,0 +1,39 @@ +# name of your application +APPLICATION = rust_gcoap + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Basic networking, and gcoap +USEMODULE += gcoap +USEMODULE += netdev_default +USEMODULE += auto_init_gnrc_netif +USEMODULE += gnrc_ipv6_default +USEMODULE += gnrc_icmpv6_echo + +USEMODULE += ztimer +USEMODULE += ztimer_usec +USEMODULE += ztimer_msec +USEMODULE += ztimer_sec + +USEMODULE += vfs +USEMODULE += constfs + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# The name of crate (as per Cargo.toml package name, but with '-' replaced with '_') +APPLICATION_RUST_MODULE = rust_gcoap +BASELIBS += $(APPLICATION_RUST_MODULE).module + +FEATURES_REQUIRED += rust_target + +include $(RIOTBASE)/Makefile.include diff --git a/examples/rust-gcoap/Makefile.ci b/examples/rust-gcoap/Makefile.ci new file mode 100644 index 0000000000..2c83ca0bbb --- /dev/null +++ b/examples/rust-gcoap/Makefile.ci @@ -0,0 +1,8 @@ +BOARD_INSUFFICIENT_MEMORY := \ + blackpill \ + bluepill \ + nucleo-f302r8 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + stm32mp157c-dk2 \ + # diff --git a/examples/rust-gcoap/README.md b/examples/rust-gcoap/README.md new file mode 100644 index 0000000000..5d8a5b42f1 --- /dev/null +++ b/examples/rust-gcoap/README.md @@ -0,0 +1,53 @@ +gcoap used with Rust +==================== + +This is the advanced Rust example; see ../rust-hello-world/ for the basics. + +In extension to the basic example, it shows: + +* C code can be mixed with Rust code easily; any C file is built and linked + as in applications without Rust. + + While it's technically possible to have header files for that code, + it is easier (and likewise often done in C applications) + to just translate the entry function's signature manually, + as is done with the `do_vfs_init()` function. + +* Code of Rust applications can be spread out into modules, + even if it builds on RIOT components. + + The CoAP handler built in the main function + combines generic CoAP components (from `coap_message_demos`) + with RIOT specific components (from `riot-coap-handler-demos`). + +* Many features of RIOT are exposed to Rust through the riot-wrappers crate, + which provides safe wrappers around RIOT structures. + + In this example, the abovementioned CoAP handler is run on the gcoap server, + for which the wrappers provide adaptation to the platform independent handler interface. + + Then, ztimer is used to sleep until the network interfaces are expected to be ready. + + Finally, the available network interfaces are iterated over + and queried for their IP addresses, + which makes it easier (in absence of an interactive shell) to find which address CoAP requests can be directed at. + +How to use +---------- + +``` +$ make all flash term +[...] +main(): This is RIOT! (Version: 2022.01-devel-560-g7f8ed-rust-application) +constfs mounted successfully +CoAP server ready; waiting for interfaces to settle before reporting addresses... +Active interface from PID KernelPID(6) ("gnrc_netdev_tap") + Address fe80:0000:0000:0000:1234:56ff:fe78:90ab + Address 2a02:0b18:c13b:8018:1234:56ff:fe78:90ab +``` + +Once that is ready, in a parallel shell, run: + +``` +$ aiocoap-client 'coap://[2a02:0b18:c13b:8018:1234:56ff:fe78:90ab]/.well-known/core' +``` diff --git a/examples/rust-gcoap/src/lib.rs b/examples/rust-gcoap/src/lib.rs new file mode 100644 index 0000000000..0059b4523c --- /dev/null +++ b/examples/rust-gcoap/src/lib.rs @@ -0,0 +1,57 @@ +// Copyright (C) 2020 Christian Amsüss +// +// This file is subject to the terms and conditions of the GNU Lesser +// General Public License v2.1. See the file LICENSE in the top level +// directory for more details. +#![no_std] + +use riot_wrappers::{riot_main, println}; +use riot_wrappers::{gcoap, thread, ztimer, gnrc}; + +use coap_handler_implementations::{ReportingHandlerBuilder, HandlerBuilder}; + +riot_main!(main); + +fn main() { + extern "C" { + fn do_vfs_init(); + } + + unsafe { do_vfs_init() }; + + let handler = coap_message_demos::full_application_tree(None) + .below(&["ps"], riot_coap_handler_demos::ps::ps_tree()) + .below(&["vfs"], riot_coap_handler_demos::vfs::vfs("/const")) + .with_wkc() + ; + let mut handler = riot_wrappers::coap_handler::GcoapHandler(handler); + + let mut listener = gcoap::SingleHandlerListener::new_catch_all(&mut handler); + + gcoap::scope(|greg| { + greg.register(&mut listener); + + println!("CoAP server ready; waiting for interfaces to settle before reporting addresses..."); + + let sectimer = ztimer::ZTimer::sec(); + sectimer.sleep_ticks(2); + + for netif in gnrc::Netif::all() { + println!("Active interface from PID {:?} ({:?})", netif.pid(), netif.pid().get_name().unwrap_or("unnamed")); + match netif.ipv6_addrs() { + Ok(addrs) => { + for a in addrs.addresses() { + println!(" Address {:?}", a); + } + } + _ => { + println!(" Does not support IPv6."); + } + } + } + + // Sending main thread to sleep; can't return or the Gcoap handler would need to be + // deregistered (which it can't). + loop { thread::sleep(); } + }) +} diff --git a/examples/rust-gcoap/vfs.c b/examples/rust-gcoap/vfs.c new file mode 100644 index 0000000000..ce25ee0c51 --- /dev/null +++ b/examples/rust-gcoap/vfs.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * This file demonstrates how C code can be mixed with Rust code in an + * application in an ad-hoc fashion. + */ + +#include +#include "fs/constfs.h" +#include + +#define HELLO_WORLD_CONTENT "Hello World!\n" +#define HELLO_RIOT_CONTENT "Hello RIOT!\n" + +#define LARGE "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" \ + "1234567890---------\n" + +static constfs_file_t constfs_files[] = { + { + .path = "/hello-world", + .size = sizeof(HELLO_WORLD_CONTENT), + .data = (const uint8_t *)HELLO_WORLD_CONTENT, + }, + { + .path = "/hello-riot", + .size = sizeof(HELLO_RIOT_CONTENT), + .data = (const uint8_t *)HELLO_RIOT_CONTENT, + }, + { + .path = "/large", + .size = sizeof(LARGE), + .data = (const uint8_t *)LARGE, + } +}; + +static constfs_t constfs_desc = { + .nfiles = ARRAY_SIZE(constfs_files), + .files = constfs_files, +}; + +static vfs_mount_t const_mount = { + .fs = &constfs_file_system, + .mount_point = "/const", + .private_data = &constfs_desc, +}; + +void do_vfs_init(void) { + int res = vfs_mount(&const_mount); + if (res < 0) { + puts("Error while mounting constfs"); + } + else { + puts("constfs mounted successfully"); + } +}