mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-02-17 13:58:38 +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",
|
"post-spy",
|
||||||
"xtask",
|
"xtask",
|
||||||
]
|
]
|
||||||
|
exclude = ["actor-example"]
|
||||||
|
|
||||||
# do not optimize proc-macro deps or build scripts
|
# do not optimize proc-macro deps or build scripts
|
||||||
[profile.dev.build-override]
|
[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