diff --git a/Cargo.toml b/Cargo.toml index fecb0f3..2391b7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ ] [workspace.package] -version = "0.3.0" +version = "0.3.1" edition = "2021" repository = "https://git.bfpower.io/BFPOWER/physical" readme = "README.md" @@ -76,6 +76,9 @@ version = "0.1.*" [workspace.dependencies.embassy-executor] version = "0.5.*" features = ["defmt", "arch-cortex-m", "integrated-timers", "executor-interrupt", "executor-thread"] +[workspace.dependencies.embassy-usb] +version = "0.2.*" +features = ["defmt"] [workspace.dependencies.embassy-stm32] version = "0.1.*" features = ["defmt", "unstable-pac"] diff --git a/README.md b/README.md index 67a396e..5c59a00 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,5 @@ The main concepts of Physical are: * Node: A node hosts peripherals. A node can have a commander but does not need one. A node can ignore or even override commands from the commander. In a complex system, nodes are intended to be kept simple, less likely to encounter an error than the commander, and in some cases should check for obvious problems in commands from the - commander. -* Commander: A commander hosts nodes. It is possible for a device to be both a node and a commander at the same time, - although it may not be the best idea to make such a setup. There is no concept of nesting commanders built into - Physical. If some kind of abstraction for a computer that commands multiple commanders, which command nodes, is - necessary, it should be made for that specific application. \ No newline at end of file + commander. Node can also communicate with other nodes. +* Commander: A commander hosts nodes. It performs long running computations and directs nodes based on the results. \ No newline at end of file diff --git a/node/Cargo.toml b/node/Cargo.toml index 9f2d8ff..1e67d5a 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -7,6 +7,11 @@ repository.workspace = true readme.workspace = true license.workspace = true +[features] +comms = [] +usb = ["embassy-usb"] +stm32 = ["embassy-stm32", "physical/stm32"] + [dependencies.physical] path = ".." [dependencies.embedded-hal] @@ -17,3 +22,9 @@ workspace = true workspace = true [dependencies.uom] workspace = true +[dependencies.embassy-stm32] +workspace = true +optional = true +[dependencies.embassy-usb] +workspace = true +optional = true diff --git a/node/src/comms.rs b/node/src/comms.rs new file mode 100644 index 0000000..d08b29b --- /dev/null +++ b/node/src/comms.rs @@ -0,0 +1,17 @@ +pub trait Sender { + async fn send(&mut self, msg: &[u8]) -> Result<(), Reset>; +} + +pub trait Receiver { + async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Reset>; +} + +//TODO: Replace with ! +pub struct Never; + +/// Communication errors indicates either: +/// Our connection was already disconnected, in which case we should reset and wait for new connection to made. +/// or +/// There was an unexpected, irrecoverable error in communication, in which case we don't want to enter a terminal error +/// safe mode, because there is no indication the actual control is broken, so all we can really do is reset the connection. +pub struct Reset; \ No newline at end of file diff --git a/node/src/lib.rs b/node/src/lib.rs index 5439644..409644a 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -1,3 +1,8 @@ #![no_std] +#[cfg(feature = "comms")] +pub mod comms; +#[cfg(feature = "stm32")] +pub mod stm32; + pub use physical::CriticalError; \ No newline at end of file diff --git a/node/src/stm32/mod.rs b/node/src/stm32/mod.rs new file mode 100644 index 0000000..5ff0b93 --- /dev/null +++ b/node/src/stm32/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "usb")] +pub mod usb; \ No newline at end of file diff --git a/node/src/stm32/usb.rs b/node/src/stm32/usb.rs new file mode 100644 index 0000000..4d5991f --- /dev/null +++ b/node/src/stm32/usb.rs @@ -0,0 +1,25 @@ +use crate::comms; +use embassy_stm32::peripherals::USB_OTG_FS; +use embassy_stm32::usb_otg::{Driver, Endpoint, In, Out}; +use embassy_usb::driver::{EndpointIn, EndpointOut}; +use embassy_usb::UsbDevice; + +pub type TypedUSB = UsbDevice<'static, Driver<'static, USB_OTG_FS>>; +pub type TypedInterIn = Endpoint<'static, USB_OTG_FS, In>; +pub type TypedInterOut = Endpoint<'static, USB_OTG_FS, Out>; + +impl comms::Sender for TypedInterIn { + async fn send(&mut self, msg: &[u8]) -> Result<(), comms::Reset> { + self.write(msg).await.map_err(|_| comms::Reset) + } +} + +impl comms::Receiver for TypedInterOut { + async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), comms::Reset> { + // This should be OK because all our messages are smaller than a single packet. + self.read(buffer) + .await + .map(|_| ()) + .map_err(|_| comms::Reset) + } +}