mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-27 14:04:56 +01:00
add split-crate example
This commit is contained in:
parent
a58be575cb
commit
11694e134f
11 changed files with 368 additions and 0 deletions
|
@ -57,6 +57,7 @@ members = [
|
|||
"post-spy",
|
||||
"xtask",
|
||||
]
|
||||
exclude = ["actor-example"]
|
||||
|
||||
# do not optimize proc-macro deps or build scripts
|
||||
[profile.dev.build-override]
|
||||
|
|
1
actor-example/.gitignore
vendored
Normal file
1
actor-example/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
target
|
1
actor-example/README.md
Normal file
1
actor-example/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
Should live in rtic-rs/rtic-examples repo
|
10
actor-example/actors/Cargo.toml
Normal file
10
actor-example/actors/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "actors"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies.rtic-actor-traits]
|
||||
path = "../../actor-traits"
|
||||
|
||||
[dev-dependencies.rtic-post-spy]
|
||||
path = "../../post-spy"
|
84
actor-example/actors/src/fake_temperature_sensor.rs
Normal file
84
actor-example/actors/src/fake_temperature_sensor.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use rtic_actor_traits::Post;
|
||||
|
||||
use crate::TemperatureReadingCelsius;
|
||||
|
||||
pub struct FakeTemperatureSensor<P>
|
||||
where
|
||||
P: Post<TemperatureReadingCelsius>,
|
||||
{
|
||||
delta: i32,
|
||||
outbox: P,
|
||||
temperature: i32,
|
||||
}
|
||||
|
||||
// a real temperature sensor would use the embedded-hal traits (e.g. I2C) or some higher level trait
|
||||
impl<P> FakeTemperatureSensor<P>
|
||||
where
|
||||
P: Post<TemperatureReadingCelsius>,
|
||||
{
|
||||
pub fn new(outbox: P, initial_temperature: i32, delta: i32) -> Self {
|
||||
Self {
|
||||
delta,
|
||||
outbox,
|
||||
temperature: initial_temperature,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self) {
|
||||
self.outbox
|
||||
.post(TemperatureReadingCelsius(self.temperature))
|
||||
.expect("OOM");
|
||||
self.temperature += self.delta;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rtic_post_spy::PostSpy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn on_read_it_posts_reading() {
|
||||
let mut sensor = FakeTemperatureSensor::new(PostSpy::default(), 0, 0);
|
||||
sensor.read();
|
||||
|
||||
let spy = sensor.outbox;
|
||||
let posted_messages = spy.posted_messages::<TemperatureReadingCelsius>();
|
||||
assert_eq!(1, posted_messages.count());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reading_starts_at_initial_temperature() {
|
||||
let initial_temperature = 1;
|
||||
let mut sensor = FakeTemperatureSensor::new(PostSpy::default(), initial_temperature, 0);
|
||||
sensor.read();
|
||||
|
||||
let spy = sensor.outbox;
|
||||
let mut posted_messages = spy.posted_messages::<TemperatureReadingCelsius>();
|
||||
assert_eq!(
|
||||
Some(&TemperatureReadingCelsius(initial_temperature)),
|
||||
posted_messages.next()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reading_changes_by_delta() {
|
||||
let initial_temperature = 42;
|
||||
let delta = 1;
|
||||
let mut sensor = FakeTemperatureSensor::new(PostSpy::default(), initial_temperature, delta);
|
||||
sensor.read();
|
||||
sensor.read();
|
||||
|
||||
let spy = sensor.outbox;
|
||||
let mut posted_messages = spy.posted_messages::<TemperatureReadingCelsius>();
|
||||
assert_eq!(
|
||||
Some(&TemperatureReadingCelsius(initial_temperature)),
|
||||
posted_messages.next()
|
||||
);
|
||||
assert_eq!(
|
||||
Some(&TemperatureReadingCelsius(initial_temperature + delta)),
|
||||
posted_messages.next()
|
||||
);
|
||||
}
|
||||
}
|
14
actor-example/actors/src/lib.rs
Normal file
14
actor-example/actors/src/lib.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
#![no_std]
|
||||
|
||||
mod fake_temperature_sensor;
|
||||
mod temperature_monitor;
|
||||
|
||||
// Actors
|
||||
pub use fake_temperature_sensor::FakeTemperatureSensor;
|
||||
pub use temperature_monitor::TemperatureMonitor;
|
||||
|
||||
// Messages
|
||||
pub struct TemperatureAlert;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TemperatureReadingCelsius(pub i32);
|
63
actor-example/actors/src/temperature_monitor.rs
Normal file
63
actor-example/actors/src/temperature_monitor.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use rtic_actor_traits::{Post, Receive};
|
||||
|
||||
use crate::{TemperatureAlert, TemperatureReadingCelsius};
|
||||
|
||||
pub struct TemperatureMonitor<P>
|
||||
where
|
||||
P: Post<TemperatureAlert>,
|
||||
{
|
||||
outbox: P,
|
||||
threshold: i32,
|
||||
}
|
||||
|
||||
impl<P> TemperatureMonitor<P>
|
||||
where
|
||||
P: Post<TemperatureAlert>,
|
||||
{
|
||||
pub fn new(outbox: P, threshold: i32) -> Self {
|
||||
Self { outbox, threshold }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Receive<TemperatureReadingCelsius> for TemperatureMonitor<P>
|
||||
where
|
||||
P: Post<TemperatureAlert>,
|
||||
{
|
||||
fn receive(&mut self, temperature: TemperatureReadingCelsius) {
|
||||
if temperature.0 >= self.threshold {
|
||||
self.outbox.post(TemperatureAlert).ok().expect("OOM");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rtic_post_spy::PostSpy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn when_temperature_is_above_threshold_it_posts_alert_once() {
|
||||
let mut monitor = TemperatureMonitor::new(PostSpy::default(), 0);
|
||||
|
||||
// manually send a message
|
||||
let message = TemperatureReadingCelsius(1);
|
||||
monitor.receive(message);
|
||||
|
||||
let spy = monitor.outbox;
|
||||
let posted_messages = spy.posted_messages::<TemperatureAlert>();
|
||||
assert_eq!(1, posted_messages.count());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_temperature_is_below_threshold_it_does_not_post_alert() {
|
||||
let mut monitor = TemperatureMonitor::new(PostSpy::default(), 0);
|
||||
|
||||
let message = TemperatureReadingCelsius(-1);
|
||||
monitor.receive(message);
|
||||
|
||||
let spy = monitor.outbox;
|
||||
let posted_messages = spy.posted_messages::<TemperatureAlert>();
|
||||
assert_eq!(0, posted_messages.count());
|
||||
}
|
||||
}
|
14
actor-example/firmware/.cargo/config.toml
Normal file
14
actor-example/firmware/.cargo/config.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
runner = "probe-run --chip nrf52840"
|
||||
rustflags = [
|
||||
"-C", "linker=flip-link",
|
||||
"-C", "link-arg=-Tdefmt.x",
|
||||
"-C", "link-arg=--nmagic",
|
||||
]
|
||||
|
||||
[build]
|
||||
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||
|
||||
[alias]
|
||||
rb = "run --bin"
|
||||
rrb = "run --release --bin"
|
69
actor-example/firmware/Cargo.toml
Normal file
69
actor-example/firmware/Cargo.toml
Normal file
|
@ -0,0 +1,69 @@
|
|||
[package]
|
||||
name = "firmware"
|
||||
edition = "2018"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
actors = { path = "../actors" }
|
||||
cortex-m = "0.7.1"
|
||||
cortex-m-rt = "0.6.13"
|
||||
cortex-m-rtic = { path = "../.." }
|
||||
defmt = "0.2.0"
|
||||
defmt-rtt = "0.2.0"
|
||||
nrf52840-hal = "0.12.2"
|
||||
panic-probe = { version = "0.2.0", features = ["print-defmt"] }
|
||||
rtic-actor-traits = { path = "../../actor-traits" }
|
||||
systick-monotonic = "0.1.0-alpha.0"
|
||||
|
||||
[features]
|
||||
# set logging levels here
|
||||
default = [
|
||||
"defmt-default",
|
||||
# "dependency-a/defmt-trace",
|
||||
]
|
||||
|
||||
# do NOT modify these features
|
||||
defmt-default = []
|
||||
defmt-trace = []
|
||||
defmt-debug = []
|
||||
defmt-info = []
|
||||
defmt-warn = []
|
||||
defmt-error = []
|
||||
|
||||
# cargo build/run
|
||||
[profile.dev]
|
||||
codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = true # <-
|
||||
incremental = false
|
||||
opt-level = 3 # <-
|
||||
overflow-checks = true # <-
|
||||
|
||||
# cargo test
|
||||
[profile.test]
|
||||
codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = true # <-
|
||||
incremental = false
|
||||
opt-level = 3 # <-
|
||||
overflow-checks = true # <-
|
||||
|
||||
# cargo build/run --release
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = false # <-
|
||||
incremental = false
|
||||
lto = 'fat'
|
||||
opt-level = 3 # <-
|
||||
overflow-checks = false # <-
|
||||
|
||||
# cargo test --release
|
||||
[profile.bench]
|
||||
codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = false # <-
|
||||
incremental = false
|
||||
lto = 'fat'
|
||||
opt-level = 3 # <-
|
||||
overflow-checks = false # <-
|
91
actor-example/firmware/src/bin/temperature-monitor.rs
Normal file
91
actor-example/firmware/src/bin/temperature-monitor.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = nrf52840_hal::pac, dispatchers = [RADIO])]
|
||||
mod app {
|
||||
use actors::{
|
||||
FakeTemperatureSensor, TemperatureAlert, TemperatureMonitor, TemperatureReadingCelsius,
|
||||
};
|
||||
use rtic::time::duration::*;
|
||||
use rtic_actor_traits::Receive;
|
||||
use systick_monotonic::Systick;
|
||||
|
||||
// configuration
|
||||
const TEMPERATURE_THRESHOLD: i32 = 37;
|
||||
const INITIAL_FAKE_TEMPERATURE: i32 = 35;
|
||||
const FAKE_TEMPERATURE_DELTA: i32 = 1;
|
||||
|
||||
// app-specific actors
|
||||
struct AlertHandler;
|
||||
|
||||
impl Receive<TemperatureAlert> for AlertHandler {
|
||||
fn receive(&mut self, _: TemperatureAlert) {
|
||||
defmt::error!("temperature alert");
|
||||
firmware::exit()
|
||||
}
|
||||
}
|
||||
|
||||
struct TemperatureTracer;
|
||||
|
||||
impl Receive<TemperatureReadingCelsius> for TemperatureTracer {
|
||||
fn receive(&mut self, reading: TemperatureReadingCelsius) {
|
||||
defmt::trace!("temperature: {} C", reading.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[actors]
|
||||
struct Actors {
|
||||
#[init(AlertHandler)]
|
||||
#[subscribe(TemperatureAlert)]
|
||||
alert_handler: AlertHandler,
|
||||
|
||||
#[subscribe(TemperatureReadingCelsius)] // <- broadcast
|
||||
temperature_monitor: TemperatureMonitor<Poster>,
|
||||
|
||||
#[init(TemperatureTracer)]
|
||||
#[subscribe(TemperatureReadingCelsius)] // <- broadcast
|
||||
temperature_tracer: TemperatureTracer,
|
||||
}
|
||||
|
||||
#[local]
|
||||
struct Local {
|
||||
temperature_sensor: FakeTemperatureSensor<Poster>,
|
||||
}
|
||||
|
||||
#[monotonic(binds = SysTick, default = true)]
|
||||
type Monotonic = Systick<100>; // 100 Hz
|
||||
|
||||
#[init]
|
||||
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics, Actors) {
|
||||
let systick = cx.core.SYST;
|
||||
let mono = Systick::new(systick, 48_000_000);
|
||||
|
||||
let poster = cx.poster;
|
||||
let temperature_monitor = TemperatureMonitor::new(poster, TEMPERATURE_THRESHOLD);
|
||||
let temperature_sensor =
|
||||
FakeTemperatureSensor::new(poster, INITIAL_FAKE_TEMPERATURE, FAKE_TEMPERATURE_DELTA);
|
||||
|
||||
// kick start the system
|
||||
periodic::spawn().expect("OOM");
|
||||
|
||||
(
|
||||
Shared {},
|
||||
Local { temperature_sensor },
|
||||
init::Monotonics(mono),
|
||||
Actors {
|
||||
temperature_monitor,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[task(local = [temperature_sensor])]
|
||||
fn periodic(cx: periodic::Context) {
|
||||
cx.local.temperature_sensor.read();
|
||||
periodic::spawn_after(1.seconds()).expect("OOM");
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct Shared {}
|
||||
}
|
20
actor-example/firmware/src/lib.rs
Normal file
20
actor-example/firmware/src/lib.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
#![no_std]
|
||||
|
||||
use defmt_rtt as _; // global logger
|
||||
use nrf52840_hal as _; // memory layout
|
||||
|
||||
use panic_probe as _;
|
||||
|
||||
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||
#[defmt::panic_handler]
|
||||
fn panic() -> ! {
|
||||
cortex_m::asm::udf()
|
||||
}
|
||||
|
||||
/// Terminates the application and makes `probe-run` exit with exit-code = 0
|
||||
pub fn exit() -> ! {
|
||||
loop {
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue