From a0093161cd92d66751b2be606f736537c07e56c4 Mon Sep 17 00:00:00 2001 From: Zack Date: Mon, 10 Oct 2022 17:33:41 -0700 Subject: [PATCH 01/51] Project setup --- Cargo.toml | 22 ++++++++++++++++++++++ README.md | 25 +++++++++++++------------ master/Cargo.toml | 15 +++++++++++++++ master/src/lib.rs | 0 4 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 Cargo.toml create mode 100644 master/Cargo.toml create mode 100644 master/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b17a8dc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[workspace] +members = [ + "master" +] + +[workspace.package] +version = "0.1.0" +edition = "2021" +repository = "https://git.bfpower.io/BFPOWER/physical" +readme = "README.md" +license = "MIT" + +[workspace.dependencies] +thiserror = "1.0.*" +uom = "0.33.*" + +# Logging +log = "0.4.*" +env_logger = "0.9.*" + +# Async +tokio = "1.21.*" \ No newline at end of file diff --git a/README.md b/README.md index 6d01fc3..729d387 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,16 @@ As this is a broad purpose physical is intended to run on or interact with a wid ## Computer Hardware Layers -* Peripherals - A peripheral is a minimal set of hardware and firmware that directly interfaces with sensors or controllers (in fact it may be a single hardwired sensor or controller). +* Peripherals - A peripheral is a minimal set of hardware and firmware that directly interfaces with sensors or controllers (in fact it may be a single hardwired sensor or controller). Depending on the peripheral, it may be able to run standalone or connected to a master. * Peripherals are always where analog to digital conversion takes place in the case of inputs and digital to analog conversion takes place in the case of outputs. * Peripherals are things that run extremely minimal software, either with no dedicated operating system at all or a minimal RTOS such as [Embassy](https://embassy.dev/). - * Peripherals should do very little data processing, usually directly sending collected data to the host in the case of inputs or adjusting electronics components in response to output settings. Potentially a peripheral could have some very simple logic built in for things that need extremely fast response times such as closing a gate between two evacuated solar collectors when one loses vacuum. - * Physical is designed to work with peripherals running arbitrary firmware, i.e. peripherals do not need to be running any components from physical. However, it will eventually be possible for to make firmware for peripherals using firmware. + * Peripherals should do very little data processing, usually directly sending collected data to the master in the case of inputs or adjusting electronics components in response to output settings. Potentially a peripheral could have some very simple logic built in for things that need extremely fast response times such as closing a gate between two evacuated solar collectors when one loses vacuum. + * Physical on the master device is designed to work with peripherals running arbitrary firmware, i.e. peripherals do not need to be running any components from physical. However, it will eventually be possible for to make firmware for peripherals using physical. -* Hosts - A host is the computer peripherals are connected to. A host may be a fairly low power single board computer like a raspberry pi 0, all the way up to a 128 core supercomputer. - * Hosts are intended to run full fledged multi-process operating systems such as GNU/Linux. - * Hosts are intended to do the heavy lifting of data processing and decision making for the system being controlled. +* Masters - A master is the computer peripherals are connected to. A master may be a fairly low power single board + computer like a raspberry pi 0, all the way up to a 128 core supercomputer. + * Masters are intended to run full fledged multi-process operating systems such as GNU/Linux but could potentially be run on a fairly lightweight RTOS as well. They do need to run in an environment with Rust std support. + * Masters are intended to do the heavy lifting of data processing and decision making for the system being controlled. ## Software Abstractions @@ -28,7 +29,7 @@ As this is a broad purpose physical is intended to run on or interact with a wid * Transformation - Some way of transforming data coming from inputs and going to outputs. E.g. transforming an analog input to a type K thermocouple. Transformations are for I/O where many different physical sensors or devices could be plugged into an input or output and need some algorithm applied to the direct data for it to be useful. E.g. analog input reads voltage whether a thermocouple or pressure sensor is connected to it. Transformation will transform the voltage to temperature or pressure. * Peripheral - Software representation of what's described in "Computer Hardware Layers". * Peripherals host inputs and outputs. -* Connection - Peripherals could be connected to the host in various ways (SPI, I²C, USB, etc.) different connection methods must be supported in physical. +* Connection - Peripherals could be connected to the master in various ways (SPI, I²C, USB, etc.) different connection methods must be supported in physical. ## Device Builder @@ -39,14 +40,14 @@ As this is a broad purpose physical is intended to run on or interact with a wid ## Roadmap -- [ ] Minimal implementation of physical for hosts. Think about what components can be separated out and reused for peripherals. +- [ ] Minimal implementation of physical for masters. Think about what components can be separated out and reused for peripherals. - [ ] Add support for more peripherals (basically aiming to add support for what's needed for early BFPOWER systems). - [ ] Common abstractions for system control (recording, PID control, etc.) - [ ] Implementation of physical running directly on peripherals. -- [ ] Kotlin embedded DSL and general API to make simple programs to be run on a physical host (optional). -- [ ] GUI to be run on a physical host to do simple data collection, control, and analysis. +- [ ] Kotlin embedded DSL and general API to make simple programs to be run on a physical master (optional). +- [ ] GUI to be run on a physical master to do simple data collection, control, and analysis. ## Questions -* Nested hosts - Should there be abstractions built in to physical for setting one host to be the master of another. - * Leaning towards not having this. Would be better to have slightly more sophisticated peripherals running a RTOS then to have the added complexity of nested hosts. \ No newline at end of file +* Nested masters - Should there be abstractions built in to physical for setting one master to be the master of another. + * Leaning towards not having this. Would be better to have slightly more sophisticated peripherals running a RTOS then to have the added complexity of nested masters. \ No newline at end of file diff --git a/master/Cargo.toml b/master/Cargo.toml new file mode 100644 index 0000000..1ee4f77 --- /dev/null +++ b/master/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "physical-master" +description = "Physical is a library for interacting with the physical world. The master physical computer is the computer peripherals are connected to and is intended to do the heavy lifting of data processing and decision making for the system being controlled" +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies] +thiserror.workspace = true +uom.workspace = true +log.workspace = true +env_logger.workspace = true +tokio = { workspace = true, features = ["full"] } \ No newline at end of file diff --git a/master/src/lib.rs b/master/src/lib.rs new file mode 100644 index 0000000..e69de29 -- 2.43.0 From 04007afae01a01278fc1bff7b807b003b8b42fb0 Mon Sep 17 00:00:00 2001 From: Zack Date: Thu, 13 Oct 2022 07:14:36 -0700 Subject: [PATCH 02/51] Changed physical master to be the root package of Physical and not a workspace member. --- Cargo.toml | 25 +++++++++++++++++-------- master/Cargo.toml | 15 --------------- {master/src => src}/lib.rs | 0 3 files changed, 17 insertions(+), 23 deletions(-) delete mode 100644 master/Cargo.toml rename {master/src => src}/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index b17a8dc..3d5b1f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,3 @@ -[workspace] -members = [ - "master" -] - [workspace.package] version = "0.1.0" edition = "2021" @@ -13,10 +8,24 @@ license = "MIT" [workspace.dependencies] thiserror = "1.0.*" uom = "0.33.*" - # Logging log = "0.4.*" env_logger = "0.9.*" - # Async -tokio = "1.21.*" \ No newline at end of file +tokio = "1.21.*" + +[package] +name = "physical" +description = "Physical is a library for interacting with the physical world." +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies] +thiserror.workspace = true +uom.workspace = true +log.workspace = true +env_logger.workspace = true +tokio = { workspace = true, features = ["full"] } \ No newline at end of file diff --git a/master/Cargo.toml b/master/Cargo.toml deleted file mode 100644 index 1ee4f77..0000000 --- a/master/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "physical-master" -description = "Physical is a library for interacting with the physical world. The master physical computer is the computer peripherals are connected to and is intended to do the heavy lifting of data processing and decision making for the system being controlled" -version.workspace = true -edition.workspace = true -repository.workspace = true -readme.workspace = true -license.workspace = true - -[dependencies] -thiserror.workspace = true -uom.workspace = true -log.workspace = true -env_logger.workspace = true -tokio = { workspace = true, features = ["full"] } \ No newline at end of file diff --git a/master/src/lib.rs b/src/lib.rs similarity index 100% rename from master/src/lib.rs rename to src/lib.rs -- 2.43.0 From 1a448fcea03224438f07f2314258224fadd1e8d2 Mon Sep 17 00:00:00 2001 From: Zack Date: Wed, 26 Oct 2022 14:23:38 -0700 Subject: [PATCH 03/51] Switch from tokio to smol libraries. --- Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3d5b1f7..5dc8ec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,8 @@ uom = "0.33.*" log = "0.4.*" env_logger = "0.9.*" # Async -tokio = "1.21.*" +futures-lite = "1.12.*" +async-io = "1.9.*" [package] name = "physical" @@ -28,4 +29,5 @@ thiserror.workspace = true uom.workspace = true log.workspace = true env_logger.workspace = true -tokio = { workspace = true, features = ["full"] } \ No newline at end of file +futures-lite.workspace = true +async-io.workspace = true \ No newline at end of file -- 2.43.0 From ea214fe7d70a9495f0d623a5f39c2ba921de3f26 Mon Sep 17 00:00:00 2001 From: Zack Date: Mon, 19 Dec 2022 12:15:48 -0800 Subject: [PATCH 04/51] Switch back to virtual workspace. --- Cargo.toml | 24 ++++++------------------ master/Cargo.toml | 16 ++++++++++++++++ {src => master/src}/lib.rs | 0 3 files changed, 22 insertions(+), 18 deletions(-) create mode 100644 master/Cargo.toml rename {src => master/src}/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 5dc8ec0..3300876 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,8 @@ +[workspace] +members = [ + "master" +] + [workspace.package] version = "0.1.0" edition = "2021" @@ -13,21 +18,4 @@ log = "0.4.*" env_logger = "0.9.*" # Async futures-lite = "1.12.*" -async-io = "1.9.*" - -[package] -name = "physical" -description = "Physical is a library for interacting with the physical world." -version.workspace = true -edition.workspace = true -repository.workspace = true -readme.workspace = true -license.workspace = true - -[dependencies] -thiserror.workspace = true -uom.workspace = true -log.workspace = true -env_logger.workspace = true -futures-lite.workspace = true -async-io.workspace = true \ No newline at end of file +async-io = "1.9.*" \ No newline at end of file diff --git a/master/Cargo.toml b/master/Cargo.toml new file mode 100644 index 0000000..8fe260b --- /dev/null +++ b/master/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "physical" +description = "Physical is a library for interacting with the physical world." +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies] +thiserror.workspace = true +uom.workspace = true +log.workspace = true +env_logger.workspace = true +futures-lite.workspace = true +async-io.workspace = true \ No newline at end of file diff --git a/src/lib.rs b/master/src/lib.rs similarity index 100% rename from src/lib.rs rename to master/src/lib.rs -- 2.43.0 From 9313361cffe59ff0456ad2a86a7a96050c7fe483 Mon Sep 17 00:00:00 2001 From: Zack Date: Fri, 13 Jan 2023 16:53:45 -0800 Subject: [PATCH 05/51] Added examples. --- Cargo.toml | 7 ++++--- examples/playground/Cargo.toml | 20 ++++++++++++++++++++ examples/playground/src/main.rs | 7 +++++++ 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 examples/playground/Cargo.toml create mode 100644 examples/playground/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 3300876..4fc1c93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ - "master" + "master", + "examples/playground" ] [workspace.package] @@ -15,7 +16,7 @@ thiserror = "1.0.*" uom = "0.33.*" # Logging log = "0.4.*" -env_logger = "0.9.*" +env_logger = "0.10.*" # Async futures-lite = "1.12.*" -async-io = "1.9.*" \ No newline at end of file +async-io = "1.12.*" \ No newline at end of file diff --git a/examples/playground/Cargo.toml b/examples/playground/Cargo.toml new file mode 100644 index 0000000..1ecc180 --- /dev/null +++ b/examples/playground/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "playground" +description = """The playground example is a special example meant for unstructured experimentation which can potentially be turned +into an actual example at some point or deleted. It should always be reset to unchanged hello world before the working +branch where the experimentation is happening is merged.""" +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +thiserror.workspace = true +uom.workspace = true +log.workspace = true +env_logger.workspace = true +futures-lite.workspace = true +async-io.workspace = true \ No newline at end of file diff --git a/examples/playground/src/main.rs b/examples/playground/src/main.rs new file mode 100644 index 0000000..964a26c --- /dev/null +++ b/examples/playground/src/main.rs @@ -0,0 +1,7 @@ +//! The playground example is a special example meant for unstructured experimentation which can potentially be turned +//! into an actual example at some point or deleted. It should always be reset to unchanged hello world before the working +//! branch where the experimentation is happening is merged. + +fn main() { + println!("Hello, world!"); +} -- 2.43.0 From d968b34702c367ee9042b0b3fa450a2af98ff09f Mon Sep 17 00:00:00 2001 From: Zack Date: Mon, 16 Jan 2023 17:26:41 -0800 Subject: [PATCH 06/51] Simplified --- Cargo.toml | 20 +++++++++++++-- README.md | 47 ---------------------------------- examples/playground/Cargo.toml | 8 +++--- master/Cargo.toml | 16 ------------ {master/src => src}/lib.rs | 0 5 files changed, 21 insertions(+), 70 deletions(-) delete mode 100644 master/Cargo.toml rename {master/src => src}/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 4fc1c93..1052d89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = [ - "master", "examples/playground" ] @@ -19,4 +18,21 @@ log = "0.4.*" env_logger = "0.10.*" # Async futures-lite = "1.12.*" -async-io = "1.12.*" \ No newline at end of file +async-io = "1.12.*" + +[package] +name = "physical" +description = "Physical is a library for interacting with the physical world." +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies] +thiserror.workspace = true +uom.workspace = true +log.workspace = true +env_logger.workspace = true +futures-lite.workspace = true +async-io.workspace = true \ No newline at end of file diff --git a/README.md b/README.md index 729d387..99cf7ab 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,6 @@ # Physical -# Conceptual Design - Physical is a library for interacting with the physical world from a computer. This can broadly be broken down into two categories: * Collecting and digitizing data from the physical world. * Controlling devices that take physical action. - -As this is a broad purpose physical is intended to run on or interact with a wide range of computer hardware. This broad range of hardware can be categorized into two distinct "layers". - -## Computer Hardware Layers - -* Peripherals - A peripheral is a minimal set of hardware and firmware that directly interfaces with sensors or controllers (in fact it may be a single hardwired sensor or controller). Depending on the peripheral, it may be able to run standalone or connected to a master. - * Peripherals are always where analog to digital conversion takes place in the case of inputs and digital to analog conversion takes place in the case of outputs. - * Peripherals are things that run extremely minimal software, either with no dedicated operating system at all or a minimal RTOS such as [Embassy](https://embassy.dev/). - * Peripherals should do very little data processing, usually directly sending collected data to the master in the case of inputs or adjusting electronics components in response to output settings. Potentially a peripheral could have some very simple logic built in for things that need extremely fast response times such as closing a gate between two evacuated solar collectors when one loses vacuum. - * Physical on the master device is designed to work with peripherals running arbitrary firmware, i.e. peripherals do not need to be running any components from physical. However, it will eventually be possible for to make firmware for peripherals using physical. - -* Masters - A master is the computer peripherals are connected to. A master may be a fairly low power single board - computer like a raspberry pi 0, all the way up to a 128 core supercomputer. - * Masters are intended to run full fledged multi-process operating systems such as GNU/Linux but could potentially be run on a fairly lightweight RTOS as well. They do need to run in an environment with Rust std support. - * Masters are intended to do the heavy lifting of data processing and decision making for the system being controlled. - -## Software Abstractions - -* Input - Individual unit for data collection (e.g. an analog input reads a voltage). -* Output - Individual unit for device control. -* Transformation - Some way of transforming data coming from inputs and going to outputs. E.g. transforming an analog input to a type K thermocouple. Transformations are for I/O where many different physical sensors or devices could be plugged into an input or output and need some algorithm applied to the direct data for it to be useful. E.g. analog input reads voltage whether a thermocouple or pressure sensor is connected to it. Transformation will transform the voltage to temperature or pressure. -* Peripheral - Software representation of what's described in "Computer Hardware Layers". - * Peripherals host inputs and outputs. -* Connection - Peripherals could be connected to the master in various ways (SPI, I²C, USB, etc.) different connection methods must be supported in physical. - -## Device Builder - -* In physical, all configuration must be done ahead of time. That is once the device build phase of the program is complete, there cannot be any change to the configuration. - * Peripherals cannot be added or removed. - * I/O settings cannot be changed. - * The only allowed "change" is to change the value of an output (like setting a digital output to high instead of low). - -## Roadmap - -- [ ] Minimal implementation of physical for masters. Think about what components can be separated out and reused for peripherals. -- [ ] Add support for more peripherals (basically aiming to add support for what's needed for early BFPOWER systems). -- [ ] Common abstractions for system control (recording, PID control, etc.) -- [ ] Implementation of physical running directly on peripherals. -- [ ] Kotlin embedded DSL and general API to make simple programs to be run on a physical master (optional). -- [ ] GUI to be run on a physical master to do simple data collection, control, and analysis. - -## Questions - -* Nested masters - Should there be abstractions built in to physical for setting one master to be the master of another. - * Leaning towards not having this. Would be better to have slightly more sophisticated peripherals running a RTOS then to have the added complexity of nested masters. \ No newline at end of file diff --git a/examples/playground/Cargo.toml b/examples/playground/Cargo.toml index 1ecc180..8f5c298 100644 --- a/examples/playground/Cargo.toml +++ b/examples/playground/Cargo.toml @@ -1,16 +1,14 @@ [package] name = "playground" -description = """The playground example is a special example meant for unstructured experimentation which can potentially be turned -into an actual example at some point or deleted. It should always be reset to unchanged hello world before the working -branch where the experimentation is happening is merged.""" +description = """The playground example is a special example meant for unstructured experimentation which can potentially + be turned into an actual example at some point or deleted. It should always be reset to unchanged hello world before + the working branch where the experimentation is happening is merged.""" version.workspace = true edition.workspace = true repository.workspace = true readme.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] thiserror.workspace = true uom.workspace = true diff --git a/master/Cargo.toml b/master/Cargo.toml deleted file mode 100644 index 8fe260b..0000000 --- a/master/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "physical" -description = "Physical is a library for interacting with the physical world." -version.workspace = true -edition.workspace = true -repository.workspace = true -readme.workspace = true -license.workspace = true - -[dependencies] -thiserror.workspace = true -uom.workspace = true -log.workspace = true -env_logger.workspace = true -futures-lite.workspace = true -async-io.workspace = true \ No newline at end of file diff --git a/master/src/lib.rs b/src/lib.rs similarity index 100% rename from master/src/lib.rs rename to src/lib.rs -- 2.43.0 From 3489b66ed694159f4d2e00d5a0f4681457e4c96b Mon Sep 17 00:00:00 2001 From: Zack Date: Mon, 16 Jan 2023 19:04:11 -0800 Subject: [PATCH 07/51] Structure --- Cargo.toml | 4 ++++ README.md | 9 +++++++++ examples/playground/Cargo.toml | 7 +------ master/Cargo.toml | 13 +++++++++++++ master/src/lib.rs | 14 ++++++++++++++ node/Cargo.toml | 13 +++++++++++++ node/src/lib.rs | 14 ++++++++++++++ 7 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 master/Cargo.toml create mode 100644 master/src/lib.rs create mode 100644 node/Cargo.toml create mode 100644 node/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 1052d89..cd882f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,9 @@ [workspace] members = [ + # Device types + "node", + "master", + # Examples "examples/playground" ] diff --git a/README.md b/README.md index 99cf7ab..85099d5 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,12 @@ Physical is a library for interacting with the physical world from a computer. T * Collecting and digitizing data from the physical world. * Controlling devices that take physical action. + +## Concepts +The main concepts of Physical are: +* Peripheral: A peripheral is a board that hosts physical I/O and usually does analog to digital conversion or + digital to analog conversion. A peripheral cannot function on its own, it must be connected to a node. This is more + narrow than the + definition of a peripheral in embedded systems generally. +* Node: A node hosts peripherals. A node can have a master but does not need one. +* Master: A master hosts nodes. It is possible for a device to be both a node and a master at the same time. \ No newline at end of file diff --git a/examples/playground/Cargo.toml b/examples/playground/Cargo.toml index 8f5c298..33f173c 100644 --- a/examples/playground/Cargo.toml +++ b/examples/playground/Cargo.toml @@ -10,9 +10,4 @@ readme.workspace = true license.workspace = true [dependencies] -thiserror.workspace = true -uom.workspace = true -log.workspace = true -env_logger.workspace = true -futures-lite.workspace = true -async-io.workspace = true \ No newline at end of file +physical = { path = "../.." } \ No newline at end of file diff --git a/master/Cargo.toml b/master/Cargo.toml new file mode 100644 index 0000000..7341c5d --- /dev/null +++ b/master/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "physical-master" +description = "A master hosts nodes." +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +physical = { path = ".." } \ No newline at end of file diff --git a/master/src/lib.rs b/master/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/master/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/node/Cargo.toml b/node/Cargo.toml new file mode 100644 index 0000000..6bbbb9e --- /dev/null +++ b/node/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "physical-node" +description = "A node hosts peripherals." +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +physical = { path = ".." } \ No newline at end of file diff --git a/node/src/lib.rs b/node/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/node/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} -- 2.43.0 From f53ca590dff8e8436466348e78c3d6aa99dddc69 Mon Sep 17 00:00:00 2001 From: Zack Date: Thu, 19 Jan 2023 08:22:32 -0800 Subject: [PATCH 08/51] Dependencies --- Cargo.toml | 5 +---- master/Cargo.toml | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd882f7..aa650d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,4 @@ license.workspace = true [dependencies] thiserror.workspace = true uom.workspace = true -log.workspace = true -env_logger.workspace = true -futures-lite.workspace = true -async-io.workspace = true \ No newline at end of file +log.workspace = true \ No newline at end of file diff --git a/master/Cargo.toml b/master/Cargo.toml index 7341c5d..7a225ff 100644 --- a/master/Cargo.toml +++ b/master/Cargo.toml @@ -10,4 +10,7 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -physical = { path = ".." } \ No newline at end of file +physical = { path = ".." } +env_logger.workspace = true +futures-lite.workspace = true +async-io.workspace = true \ No newline at end of file -- 2.43.0 From 9a32145119d7058b2c067dcf7aeb8e8cc3e8fcd5 Mon Sep 17 00:00:00 2001 From: Zack Date: Fri, 20 Jan 2023 14:58:42 -0800 Subject: [PATCH 09/51] Dependencies --- Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index aa650d7..5063055 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ env_logger = "0.10.*" # Async futures-lite = "1.12.*" async-io = "1.12.*" +# Serialization +parity-scale-codec = "3.2.*" [package] name = "physical" @@ -36,4 +38,5 @@ license.workspace = true [dependencies] thiserror.workspace = true uom.workspace = true -log.workspace = true \ No newline at end of file +log.workspace = true +parity-scale-codec.workspace = true \ No newline at end of file -- 2.43.0 From 02212e99ab562b80829de5d2c6e6b79c0b9d62bb Mon Sep 17 00:00:00 2001 From: Zack Date: Fri, 24 Mar 2023 08:53:28 -0700 Subject: [PATCH 10/51] Dependencies, and readme updates. --- Cargo.toml | 17 ++++++++++------- README.md | 12 ++++++++---- master/Cargo.toml | 2 +- rustfmt.toml | 3 +++ 4 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 rustfmt.toml diff --git a/Cargo.toml b/Cargo.toml index 5063055..07aeec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,16 +15,20 @@ readme = "README.md" license = "MIT" [workspace.dependencies] +##### no-std ##### +# General utility thiserror = "1.0.*" -uom = "0.33.*" +# Units of measurement +uom = "0.34.*" # Logging -log = "0.4.*" -env_logger = "0.10.*" +tracing = "0.1.*" +# Serialization +parity-scale-codec = "3.4.*" + +##### std ##### # Async futures-lite = "1.12.*" -async-io = "1.12.*" -# Serialization -parity-scale-codec = "3.2.*" +async-io = "1.13.*" [package] name = "physical" @@ -38,5 +42,4 @@ license.workspace = true [dependencies] thiserror.workspace = true uom.workspace = true -log.workspace = true parity-scale-codec.workspace = true \ No newline at end of file diff --git a/README.md b/README.md index 85099d5..51b089d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,11 @@ Physical is a library for interacting with the physical world from a computer. T The main concepts of Physical are: * Peripheral: A peripheral is a board that hosts physical I/O and usually does analog to digital conversion or digital to analog conversion. A peripheral cannot function on its own, it must be connected to a node. This is more - narrow than the - definition of a peripheral in embedded systems generally. -* Node: A node hosts peripherals. A node can have a master but does not need one. -* Master: A master hosts nodes. It is possible for a device to be both a node and a master at the same time. \ No newline at end of file + narrow than the definition of a peripheral in embedded systems generally. Peripheral support is done on the basis + of complete boards, not individual components like an ADC. Abstractions for individual components should be made + separately, such as in BFHAL. +* Node: A node hosts peripherals. A node can have a master but does not need one. A node can ignore or even override + commands from the master computer. In a complex system, nodes are intended to be kept simple, less likely to + encounter an error than the master, and in many cases should check for obvious problems in commands from the master. +* Master: A master hosts nodes. It is possible for a device to be both a node and a master at the same time, + although it may not be the best idea to make such a setup. \ No newline at end of file diff --git a/master/Cargo.toml b/master/Cargo.toml index 7a225ff..0e4d485 100644 --- a/master/Cargo.toml +++ b/master/Cargo.toml @@ -11,6 +11,6 @@ license.workspace = true [dependencies] physical = { path = ".." } -env_logger.workspace = true +tracing.workspace = true futures-lite.workspace = true async-io.workspace = true \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3d362b7 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +format_strings=true +wrap_comments=true +comment_width=100 \ No newline at end of file -- 2.43.0 From 2cc2b45f19bfe1eba6b83e22321eca994ae87758 Mon Sep 17 00:00:00 2001 From: Zack Date: Sat, 25 Mar 2023 08:58:21 -0700 Subject: [PATCH 11/51] Dependencies, and readme updates. --- Cargo.toml | 2 +- README.md | 28 +++++++++++++++++----------- {master => commander}/Cargo.toml | 4 ++-- {master => commander}/src/lib.rs | 0 4 files changed, 20 insertions(+), 14 deletions(-) rename {master => commander}/Cargo.toml (77%) rename {master => commander}/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 07aeec2..4602caa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = [ # Device types "node", - "master", + "commander", # Examples "examples/playground" ] diff --git a/README.md b/README.md index 51b089d..f84712c 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,25 @@ # Physical -Physical is a library for interacting with the physical world from a computer. This can broadly be broken down into two categories: +Physical is a library for interacting with the physical world from a computer. This can broadly be broken down into two +categories: * Collecting and digitizing data from the physical world. * Controlling devices that take physical action. ## Concepts + The main concepts of Physical are: -* Peripheral: A peripheral is a board that hosts physical I/O and usually does analog to digital conversion or - digital to analog conversion. A peripheral cannot function on its own, it must be connected to a node. This is more - narrow than the definition of a peripheral in embedded systems generally. Peripheral support is done on the basis - of complete boards, not individual components like an ADC. Abstractions for individual components should be made - separately, such as in BFHAL. -* Node: A node hosts peripherals. A node can have a master but does not need one. A node can ignore or even override - commands from the master computer. In a complex system, nodes are intended to be kept simple, less likely to - encounter an error than the master, and in many cases should check for obvious problems in commands from the master. -* Master: A master hosts nodes. It is possible for a device to be both a node and a master at the same time, - although it may not be the best idea to make such a setup. \ No newline at end of file + +* Peripheral: A peripheral is a board that hosts physical I/O and usually does analog to digital conversion or + digital to analog conversion. A peripheral cannot function on its own, it must be connected to a node. This is more + narrow than the definition of a peripheral in embedded systems generally. Peripheral support is done on the basis + of complete boards, not individual components like an ADC. Abstractions for individual components should be made + separately, such as in BFPOWER drivers. +* 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 diff --git a/master/Cargo.toml b/commander/Cargo.toml similarity index 77% rename from master/Cargo.toml rename to commander/Cargo.toml index 0e4d485..185bae4 100644 --- a/master/Cargo.toml +++ b/commander/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "physical-master" -description = "A master hosts nodes." +name = "physical-commander" +description = "A commander hosts nodes." version.workspace = true edition.workspace = true repository.workspace = true diff --git a/master/src/lib.rs b/commander/src/lib.rs similarity index 100% rename from master/src/lib.rs rename to commander/src/lib.rs -- 2.43.0 From 3719f81b35f1e442b7ff721864256cec475eecf3 Mon Sep 17 00:00:00 2001 From: Zack Date: Mon, 3 Apr 2023 21:38:03 -0700 Subject: [PATCH 12/51] Added CellView type to limit where Cell value can be set from. --- src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e69de29..3a721ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -0,0 +1,15 @@ +#![no_std] + +use core::cell::Cell; + +/// Provides a view only reference to a [Cell]. +/// Useful alternative to `&Cell` when an API wants to control where a [Cell]s value can be set. +#[derive(Copy, Clone)] +pub struct CellView<'a, T: Copy>(&'a Cell); + +impl<'a, T: Copy> CellView<'a, T> { + #[inline] + pub fn get(self) -> T { + self.0.get() + } +} -- 2.43.0 From d9bc0a8da8156e4e8558f0593ccaeb79f3ff4070 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 4 Apr 2023 08:39:44 -0700 Subject: [PATCH 13/51] Moved CellView to own file. --- src/cell.rs | 13 +++++++++++++ src/lib.rs | 14 +------------- 2 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 src/cell.rs diff --git a/src/cell.rs b/src/cell.rs new file mode 100644 index 0000000..0fe56eb --- /dev/null +++ b/src/cell.rs @@ -0,0 +1,13 @@ +use core::cell::Cell; + +/// Provides a view only reference to a [Cell]. +/// Useful alternative to `&Cell` when an API wants to control where a [Cell]s value can be set. +#[derive(Copy, Clone)] +pub struct CellView<'a, T: Copy>(&'a Cell); + +impl<'a, T: Copy> CellView<'a, T> { + #[inline] + pub fn get(self) -> T { + self.0.get() + } +} diff --git a/src/lib.rs b/src/lib.rs index 3a721ff..c72a786 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,3 @@ #![no_std] -use core::cell::Cell; - -/// Provides a view only reference to a [Cell]. -/// Useful alternative to `&Cell` when an API wants to control where a [Cell]s value can be set. -#[derive(Copy, Clone)] -pub struct CellView<'a, T: Copy>(&'a Cell); - -impl<'a, T: Copy> CellView<'a, T> { - #[inline] - pub fn get(self) -> T { - self.0.get() - } -} +mod cell; -- 2.43.0 From feebce2b0718794967ebe2a38dbd2522b087da11 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Sun, 9 Apr 2023 08:03:41 -0700 Subject: [PATCH 14/51] Moved CellView to Node. Dependency layout adjustments. rustfmt modifications. --- Cargo.toml | 31 +++++++++++++++++++++---------- {src => node/src}/cell.rs | 6 ++++++ node/src/lib.rs | 15 +-------------- rustfmt.toml | 7 ++++++- src/lib.rs | 1 - 5 files changed, 34 insertions(+), 26 deletions(-) rename {src => node/src}/cell.rs (72%) diff --git a/Cargo.toml b/Cargo.toml index 4602caa..5e058e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,19 +16,29 @@ license = "MIT" [workspace.dependencies] ##### no-std ##### -# General utility -thiserror = "1.0.*" +# Error handling +[workspace.dependencies.thiserror] +version = "1.0.*" +# Concurrency +[workspace.dependencies.crossbeam] +version = "0.8.*" +default-features = false # Units of measurement -uom = "0.34.*" +[workspace.dependencies.uom] +version = "0.34.*" # Logging -tracing = "0.1.*" +[workspace.dependencies.tracing] +version = "0.1.*" # Serialization -parity-scale-codec = "3.4.*" +[workspace.dependencies.parity-scale-codec] +version = "3.4.*" ##### std ##### # Async -futures-lite = "1.12.*" -async-io = "1.13.*" +[workspace.dependencies.futures-lite] +version = "1.13.*" +[workspace.dependencies.async-io] +version = "1.13.*" [package] name = "physical" @@ -40,6 +50,7 @@ readme.workspace = true license.workspace = true [dependencies] -thiserror.workspace = true -uom.workspace = true -parity-scale-codec.workspace = true \ No newline at end of file +thiserror = { workspace = true } +crossbeam = { workspace = true } +uom = { workspace = true } +parity-scale-codec = { workspace = true } \ No newline at end of file diff --git a/src/cell.rs b/node/src/cell.rs similarity index 72% rename from src/cell.rs rename to node/src/cell.rs index 0fe56eb..8b84137 100644 --- a/src/cell.rs +++ b/node/src/cell.rs @@ -11,3 +11,9 @@ impl<'a, T: Copy> CellView<'a, T> { self.0.get() } } + +impl<'a, T: Copy> From<&'a Cell> for CellView<'a, T> { + fn from(value: &'a Cell) -> Self { + CellView(value) + } +} diff --git a/node/src/lib.rs b/node/src/lib.rs index 7d12d9a..cf06c83 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -1,14 +1 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +mod cell; diff --git a/rustfmt.toml b/rustfmt.toml index 3d362b7..8eb903c 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,8 @@ +imports_granularity="Module" format_strings=true wrap_comments=true -comment_width=100 \ No newline at end of file +match_block_trailing_comma=true +enum_discrim_align_threshold=25 +fn_call_width=100 +comment_width=100 +single_line_if_else_max_width=100 \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index c72a786..1ef6109 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,2 @@ #![no_std] -mod cell; -- 2.43.0 From 5c596dd50e8a901099385e3ae8c183c47efd2a5e Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Sun, 9 Apr 2023 16:49:53 -0700 Subject: [PATCH 15/51] Elided unnecessary lifetime in CellView impl --- node/src/cell.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/cell.rs b/node/src/cell.rs index 8b84137..9526551 100644 --- a/node/src/cell.rs +++ b/node/src/cell.rs @@ -5,7 +5,7 @@ use core::cell::Cell; #[derive(Copy, Clone)] pub struct CellView<'a, T: Copy>(&'a Cell); -impl<'a, T: Copy> CellView<'a, T> { +impl CellView<'_, T> { #[inline] pub fn get(self) -> T { self.0.get() -- 2.43.0 From 2ce1b46d757456b0a2759b3d5e474158350288ed Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 6 Jun 2023 11:06:42 -0700 Subject: [PATCH 16/51] Project setup --- Cargo.toml | 11 ++--------- rustfmt.toml | 16 ++++++++-------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5e058e0..42a51b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,6 @@ license = "MIT" # Error handling [workspace.dependencies.thiserror] version = "1.0.*" -# Concurrency -[workspace.dependencies.crossbeam] -version = "0.8.*" -default-features = false # Units of measurement [workspace.dependencies.uom] version = "0.34.*" @@ -31,7 +27,7 @@ version = "0.34.*" version = "0.1.*" # Serialization [workspace.dependencies.parity-scale-codec] -version = "3.4.*" +version = "3.5.*" ##### std ##### # Async @@ -50,7 +46,4 @@ readme.workspace = true license.workspace = true [dependencies] -thiserror = { workspace = true } -crossbeam = { workspace = true } -uom = { workspace = true } -parity-scale-codec = { workspace = true } \ No newline at end of file +uom = { workspace = true } \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml index 8eb903c..1a2c99f 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,8 +1,8 @@ -imports_granularity="Module" -format_strings=true -wrap_comments=true -match_block_trailing_comma=true -enum_discrim_align_threshold=25 -fn_call_width=100 -comment_width=100 -single_line_if_else_max_width=100 \ No newline at end of file +imports_granularity = "Module" +format_strings = true +wrap_comments = true +match_block_trailing_comma = true +enum_discrim_align_threshold = 25 +fn_call_width = 100 +comment_width = 120 +single_line_if_else_max_width = 100 \ No newline at end of file -- 2.43.0 From b3b746ca642122b6e58db6afb1b9582d5305640a Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Sat, 10 Jun 2023 07:58:33 -0700 Subject: [PATCH 17/51] Project setup --- Cargo.toml | 7 ------- commander/Cargo.toml | 4 +--- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 42a51b6..407368e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,13 +29,6 @@ version = "0.1.*" [workspace.dependencies.parity-scale-codec] version = "3.5.*" -##### std ##### -# Async -[workspace.dependencies.futures-lite] -version = "1.13.*" -[workspace.dependencies.async-io] -version = "1.13.*" - [package] name = "physical" description = "Physical is a library for interacting with the physical world." diff --git a/commander/Cargo.toml b/commander/Cargo.toml index 185bae4..0c727d9 100644 --- a/commander/Cargo.toml +++ b/commander/Cargo.toml @@ -11,6 +11,4 @@ license.workspace = true [dependencies] physical = { path = ".." } -tracing.workspace = true -futures-lite.workspace = true -async-io.workspace = true \ No newline at end of file +tracing.workspace = true \ No newline at end of file -- 2.43.0 From 8355cc1f249f833476462f4eb970f76749576cd1 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Mon, 12 Jun 2023 09:33:01 -0700 Subject: [PATCH 18/51] Project setup --- Cargo.toml | 97 +++++++++++++++++++++++++++++++++++++++++--- commander/Cargo.toml | 9 ++-- node/Cargo.toml | 16 ++++++-- rust-toolchain.toml | 14 +++++++ 4 files changed, 122 insertions(+), 14 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/Cargo.toml b/Cargo.toml index 407368e..58382fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +#--------------------------------------------------------------------------------------------------------------------- +#----- Workspace ------------------------ +#--------------------------------------------------------------------------------------------------------------------- [workspace] members = [ # Device types @@ -15,20 +18,84 @@ readme = "README.md" license = "MIT" [workspace.dependencies] -##### no-std ##### -# Error handling -[workspace.dependencies.thiserror] -version = "1.0.*" +#----- no-std ---------------------------------- +# Math +[workspace.dependencies.libm] +version = "0.2.*" # Units of measurement [workspace.dependencies.uom] version = "0.34.*" +default-features = false +features = ["f32", "si"] # Logging [workspace.dependencies.tracing] version = "0.1.*" +[workspace.dependencies.defmt] +version = "0.3.*" +[workspace.dependencies.defmt-rtt] +version = "0.4.*" # Serialization [workspace.dependencies.parity-scale-codec] version = "3.5.*" +# Embedded-HAL +[workspace.dependencies.embedded-hal] +version = "1.0.0-alpha.10" +[workspace.dependencies.embedded-hal-async] +version = "0.2.0-alpha.1" +# Memory +[workspace.dependencies.static_cell] +version = "1.1.*" +[workspace.dependencies.heapless] +version = "0.7.*" +# Other embedded utilities +[workspace.dependencies.cortex-m] +version = "0.7.*" +[workspace.dependencies.cortex-m-rt] +version = "0.7.*" +[workspace.dependencies.panic-probe] +version = "0.3.*" +features = ["print-defmt"] +# Embassy +[workspace.dependencies.embassy-futures] +version = "0.1.*" +[workspace.dependencies.embassy-time] +version = "0.1.*" +features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "nightly", ] +[workspace.dependencies.embassy-sync] +version = "0.1.*" +features = ["defmt"] +[workspace.dependencies.embassy-embedded-hal] +version = "0.1.*" +features = ["nightly"] +[workspace.dependencies.embassy-executor] +version = "0.1.*" +features = ["defmt", "arch-cortex-m", "integrated-timers", "executor-interrupt", "executor-thread", "nightly"] +[workspace.dependencies.embassy-stm32] +version = "0.1.*" +features = ["defmt", "unstable-traits", "unstable-pac", "nightly"] +[workspace.dependencies.embassy-nrf] +version = "0.1.*" +features = ["defmt", "unstable-traits", "nightly"] +#--------------------------------------------------------------------------------------------------------------------- +#----- Patch ------------------------ +#--------------------------------------------------------------------------------------------------------------------- +[patch.crates-io] +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-embedded-hal = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-nrf = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-stm32 = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } + +#--------------------------------------------------------------------------------------------------------------------- +#----- Package ------------------------ +#--------------------------------------------------------------------------------------------------------------------- [package] name = "physical" description = "Physical is a library for interacting with the physical world." @@ -39,4 +106,24 @@ readme.workspace = true license.workspace = true [dependencies] -uom = { workspace = true } \ No newline at end of file +uom = { workspace = true } +parity-scale-codec = { workspace = true } + +#--------------------------------------------------------------------------------------------------------------------- +#----- Profiles ------------------------ +#--------------------------------------------------------------------------------------------------------------------- +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 +panic = "abort" + +[profile.dev] +opt-level = 3 +debug = true +debug-assertions = true +overflow-checks = true +lto = true +panic = "abort" +incremental = false +codegen-units = 1 \ No newline at end of file diff --git a/commander/Cargo.toml b/commander/Cargo.toml index 0c727d9..70fdefb 100644 --- a/commander/Cargo.toml +++ b/commander/Cargo.toml @@ -7,8 +7,7 @@ repository.workspace = true readme.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -physical = { path = ".." } -tracing.workspace = true \ No newline at end of file +[dependencies.physical] +path = ".." +[dependencies.tracing] +workspace = true \ No newline at end of file diff --git a/node/Cargo.toml b/node/Cargo.toml index 6bbbb9e..ba7f818 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -7,7 +7,15 @@ repository.workspace = true readme.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -physical = { path = ".." } \ No newline at end of file +[dependencies.physical] +path = ".." +[dependencies.embedded-hal] +workspace = true +[dependencies.embedded-hal-async] +workspace = true +[dependencies.defmt] +workspace = true +[dependencies.uom] +workspace = true +[dependencies.embassy-sync] +workspace = true \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..2301ddc --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,14 @@ +# Before upgrading check that everything is available on all tier1 targets here: +# https://rust-lang.github.io/rustup-components-history +[toolchain] +channel = "nightly-2023-04-18" +components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] +targets = [ + "thumbv7em-none-eabi", + "thumbv7m-none-eabi", + "thumbv6m-none-eabi", + "thumbv7em-none-eabihf", + "thumbv8m.main-none-eabihf", + "riscv32imac-unknown-none-elf", + "wasm32-unknown-unknown", +] -- 2.43.0 From 1446bf6162d0af75d78dba9c0140c9d116c31c0f Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 13 Jun 2023 21:09:57 -0700 Subject: [PATCH 19/51] Project setup --- Cargo.toml | 12 +++++++- node/Cargo.toml | 2 -- node/src/lib.rs | 5 +++- node/src/transducer/input.rs | 2 ++ node/src/transducer/mod.rs | 15 ++++++++++ node/src/transducer/output.rs | 1 + peripheral-components/ads1256/node/Cargo.toml | 29 +++++++++++++++++++ .../ads1256/node/src/analog_input.rs | 10 +++++++ peripheral-components/ads1256/node/src/lib.rs | 4 +++ .../ads1256/types/Cargo.toml | 17 +++++++++++ .../ads1256/types/src/lib.rs | 1 + .../standalone-ads1256/node/Cargo.toml | 25 ++++++++++++++++ .../standalone-ads1256/node/src/lib.rs | 1 + .../standalone-ads1256/types/Cargo.toml | 13 +++++++++ .../standalone-ads1256/types/src/lib.rs | 1 + src/lib.rs | 1 + src/transducer/input.rs | 0 src/transducer/mod.rs | 10 +++++++ src/transducer/output.rs | 7 +++++ 19 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 node/src/transducer/input.rs create mode 100644 node/src/transducer/mod.rs create mode 100644 node/src/transducer/output.rs create mode 100644 peripheral-components/ads1256/node/Cargo.toml create mode 100644 peripheral-components/ads1256/node/src/analog_input.rs create mode 100644 peripheral-components/ads1256/node/src/lib.rs create mode 100644 peripheral-components/ads1256/types/Cargo.toml create mode 100644 peripheral-components/ads1256/types/src/lib.rs create mode 100644 peripherals/standalone-ads1256/node/Cargo.toml create mode 100644 peripherals/standalone-ads1256/node/src/lib.rs create mode 100644 peripherals/standalone-ads1256/types/Cargo.toml create mode 100644 peripherals/standalone-ads1256/types/src/lib.rs create mode 100644 src/transducer/input.rs create mode 100644 src/transducer/mod.rs create mode 100644 src/transducer/output.rs diff --git a/Cargo.toml b/Cargo.toml index 58382fb..dde9b18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,10 @@ members = [ # Device types "node", "commander", + # Peripherals + "peripherals/standalone-ads1256/*", + # Peripheral components + "peripheral-components/ads1256/*", # Examples "examples/playground" ] @@ -17,7 +21,6 @@ repository = "https://git.bfpower.io/BFPOWER/physical" readme = "README.md" license = "MIT" -[workspace.dependencies] #----- no-std ---------------------------------- # Math [workspace.dependencies.libm] @@ -55,6 +58,13 @@ version = "0.7.*" [workspace.dependencies.panic-probe] version = "0.3.*" features = ["print-defmt"] +# BFPOWER Drivers +[workspace.dependencies.ads1256-types] +git = "https://git.bfpower.io/BFPOWER/bfpower-drivers.git" +features = ["defmt"] +[workspace.dependencies.ads1256] +git = "https://git.bfpower.io/BFPOWER/bfpower-drivers.git" +features = ["uom"] # Embassy [workspace.dependencies.embassy-futures] version = "0.1.*" diff --git a/node/Cargo.toml b/node/Cargo.toml index ba7f818..7eb900a 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -16,6 +16,4 @@ workspace = true [dependencies.defmt] workspace = true [dependencies.uom] -workspace = true -[dependencies.embassy-sync] workspace = true \ No newline at end of file diff --git a/node/src/lib.rs b/node/src/lib.rs index cf06c83..66ffc1f 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -1 +1,4 @@ -mod cell; +#![feature(async_fn_in_trait)] + +pub mod cell; +mod transducer; \ No newline at end of file diff --git a/node/src/transducer/input.rs b/node/src/transducer/input.rs new file mode 100644 index 0000000..839acc6 --- /dev/null +++ b/node/src/transducer/input.rs @@ -0,0 +1,2 @@ +pub use physical::transducer::input::*; + diff --git a/node/src/transducer/mod.rs b/node/src/transducer/mod.rs new file mode 100644 index 0000000..b0a4544 --- /dev/null +++ b/node/src/transducer/mod.rs @@ -0,0 +1,15 @@ +mod input; +mod output; + +pub use physical::transducer::*; + +// --------------------------------------------------------------------------------------------------------------------- +// ----- Publisher ------------------------ +// --------------------------------------------------------------------------------------------------------------------- +#[cfg(feature = "embassy-sync")] +pub trait Publisher { + type Value: Copy; + + + fn subscribe() -> SubT; +} \ No newline at end of file diff --git a/node/src/transducer/output.rs b/node/src/transducer/output.rs new file mode 100644 index 0000000..c441daa --- /dev/null +++ b/node/src/transducer/output.rs @@ -0,0 +1 @@ +pub use physical::transducer::output::*; \ No newline at end of file diff --git a/peripheral-components/ads1256/node/Cargo.toml b/peripheral-components/ads1256/node/Cargo.toml new file mode 100644 index 0000000..76d72d0 --- /dev/null +++ b/peripheral-components/ads1256/node/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "physical-ads1256" +description = "Shared abstractions for ADS1256 components." +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[features] +standard-multiplexer = [] + +[dependencies.physical-node] +path = "../../../node" +[dependencies.physical-ads1256-types] +path = "../types" +[dependencies.ads1256] +workspace = true +[dependencies.embedded-hal] +workspace = true +[dependencies.embedded-hal-async] +workspace = true +[dependencies.defmt] +workspace = true +[dependencies.uom] +workspace = true +[dependencies.embassy-sync] +workspace = true +optional = true \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/analog_input.rs b/peripheral-components/ads1256/node/src/analog_input.rs new file mode 100644 index 0000000..bf02d65 --- /dev/null +++ b/peripheral-components/ads1256/node/src/analog_input.rs @@ -0,0 +1,10 @@ + +struct AnalogInput {} + +// AnalogInputS +// AnalogInputI +// AnalogInputC +// AnalogInputSI +// AnalogInputSC +// AnalogInputIC +// AnalogInputSIC diff --git a/peripheral-components/ads1256/node/src/lib.rs b/peripheral-components/ads1256/node/src/lib.rs new file mode 100644 index 0000000..54dc809 --- /dev/null +++ b/peripheral-components/ads1256/node/src/lib.rs @@ -0,0 +1,4 @@ +#![no_std] + +#[cfg(feature = "standard-multiplexer")] +mod analog_input; \ No newline at end of file diff --git a/peripheral-components/ads1256/types/Cargo.toml b/peripheral-components/ads1256/types/Cargo.toml new file mode 100644 index 0000000..29dbf41 --- /dev/null +++ b/peripheral-components/ads1256/types/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "physical-ads1256-types" +description = "ADS1256 Physical types." +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[features] +standard-multiplexer = [] + +[dependencies.ads1256-types] +workspace = true +[dependencies.defmt] +workspace = true +optional = true \ No newline at end of file diff --git a/peripheral-components/ads1256/types/src/lib.rs b/peripheral-components/ads1256/types/src/lib.rs new file mode 100644 index 0000000..0c9ac1a --- /dev/null +++ b/peripheral-components/ads1256/types/src/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/peripherals/standalone-ads1256/node/Cargo.toml b/peripherals/standalone-ads1256/node/Cargo.toml new file mode 100644 index 0000000..b88e589 --- /dev/null +++ b/peripherals/standalone-ads1256/node/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "physical-standalone-ads1256" +description = "Support for dedicated ADS1256 boards (i.e. boards that have no other functionality other than to expose the ADS1256)" +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies.physical-node] +path = "../../../node" +[dependencies.physical-standalone-ads1256-types] +path = "../types" +[dependencies.physical-ads1256] +path = "../../../peripheral-components/ads1256/node" +[dependencies.ads1256] +workspace = true +[dependencies.embedded-hal] +workspace = true +[dependencies.embedded-hal-async] +workspace = true +[dependencies.defmt] +workspace = true +[dependencies.uom] +workspace = true \ No newline at end of file diff --git a/peripherals/standalone-ads1256/node/src/lib.rs b/peripherals/standalone-ads1256/node/src/lib.rs new file mode 100644 index 0000000..2e7f0d4 --- /dev/null +++ b/peripherals/standalone-ads1256/node/src/lib.rs @@ -0,0 +1 @@ +#![no_std] \ No newline at end of file diff --git a/peripherals/standalone-ads1256/types/Cargo.toml b/peripherals/standalone-ads1256/types/Cargo.toml new file mode 100644 index 0000000..1dd8682 --- /dev/null +++ b/peripherals/standalone-ads1256/types/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "physical-standalone-ads1256-types" +description = "Support for dedicated ADS1256 boards (i.e. boards that have no other functionality other than to expose the ADS1256)" +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies.ads1256-types] +workspace = true +[dependencies.physical-ads1256-types] +path = "../../../peripheral-components/ads1256/types" \ No newline at end of file diff --git a/peripherals/standalone-ads1256/types/src/lib.rs b/peripherals/standalone-ads1256/types/src/lib.rs new file mode 100644 index 0000000..0c9ac1a --- /dev/null +++ b/peripherals/standalone-ads1256/types/src/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/src/lib.rs b/src/lib.rs index 1ef6109..4ef2a82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ #![no_std] +pub mod transducer; \ No newline at end of file diff --git a/src/transducer/input.rs b/src/transducer/input.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/transducer/mod.rs b/src/transducer/mod.rs new file mode 100644 index 0000000..ac4c6a2 --- /dev/null +++ b/src/transducer/mod.rs @@ -0,0 +1,10 @@ +pub mod input; +pub mod output; + +// Initialisation will always be async and won't complete until a state is available for all +// stateful transducers. +pub trait Stateful { + type Value: Copy; + + fn state() -> Self::Value; +} diff --git a/src/transducer/output.rs b/src/transducer/output.rs new file mode 100644 index 0000000..1c7180f --- /dev/null +++ b/src/transducer/output.rs @@ -0,0 +1,7 @@ +pub trait Output { + type Value: Copy; + + //TODO: return result + //TODO: Make maybe async + fn set(setting: Self::Value); +} \ No newline at end of file -- 2.43.0 From 19b8bc607cbd154567c016b775de0e594aa18a3e Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 13 Jun 2023 22:46:34 -0700 Subject: [PATCH 20/51] Core traits. --- node/Cargo.toml | 5 +++- node/src/transducer/mod.rs | 24 ++++++++++++++----- peripheral-components/ads1256/node/Cargo.toml | 5 +--- src/transducer/mod.rs | 2 +- src/transducer/output.rs | 2 +- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/node/Cargo.toml b/node/Cargo.toml index 7eb900a..644cde8 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -16,4 +16,7 @@ workspace = true [dependencies.defmt] workspace = true [dependencies.uom] -workspace = true \ No newline at end of file +workspace = true +[dependencies.embassy-sync] +workspace = true +optional = true \ No newline at end of file diff --git a/node/src/transducer/mod.rs b/node/src/transducer/mod.rs index b0a4544..5a37c76 100644 --- a/node/src/transducer/mod.rs +++ b/node/src/transducer/mod.rs @@ -3,13 +3,25 @@ mod output; pub use physical::transducer::*; -// --------------------------------------------------------------------------------------------------------------------- -// ----- Publisher ------------------------ -// --------------------------------------------------------------------------------------------------------------------- +#[cfg(feature = "embassy-sync")] +use embassy_sync::blocking_mutex::raw::RawMutex; +#[cfg(feature = "embassy-sync")] +use embassy_sync::pubsub; +#[cfg(feature = "embassy-sync")] +use embassy_sync::pubsub::Subscriber; + #[cfg(feature = "embassy-sync")] pub trait Publisher { type Value: Copy; + type Mutex: RawMutex; + const CAPACITY: usize; + const NUM_SUBS: usize; + const NUM_PUBS: usize; - - fn subscribe() -> SubT; -} \ No newline at end of file + fn subscribe( + &self, + ) -> Result< + Subscriber, + pubsub::Error, + >; +} diff --git a/peripheral-components/ads1256/node/Cargo.toml b/peripheral-components/ads1256/node/Cargo.toml index 76d72d0..b4aa150 100644 --- a/peripheral-components/ads1256/node/Cargo.toml +++ b/peripheral-components/ads1256/node/Cargo.toml @@ -23,7 +23,4 @@ workspace = true [dependencies.defmt] workspace = true [dependencies.uom] -workspace = true -[dependencies.embassy-sync] -workspace = true -optional = true \ No newline at end of file +workspace = true \ No newline at end of file diff --git a/src/transducer/mod.rs b/src/transducer/mod.rs index ac4c6a2..d3986f6 100644 --- a/src/transducer/mod.rs +++ b/src/transducer/mod.rs @@ -6,5 +6,5 @@ pub mod output; pub trait Stateful { type Value: Copy; - fn state() -> Self::Value; + fn state(&self) -> Self::Value; } diff --git a/src/transducer/output.rs b/src/transducer/output.rs index 1c7180f..989830e 100644 --- a/src/transducer/output.rs +++ b/src/transducer/output.rs @@ -3,5 +3,5 @@ pub trait Output { //TODO: return result //TODO: Make maybe async - fn set(setting: Self::Value); + fn set(&mut self, setting: Self::Value); } \ No newline at end of file -- 2.43.0 From 45ec03dfd1936b684fe253224f6ae9ade618c31f Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Wed, 14 Jun 2023 15:39:02 -0700 Subject: [PATCH 21/51] Created input types. --- node/src/lib.rs | 7 +++++-- node/src/transducer/input.rs | 28 ++++++++++++++++++++++++++++ {node/src => src}/cell.rs | 0 src/lib.rs | 3 ++- src/transducer/input.rs | 20 ++++++++++++++++++++ src/transducer/mod.rs | 4 ++++ 6 files changed, 59 insertions(+), 3 deletions(-) rename {node/src => src}/cell.rs (100%) diff --git a/node/src/lib.rs b/node/src/lib.rs index 66ffc1f..7a6c8c9 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -1,4 +1,7 @@ #![feature(async_fn_in_trait)] -pub mod cell; -mod transducer; \ No newline at end of file +mod transducer; + +pub mod cell { + pub use physical::cell::*; +} diff --git a/node/src/transducer/input.rs b/node/src/transducer/input.rs index 839acc6..f36bec2 100644 --- a/node/src/transducer/input.rs +++ b/node/src/transducer/input.rs @@ -1,2 +1,30 @@ +use crate::cell::CellView; +#[cfg(feature = "embassy-sync")] +use embassy_sync::blocking_mutex::raw::RawMutex; +#[cfg(feature = "embassy-sync")] +use embassy_sync::pubsub::PubSubChannel; pub use physical::transducer::input::*; +#[cfg(feature = "embassy-sync")] +pub struct ChannelInput< + T: Copy, + MutexT: RawMutex, + const CAPACITY: usize, + const NUM_SUBS: usize, + const NUM_PUBS: usize, +> { + channel: PubSubChannel, +} + +#[cfg(feature = "embassy-sync")] +pub struct StatefulChannelInput< + 'a, + T: Copy, + MutexT: RawMutex, + const CAPACITY: usize, + const NUM_SUBS: usize, + const NUM_PUBS: usize, +> { + pub state_cell: CellView<'a, T>, + channel: PubSubChannel, +} diff --git a/node/src/cell.rs b/src/cell.rs similarity index 100% rename from node/src/cell.rs rename to src/cell.rs diff --git a/src/lib.rs b/src/lib.rs index 4ef2a82..6437a0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ #![no_std] -pub mod transducer; \ No newline at end of file +pub mod transducer; +pub mod cell; diff --git a/src/transducer/input.rs b/src/transducer/input.rs index e69de29..7f8cc1d 100644 --- a/src/transducer/input.rs +++ b/src/transducer/input.rs @@ -0,0 +1,20 @@ +use crate::cell::CellView; +use crate::transducer::Stateful; + +pub struct StatefulInput<'a, T: Copy> { + pub state_cell: CellView<'a, T>, +} + +impl<'a, T: Copy> Stateful for StatefulInput<'a, T> { + type Value = T; + + #[inline(always)] + fn state_cell(&self) -> CellView<'a, Self::Value> { + self.state_cell + } + + #[inline] + fn state(&self) -> Self::Value { + self.state_cell.get() + } +} diff --git a/src/transducer/mod.rs b/src/transducer/mod.rs index d3986f6..2d69172 100644 --- a/src/transducer/mod.rs +++ b/src/transducer/mod.rs @@ -1,3 +1,5 @@ +use crate::cell::CellView; + pub mod input; pub mod output; @@ -6,5 +8,7 @@ pub mod output; pub trait Stateful { type Value: Copy; + fn state_cell(&self) -> CellView; + fn state(&self) -> Self::Value; } -- 2.43.0 From a143ad0e54e314d18bf00ac604b90bee84330a88 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Wed, 14 Jun 2023 22:22:49 -0700 Subject: [PATCH 22/51] Created input types. --- node/src/lib.rs | 1 + node/src/transducer/input.rs | 4 ++-- src/lib.rs | 1 + src/transducer/input.rs | 4 ++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/node/src/lib.rs b/node/src/lib.rs index 7a6c8c9..a55204f 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -1,3 +1,4 @@ +#![no_std] #![feature(async_fn_in_trait)] mod transducer; diff --git a/node/src/transducer/input.rs b/node/src/transducer/input.rs index f36bec2..a57dfa9 100644 --- a/node/src/transducer/input.rs +++ b/node/src/transducer/input.rs @@ -6,7 +6,7 @@ use embassy_sync::pubsub::PubSubChannel; pub use physical::transducer::input::*; #[cfg(feature = "embassy-sync")] -pub struct ChannelInput< +pub struct PublishInput< T: Copy, MutexT: RawMutex, const CAPACITY: usize, @@ -17,7 +17,7 @@ pub struct ChannelInput< } #[cfg(feature = "embassy-sync")] -pub struct StatefulChannelInput< +pub struct StatefulPublishInput< 'a, T: Copy, MutexT: RawMutex, diff --git a/src/lib.rs b/src/lib.rs index 6437a0e..9fe79e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![feature(async_fn_in_trait)] pub mod transducer; pub mod cell; diff --git a/src/transducer/input.rs b/src/transducer/input.rs index 7f8cc1d..7b40489 100644 --- a/src/transducer/input.rs +++ b/src/transducer/input.rs @@ -1,6 +1,10 @@ use crate::cell::CellView; use crate::transducer::Stateful; +pub trait Poll { + async fn poll() -> T; +} + pub struct StatefulInput<'a, T: Copy> { pub state_cell: CellView<'a, T>, } -- 2.43.0 From ad66d8e0307d9fa898a91a2cc3439ed693235794 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 16 Jun 2023 18:23:43 -0700 Subject: [PATCH 23/51] Created multiplexing publish / subscribe example. --- Cargo.toml | 3 +- examples/ads1256/.cargo/config.toml | 9 + examples/ads1256/Cargo.toml | 42 ++++ examples/ads1256/build.rs | 5 + examples/ads1256/src/bin/multiplex.rs | 181 ++++++++++++++++++ examples/playground/Cargo.toml | 13 -- examples/playground/src/main.rs | 7 - node/src/lib.rs | 2 +- node/src/transducer/input.rs | 103 ++++++++-- node/src/transducer/mod.rs | 14 +- peripheral-components/ads1256/node/Cargo.toml | 5 +- .../ads1256/node/src/analog_input.rs | 10 - peripheral-components/ads1256/node/src/lib.rs | 4 +- .../ads1256/node/src/standard_multiplexer.rs | 0 .../ads1256/types/Cargo.toml | 3 +- .../ads1256/types/src/config.rs | 2 + .../ads1256/types/src/lib.rs | 10 + .../ads1256/types/src/standard_input.rs | 39 ++++ src/cell.rs | 3 +- src/transducer/conversion/mod.rs | 0 src/transducer/input.rs | 25 ++- src/transducer/mod.rs | 6 +- 22 files changed, 419 insertions(+), 67 deletions(-) create mode 100644 examples/ads1256/.cargo/config.toml create mode 100644 examples/ads1256/Cargo.toml create mode 100644 examples/ads1256/build.rs create mode 100644 examples/ads1256/src/bin/multiplex.rs delete mode 100644 examples/playground/Cargo.toml delete mode 100644 examples/playground/src/main.rs delete mode 100644 peripheral-components/ads1256/node/src/analog_input.rs create mode 100644 peripheral-components/ads1256/node/src/standard_multiplexer.rs create mode 100644 peripheral-components/ads1256/types/src/config.rs create mode 100644 peripheral-components/ads1256/types/src/standard_input.rs create mode 100644 src/transducer/conversion/mod.rs diff --git a/Cargo.toml b/Cargo.toml index dde9b18..83426da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ # Peripheral components "peripheral-components/ads1256/*", # Examples - "examples/playground" + "examples/ads1256" ] [workspace.package] @@ -40,6 +40,7 @@ version = "0.4.*" # Serialization [workspace.dependencies.parity-scale-codec] version = "3.5.*" +default-features = false # Embedded-HAL [workspace.dependencies.embedded-hal] version = "1.0.0-alpha.10" diff --git a/examples/ads1256/.cargo/config.toml b/examples/ads1256/.cargo/config.toml new file mode 100644 index 0000000..dbcc285 --- /dev/null +++ b/examples/ads1256/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` +runner = "probe-rs-cli run --chip STM32F429ZITx" + +[build] +target = "thumbv7em-none-eabi" + +[env] +DEFMT_LOG = "trace" \ No newline at end of file diff --git a/examples/ads1256/Cargo.toml b/examples/ads1256/Cargo.toml new file mode 100644 index 0000000..194ac18 --- /dev/null +++ b/examples/ads1256/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "ads1256-examples" +description = "Examples using the ads1256." +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies.physical-node] +path = "../../node" +features = ["embassy-sync"] +[dependencies.physical-ads1256] +path = "../../peripheral-components/ads1256/node" +features = ["standard-input"] +[dependencies.ads1256] +workspace = true +[dependencies.uom] +workspace = true +[dependencies.defmt] +workspace = true +[dependencies.defmt-rtt] +workspace = true +[dependencies.cortex-m] +workspace = true +features = ["critical-section-single-core"] +[dependencies.cortex-m-rt] +workspace = true +[dependencies.embassy-stm32] +workspace = true +features = ["stm32f429zi", "memory-x", "time", "exti", "time-driver-any"] +[dependencies.embassy-executor] +workspace = true +[dependencies.embassy-futures] +workspace = true +[dependencies.embassy-time] +workspace = true +features = ["tick-hz-16_000_000"] +[dependencies.embassy-sync] +workspace = true +[dependencies.panic-probe] +workspace = true diff --git a/examples/ads1256/build.rs b/examples/ads1256/build.rs new file mode 100644 index 0000000..56127fd --- /dev/null +++ b/examples/ads1256/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} \ No newline at end of file diff --git a/examples/ads1256/src/bin/multiplex.rs b/examples/ads1256/src/bin/multiplex.rs new file mode 100644 index 0000000..d35a011 --- /dev/null +++ b/examples/ads1256/src/bin/multiplex.rs @@ -0,0 +1,181 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait, async_fn_in_trait)] + +use core::cell::Cell; +use cortex_m::prelude::{ + _embedded_hal_blocking_delay_DelayMs, _embedded_hal_blocking_delay_DelayUs, +}; +use {defmt_rtt as _, panic_probe as _}; + +use {embassy_executor as executor, embassy_stm32 as stm32}; + +use ads1256::{ + AdControl, Ads1256, AutoCal, BitOrder, BlockingDelay, Buffer, ClockOut, Config, DState, + DataRate, DigitalIo, DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, + MuxInput, OutputPin, Sdcs, SpiBus, Status, Wait, +}; +use embassy_time::{Delay, Duration, Timer}; +use executor::Spawner; +use stm32::dma::NoDma; +use stm32::exti::ExtiInput; +use stm32::gpio::{Input, Level, Output, Pull, Speed}; +use stm32::spi::Spi; +use stm32::time::Hertz; +use stm32::{pac, spi}; + +use uom::si::electric_potential::volt; +use uom::si::f32; + +use defmt::{debug, error, info, trace, unwrap}; +use embassy_executor::_export::StaticCell; +use embassy_stm32::peripherals::{EXTI6, PF6, PF7, SPI3}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::pubsub::PubSubChannel; +use physical_ads1256::{standard_input, SingleEnded}; +use physical_node::transducer::input::RawStatePubInput; +use physical_node::transducer::Publisher; + +const AUTOCAL_CONF: Config = Config { + status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), + multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::Common), + ad_control: AdControl::setting(Gain::X2, Sdcs::Off, ClockOut::Off), + data_rate: DataRate::Sps2_5, + digital_io: DigitalIo::setting(DigitalIoState::default(), DigitalIoDirection::default()), +}; + +struct Ads1256Delay; + +impl ads1256::BlockingDelay for Ads1256Delay { + #[inline] + fn t6_delay(&mut self) { + Delay.delay_us(1u32); + } + + fn t11_1_delay(&mut self) {} + + fn t11_2_delay(&mut self) {} +} + +struct Inputs { + ai1: RawStatePubInput, + ai2: RawStatePubInput, + ai3: RawStatePubInput, +} + +// Inputs +static ANALOG_INPUTS: StaticCell = StaticCell::new(); +static ADS_1256: StaticCell, ExtiInput>> = StaticCell::new(); +static SPI: StaticCell> = StaticCell::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + unsafe { + pac::FLASH.acr().modify(|v| { + v.set_prften(true); + v.set_icen(true); + v.set_dcen(true); + }); + } + + let p = embassy_stm32::init(Default::default()); + + let mut spi_conf = spi::Config::default(); + spi_conf.mode = spi::MODE_1; + spi_conf.bit_order = spi::BitOrder::MsbFirst; + + let ads1256_data_ready = ExtiInput::new(Input::new(p.PF6, Pull::Up), p.EXTI6); + let select_ads1256 = Output::new(p.PF7, Level::High, Speed::VeryHigh); + + let spi = SPI.init(Spi::new( + p.SPI3, + p.PC10, + p.PC12, + p.PC11, + NoDma, + NoDma, + Hertz(ads1256::defaults::SPI_CLK_HZ), + spi_conf, + )); + + let ads_1256 = ADS_1256.init(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready)); + + let inputs = &*ANALOG_INPUTS.init(Inputs { + ai1: RawStatePubInput::new( + Cell::new(f32::ElectricPotential::new::(-1000.0)), + PubSubChannel::new(), + ), + ai2: RawStatePubInput::new( + Cell::new(f32::ElectricPotential::new::(-1000.0)), + PubSubChannel::new(), + ), + ai3: RawStatePubInput::new( + Cell::new(f32::ElectricPotential::new::(-1000.0)), + PubSubChannel::new(), + ), + }); + + spawner.spawn(log_task(inputs)).unwrap(); + spawner + .spawn(drive_inputs_task(ads_1256, spi, inputs)) + .unwrap(); +} + +#[embassy_executor::task] +async fn drive_inputs_task( + ads_1256: &'static mut Ads1256, ExtiInput<'static, PF6>>, + spi: &'static mut Spi<'static, SPI3, NoDma, NoDma>, + inputs: &'static Inputs, +) { + let Inputs { ai1, ai2, ai3 } = inputs; + + loop { + let mut accumulator = f32::ElectricPotential::new::(0.0); + + let voltage = ads_1256 + .autocal_convert(spi, SingleEnded::AIn0.into(), None, None, None, false) + .await + .unwrap() + .to_voltage(AUTOCAL_CONF.ad_control.gain()); + ai1.update(voltage); + accumulator += voltage; + + let voltage = ads_1256 + .autocal_convert(spi, SingleEnded::AIn1.into(), None, None, None, false) + .await + .unwrap() + .to_voltage(AUTOCAL_CONF.ad_control.gain()); + ai2.update(voltage); + accumulator += voltage; + + let voltage = ads_1256 + .autocal_convert(spi, SingleEnded::AIn2.into(), None, None, None, false) + .await + .unwrap() + .to_voltage(AUTOCAL_CONF.ad_control.gain()); + ai3.update(voltage); + accumulator += voltage; + + let accum_volts = accumulator.get::(); + info!("Immediate loop iteration result, combined volts: {}", accum_volts); + } +} + +#[embassy_executor::task] +async fn log_task(inputs: &'static Inputs) { + let Inputs { ai1, ai2, ai3 } = inputs; + let mut ai1_sub = ai1.subscribe().unwrap(); + let mut ai2_sub = ai2.subscribe().unwrap(); + let mut ai3_sub = ai3.subscribe().unwrap(); + + loop { + let msg = ai1_sub.next_message_pure().await.get::(); + info!("Log task ai1: {}", msg); + + let msg = ai2_sub.next_message_pure().await.get::(); + info!("Log task ai2: {}", msg); + + let msg = ai3_sub.next_message_pure().await.get::(); + info!("Log task ai3: {}", msg); + } +} diff --git a/examples/playground/Cargo.toml b/examples/playground/Cargo.toml deleted file mode 100644 index 33f173c..0000000 --- a/examples/playground/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "playground" -description = """The playground example is a special example meant for unstructured experimentation which can potentially - be turned into an actual example at some point or deleted. It should always be reset to unchanged hello world before - the working branch where the experimentation is happening is merged.""" -version.workspace = true -edition.workspace = true -repository.workspace = true -readme.workspace = true -license.workspace = true - -[dependencies] -physical = { path = "../.." } \ No newline at end of file diff --git a/examples/playground/src/main.rs b/examples/playground/src/main.rs deleted file mode 100644 index 964a26c..0000000 --- a/examples/playground/src/main.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! The playground example is a special example meant for unstructured experimentation which can potentially be turned -//! into an actual example at some point or deleted. It should always be reset to unchanged hello world before the working -//! branch where the experimentation is happening is merged. - -fn main() { - println!("Hello, world!"); -} diff --git a/node/src/lib.rs b/node/src/lib.rs index a55204f..e712a34 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] #![feature(async_fn_in_trait)] -mod transducer; +pub mod transducer; pub mod cell { pub use physical::cell::*; diff --git a/node/src/transducer/input.rs b/node/src/transducer/input.rs index a57dfa9..43f0885 100644 --- a/node/src/transducer/input.rs +++ b/node/src/transducer/input.rs @@ -1,30 +1,105 @@ use crate::cell::CellView; +use crate::transducer::Publisher; +use core::cell::Cell; #[cfg(feature = "embassy-sync")] use embassy_sync::blocking_mutex::raw::RawMutex; #[cfg(feature = "embassy-sync")] use embassy_sync::pubsub::PubSubChannel; +use embassy_sync::pubsub::{Error, PubSubBehavior, Subscriber}; pub use physical::transducer::input::*; +use physical::transducer::Stateful; #[cfg(feature = "embassy-sync")] -pub struct PublishInput< +pub struct RawPublishInput +{ + pub channel: PubSubChannel, +} + +impl + RawPublishInput +{ + #[inline(always)] + pub fn new(channel: PubSubChannel) -> Self { + Self { channel } + } + + #[inline(always)] + pub fn update(&self, value: T) { + self.channel.publish_immediate(value); + } +} + +impl + Publisher for RawPublishInput +{ + type Value = T; + type Mutex = MutexT; + + #[inline(always)] + fn subscribe( + &self, + ) -> Result, Error> { + self.channel.subscriber() + } +} + +#[cfg(feature = "embassy-sync")] +pub struct RawStatePubInput< T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize, - const NUM_PUBS: usize, > { - channel: PubSubChannel, + pub state_cell: Cell, + pub channel: PubSubChannel, } -#[cfg(feature = "embassy-sync")] -pub struct StatefulPublishInput< - 'a, - T: Copy, - MutexT: RawMutex, - const CAPACITY: usize, - const NUM_SUBS: usize, - const NUM_PUBS: usize, -> { - pub state_cell: CellView<'a, T>, - channel: PubSubChannel, +impl + RawStatePubInput +{ + #[inline(always)] + pub fn new( + state_cell: Cell, + channel: PubSubChannel, + ) -> Self { + Self { + state_cell, + channel, + } + } + + #[inline] + pub fn update(&self, value: T) { + self.state_cell.set(value); + self.channel.publish_immediate(value); + } +} + +impl Stateful + for RawStatePubInput +{ + type Value = T; + + #[inline(always)] + fn state_cell(&self) -> CellView { + (&self.state_cell).into() + } + + #[inline(always)] + fn state(&self) -> Self::Value { + self.state_cell.get() + } +} + +impl + Publisher for RawStatePubInput +{ + type Value = T; + type Mutex = MutexT; + + fn subscribe( + &self, + ) -> Result, Error> { + self.channel.subscriber() + } } diff --git a/node/src/transducer/mod.rs b/node/src/transducer/mod.rs index 5a37c76..c698b30 100644 --- a/node/src/transducer/mod.rs +++ b/node/src/transducer/mod.rs @@ -1,5 +1,5 @@ -mod input; -mod output; +pub mod input; +pub mod output; pub use physical::transducer::*; @@ -11,17 +11,11 @@ use embassy_sync::pubsub; use embassy_sync::pubsub::Subscriber; #[cfg(feature = "embassy-sync")] -pub trait Publisher { +pub trait Publisher { type Value: Copy; type Mutex: RawMutex; - const CAPACITY: usize; - const NUM_SUBS: usize; - const NUM_PUBS: usize; fn subscribe( &self, - ) -> Result< - Subscriber, - pubsub::Error, - >; + ) -> Result, pubsub::Error>; } diff --git a/peripheral-components/ads1256/node/Cargo.toml b/peripheral-components/ads1256/node/Cargo.toml index b4aa150..dfe59d3 100644 --- a/peripheral-components/ads1256/node/Cargo.toml +++ b/peripheral-components/ads1256/node/Cargo.toml @@ -8,12 +8,15 @@ readme.workspace = true license.workspace = true [features] -standard-multiplexer = [] +config = ["physical-ads1256-types/config"] +standard-input = ["physical-ads1256-types/standard-input"] +standard-multiplexer = ["standard-input"] [dependencies.physical-node] path = "../../../node" [dependencies.physical-ads1256-types] path = "../types" +features = ["defmt"] [dependencies.ads1256] workspace = true [dependencies.embedded-hal] diff --git a/peripheral-components/ads1256/node/src/analog_input.rs b/peripheral-components/ads1256/node/src/analog_input.rs deleted file mode 100644 index bf02d65..0000000 --- a/peripheral-components/ads1256/node/src/analog_input.rs +++ /dev/null @@ -1,10 +0,0 @@ - -struct AnalogInput {} - -// AnalogInputS -// AnalogInputI -// AnalogInputC -// AnalogInputSI -// AnalogInputSC -// AnalogInputIC -// AnalogInputSIC diff --git a/peripheral-components/ads1256/node/src/lib.rs b/peripheral-components/ads1256/node/src/lib.rs index 54dc809..23a18d5 100644 --- a/peripheral-components/ads1256/node/src/lib.rs +++ b/peripheral-components/ads1256/node/src/lib.rs @@ -1,4 +1,6 @@ #![no_std] #[cfg(feature = "standard-multiplexer")] -mod analog_input; \ No newline at end of file +mod standard_multiplexer; + +pub use physical_ads1256_types::*; \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer.rs b/peripheral-components/ads1256/node/src/standard_multiplexer.rs new file mode 100644 index 0000000..e69de29 diff --git a/peripheral-components/ads1256/types/Cargo.toml b/peripheral-components/ads1256/types/Cargo.toml index 29dbf41..065bf12 100644 --- a/peripheral-components/ads1256/types/Cargo.toml +++ b/peripheral-components/ads1256/types/Cargo.toml @@ -8,7 +8,8 @@ readme.workspace = true license.workspace = true [features] -standard-multiplexer = [] +config = [] +standard-input = [] [dependencies.ads1256-types] workspace = true diff --git a/peripheral-components/ads1256/types/src/config.rs b/peripheral-components/ads1256/types/src/config.rs new file mode 100644 index 0000000..d162cab --- /dev/null +++ b/peripheral-components/ads1256/types/src/config.rs @@ -0,0 +1,2 @@ +use ads1256_types::{Buffer, Config, DataRate, Gain}; + diff --git a/peripheral-components/ads1256/types/src/lib.rs b/peripheral-components/ads1256/types/src/lib.rs index 0c9ac1a..ec78e3c 100644 --- a/peripheral-components/ads1256/types/src/lib.rs +++ b/peripheral-components/ads1256/types/src/lib.rs @@ -1 +1,11 @@ #![no_std] + +#[cfg(feature = "standard-input")] +pub mod standard_input; +#[cfg(feature = "config")] +mod config; + +#[cfg(feature = "config")] +pub use config::*; +#[cfg(feature = "standard-input")] +pub use standard_input::*; diff --git a/peripheral-components/ads1256/types/src/standard_input.rs b/peripheral-components/ads1256/types/src/standard_input.rs new file mode 100644 index 0000000..b4fa825 --- /dev/null +++ b/peripheral-components/ads1256/types/src/standard_input.rs @@ -0,0 +1,39 @@ +use ads1256_types::{Buffer, Config, DataRate, Gain, Multiplexer, MuxInput}; + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[repr(u8)] +pub enum Differential { + AIn0 = Multiplexer::setting(MuxInput::AIn0, MuxInput::AIn1).0, + AIn1 = Multiplexer::setting(MuxInput::AIn2, MuxInput::AIn3).0, + AIn2 = Multiplexer::setting(MuxInput::AIn4, MuxInput::AIn5).0, + AIn3 = Multiplexer::setting(MuxInput::AIn6, MuxInput::AIn7).0, +} + +impl Into for Differential { + #[inline(always)] + fn into(self) -> Multiplexer { + Multiplexer(self as u8) + } +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[repr(u8)] +pub enum SingleEnded { + AIn0 = Multiplexer::setting(MuxInput::AIn0, MuxInput::Common).0, + AIn1 = Multiplexer::setting(MuxInput::AIn1, MuxInput::Common).0, + AIn2 = Multiplexer::setting(MuxInput::AIn2, MuxInput::Common).0, + AIn3 = Multiplexer::setting(MuxInput::AIn3, MuxInput::Common).0, + AIn4 = Multiplexer::setting(MuxInput::AIn4, MuxInput::Common).0, + AIn5 = Multiplexer::setting(MuxInput::AIn5, MuxInput::Common).0, + AIn6 = Multiplexer::setting(MuxInput::AIn6, MuxInput::Common).0, + AIn7 = Multiplexer::setting(MuxInput::AIn7, MuxInput::Common).0, +} + +impl Into for SingleEnded { + #[inline(always)] + fn into(self) -> Multiplexer { + Multiplexer(self as u8) + } +} diff --git a/src/cell.rs b/src/cell.rs index 9526551..16ee00b 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -6,13 +6,14 @@ use core::cell::Cell; pub struct CellView<'a, T: Copy>(&'a Cell); impl CellView<'_, T> { - #[inline] + #[inline(always)] pub fn get(self) -> T { self.0.get() } } impl<'a, T: Copy> From<&'a Cell> for CellView<'a, T> { + #[inline(always)] fn from(value: &'a Cell) -> Self { CellView(value) } diff --git a/src/transducer/conversion/mod.rs b/src/transducer/conversion/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/transducer/input.rs b/src/transducer/input.rs index 7b40489..12540ba 100644 --- a/src/transducer/input.rs +++ b/src/transducer/input.rs @@ -1,3 +1,4 @@ +use core::cell::Cell; use crate::cell::CellView; use crate::transducer::Stateful; @@ -5,19 +6,31 @@ pub trait Poll { async fn poll() -> T; } -pub struct StatefulInput<'a, T: Copy> { - pub state_cell: CellView<'a, T>, +pub struct RawStatefulInput { + pub state_cell: Cell, } -impl<'a, T: Copy> Stateful for StatefulInput<'a, T> { +impl RawStatefulInput { + #[inline(always)] + pub fn new(state_cell: Cell) -> Self { + Self { state_cell } + } + + #[inline(always)] + pub fn raw_update(&self, value: T) { + self.state_cell.set(value); + } +} + +impl Stateful for RawStatefulInput { type Value = T; #[inline(always)] - fn state_cell(&self) -> CellView<'a, Self::Value> { - self.state_cell + fn state_cell(&self) -> CellView { + (&self.state_cell).into() } - #[inline] + #[inline(always)] fn state(&self) -> Self::Value { self.state_cell.get() } diff --git a/src/transducer/mod.rs b/src/transducer/mod.rs index 2d69172..eada613 100644 --- a/src/transducer/mod.rs +++ b/src/transducer/mod.rs @@ -2,6 +2,7 @@ use crate::cell::CellView; pub mod input; pub mod output; +mod conversion; // Initialisation will always be async and won't complete until a state is available for all // stateful transducers. @@ -10,5 +11,8 @@ pub trait Stateful { fn state_cell(&self) -> CellView; - fn state(&self) -> Self::Value; + #[inline(always)] + fn state(&self) -> Self::Value { + self.state_cell().get() + } } -- 2.43.0 From 60e0edf7d2ecb0d8cbd36b8b3ac1f2d16887e3b6 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Sat, 17 Jun 2023 10:41:20 -0700 Subject: [PATCH 24/51] Switch to fully composition based Transducers. --- examples/ads1256/src/bin/multiplex.rs | 28 +++++----- node/src/transducer/input.rs | 75 +++++++-------------------- node/src/transducer/mod.rs | 47 ++++++++++++++++- src/transducer/input.rs | 30 ----------- src/transducer/mod.rs | 39 +++++++++++++- src/transducer/output.rs | 2 +- 6 files changed, 116 insertions(+), 105 deletions(-) diff --git a/examples/ads1256/src/bin/multiplex.rs b/examples/ads1256/src/bin/multiplex.rs index d35a011..df5a1ac 100644 --- a/examples/ads1256/src/bin/multiplex.rs +++ b/examples/ads1256/src/bin/multiplex.rs @@ -33,8 +33,8 @@ use embassy_stm32::peripherals::{EXTI6, PF6, PF7, SPI3}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pubsub::PubSubChannel; use physical_ads1256::{standard_input, SingleEnded}; -use physical_node::transducer::input::RawStatePubInput; -use physical_node::transducer::Publisher; +use physical_node::transducer::input::StatefulPublisher; +use physical_node::transducer::Publish; const AUTOCAL_CONF: Config = Config { status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), @@ -58,9 +58,9 @@ impl ads1256::BlockingDelay for Ads1256Delay { } struct Inputs { - ai1: RawStatePubInput, - ai2: RawStatePubInput, - ai3: RawStatePubInput, + ai1: StatefulPublisher, + ai2: StatefulPublisher, + ai3: StatefulPublisher, } // Inputs @@ -101,17 +101,17 @@ async fn main(spawner: Spawner) { let ads_1256 = ADS_1256.init(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready)); let inputs = &*ANALOG_INPUTS.init(Inputs { - ai1: RawStatePubInput::new( - Cell::new(f32::ElectricPotential::new::(-1000.0)), - PubSubChannel::new(), + ai1: StatefulPublisher::new( + Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), + PubSubChannel::new().into(), ), - ai2: RawStatePubInput::new( - Cell::new(f32::ElectricPotential::new::(-1000.0)), - PubSubChannel::new(), + ai2: StatefulPublisher::new( + Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), + PubSubChannel::new().into(), ), - ai3: RawStatePubInput::new( - Cell::new(f32::ElectricPotential::new::(-1000.0)), - PubSubChannel::new(), + ai3: StatefulPublisher::new( + Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), + PubSubChannel::new().into(), ), }); diff --git a/node/src/transducer/input.rs b/node/src/transducer/input.rs index 43f0885..dedbf72 100644 --- a/node/src/transducer/input.rs +++ b/node/src/transducer/input.rs @@ -1,105 +1,68 @@ use crate::cell::CellView; -use crate::transducer::Publisher; +use crate::transducer::{Publish, Publisher}; use core::cell::Cell; #[cfg(feature = "embassy-sync")] use embassy_sync::blocking_mutex::raw::RawMutex; #[cfg(feature = "embassy-sync")] -use embassy_sync::pubsub::PubSubChannel; -use embassy_sync::pubsub::{Error, PubSubBehavior, Subscriber}; +use embassy_sync::pubsub::{Error, PubSubChannel, Subscriber}; pub use physical::transducer::input::*; -use physical::transducer::Stateful; +use physical::transducer::{State, Stateful}; #[cfg(feature = "embassy-sync")] -pub struct RawPublishInput -{ - pub channel: PubSubChannel, -} - -impl - RawPublishInput -{ - #[inline(always)] - pub fn new(channel: PubSubChannel) -> Self { - Self { channel } - } - - #[inline(always)] - pub fn update(&self, value: T) { - self.channel.publish_immediate(value); - } -} - -impl - Publisher for RawPublishInput -{ - type Value = T; - type Mutex = MutexT; - - #[inline(always)] - fn subscribe( - &self, - ) -> Result, Error> { - self.channel.subscriber() - } -} - -#[cfg(feature = "embassy-sync")] -pub struct RawStatePubInput< +pub struct StatefulPublisher< T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize, > { - pub state_cell: Cell, - pub channel: PubSubChannel, + pub state: State, + pub publisher: Publisher, } impl - RawStatePubInput + StatefulPublisher { #[inline(always)] pub fn new( - state_cell: Cell, - channel: PubSubChannel, + state: State, + publisher: Publisher, ) -> Self { - Self { - state_cell, - channel, - } + Self { state, publisher } } - #[inline] + #[inline(always)] pub fn update(&self, value: T) { - self.state_cell.set(value); - self.channel.publish_immediate(value); + self.state.update(value); + self.publisher.update(value); } } impl Stateful - for RawStatePubInput + for StatefulPublisher { type Value = T; #[inline(always)] fn state_cell(&self) -> CellView { - (&self.state_cell).into() + self.state.state_cell() } #[inline(always)] fn state(&self) -> Self::Value { - self.state_cell.get() + self.state.state() } } impl - Publisher for RawStatePubInput + Publish for StatefulPublisher { type Value = T; type Mutex = MutexT; + #[inline(always)] fn subscribe( &self, ) -> Result, Error> { - self.channel.subscriber() + self.publisher.subscribe() } } diff --git a/node/src/transducer/mod.rs b/node/src/transducer/mod.rs index c698b30..a9ac243 100644 --- a/node/src/transducer/mod.rs +++ b/node/src/transducer/mod.rs @@ -8,10 +8,10 @@ use embassy_sync::blocking_mutex::raw::RawMutex; #[cfg(feature = "embassy-sync")] use embassy_sync::pubsub; #[cfg(feature = "embassy-sync")] -use embassy_sync::pubsub::Subscriber; +use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel, Subscriber}; #[cfg(feature = "embassy-sync")] -pub trait Publisher { +pub trait Publish { type Value: Copy; type Mutex: RawMutex; @@ -19,3 +19,46 @@ pub trait Publisher { &self, ) -> Result, pubsub::Error>; } + +#[cfg(feature = "embassy-sync")] +pub struct Publisher { + channel: PubSubChannel, +} + +impl + Publisher +{ + #[inline(always)] + pub fn new(channel: PubSubChannel) -> Self { + Self { channel } + } + + #[inline(always)] + pub fn update(&self, value: T) { + self.channel.publish_immediate(value); + } +} + +impl + Publish for Publisher +{ + type Value = T; + type Mutex = MutexT; + + #[inline(always)] + fn subscribe( + &self, + ) -> Result, pubsub::Error> { + self.channel.subscriber() + } +} + +impl +From> +for Publisher +{ + #[inline(always)] + fn from(channel: PubSubChannel) -> Self { + Publisher::new(channel) + } +} diff --git a/src/transducer/input.rs b/src/transducer/input.rs index 12540ba..e6b07fe 100644 --- a/src/transducer/input.rs +++ b/src/transducer/input.rs @@ -5,33 +5,3 @@ use crate::transducer::Stateful; pub trait Poll { async fn poll() -> T; } - -pub struct RawStatefulInput { - pub state_cell: Cell, -} - -impl RawStatefulInput { - #[inline(always)] - pub fn new(state_cell: Cell) -> Self { - Self { state_cell } - } - - #[inline(always)] - pub fn raw_update(&self, value: T) { - self.state_cell.set(value); - } -} - -impl Stateful for RawStatefulInput { - type Value = T; - - #[inline(always)] - fn state_cell(&self) -> CellView { - (&self.state_cell).into() - } - - #[inline(always)] - fn state(&self) -> Self::Value { - self.state_cell.get() - } -} diff --git a/src/transducer/mod.rs b/src/transducer/mod.rs index eada613..8948ca0 100644 --- a/src/transducer/mod.rs +++ b/src/transducer/mod.rs @@ -1,3 +1,4 @@ +use core::cell::Cell; use crate::cell::CellView; pub mod input; @@ -11,8 +12,42 @@ pub trait Stateful { fn state_cell(&self) -> CellView; + fn state(&self) -> Self::Value; +} + +pub struct State { + state_cell: Cell, +} + +impl State { #[inline(always)] - fn state(&self) -> Self::Value { - self.state_cell().get() + pub fn new(state_cell: Cell) -> Self { + Self { state_cell } + } + + #[inline(always)] + pub fn update(&self, value: T) { + self.state_cell.set(value); + } +} + +impl Stateful for State { + type Value = T; + + #[inline(always)] + fn state_cell(&self) -> CellView { + (&self.state_cell).into() + } + + #[inline(always)] + fn state(&self) -> Self::Value { + self.state_cell.get() + } +} + +impl From> for State { + #[inline(always)] + fn from(state_cell: Cell) -> Self { + State::new(state_cell) } } diff --git a/src/transducer/output.rs b/src/transducer/output.rs index 989830e..be5e963 100644 --- a/src/transducer/output.rs +++ b/src/transducer/output.rs @@ -3,5 +3,5 @@ pub trait Output { //TODO: return result //TODO: Make maybe async - fn set(&mut self, setting: Self::Value); + fn output(&mut self, setting: Self::Value); } \ No newline at end of file -- 2.43.0 From 07ae111e150b017438a3e698aa5e5bc1275ef6d4 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Sat, 17 Jun 2023 10:43:52 -0700 Subject: [PATCH 25/51] Switch to fully composition based Transducers. --- src/transducer/input.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/transducer/input.rs b/src/transducer/input.rs index e6b07fe..d03cc81 100644 --- a/src/transducer/input.rs +++ b/src/transducer/input.rs @@ -1,6 +1,3 @@ -use core::cell::Cell; -use crate::cell::CellView; -use crate::transducer::Stateful; pub trait Poll { async fn poll() -> T; -- 2.43.0 From ce72a8a5be28651b59bf2c9bb05ad5b1b17b4163 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Sun, 18 Jun 2023 20:49:30 -0700 Subject: [PATCH 26/51] Device macro to generate stateful and publish variants of device specific poll inputs. --- Cargo.toml | 10 ++++ macros/node-poll-variants/Cargo.toml | 23 ++++++++++ macros/node-poll-variants/src/lib.rs | 46 +++++++++++++++++++ macros/node-poll-variants/tests/generate.rs | 11 +++++ macros/node-poll-variants/tests/test_build.rs | 5 ++ src/transducer/output.rs | 10 ++-- 6 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 macros/node-poll-variants/Cargo.toml create mode 100644 macros/node-poll-variants/src/lib.rs create mode 100644 macros/node-poll-variants/tests/generate.rs create mode 100644 macros/node-poll-variants/tests/test_build.rs diff --git a/Cargo.toml b/Cargo.toml index 83426da..6bcb4e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ members = [ "peripherals/standalone-ads1256/*", # Peripheral components "peripheral-components/ads1256/*", + # Macros + "macros/node-poll-variants", # Examples "examples/ads1256" ] @@ -87,6 +89,14 @@ features = ["defmt", "unstable-traits", "unstable-pac", "nightly"] [workspace.dependencies.embassy-nrf] version = "0.1.*" features = ["defmt", "unstable-traits", "nightly"] +# Macros +[workspace.dependencies.syn] +version = "2.0.*" +features = ["extra-traits"] +[workspace.dependencies.quote] +version = "1.0.*" +[workspace.dependencies.trybuild] +version = "1.0.*" #--------------------------------------------------------------------------------------------------------------------- #----- Patch ------------------------ diff --git a/macros/node-poll-variants/Cargo.toml b/macros/node-poll-variants/Cargo.toml new file mode 100644 index 0000000..85c0a16 --- /dev/null +++ b/macros/node-poll-variants/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "node-poll-variants" +description = "Macros for physical nodes." +version.workspace = true +edition.workspace = true +repository.workspace = true +readme.workspace = true +license.workspace = true + +[lib] +proc-macro = true + +[[test]] +name = "tests" +path = "tests/test_build.rs" + +[dependencies.syn] +workspace = true +[dependencies.quote] +workspace = true + +[dev-dependencies.trybuild] +workspace = true \ No newline at end of file diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs new file mode 100644 index 0000000..a75bde3 --- /dev/null +++ b/macros/node-poll-variants/src/lib.rs @@ -0,0 +1,46 @@ +use proc_macro::TokenStream; +use std::ops::Deref; +use quote::quote; +use syn::{parse_macro_input, DeriveInput, Data, Ident}; + +// Struct name: Ads1256PollStateful, Ads1256PollPublish, Ads1256PollStatePub +#[proc_macro_derive(PollVariants)] +pub fn transducer_macro(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let DeriveInput { + attrs, + vis, + ident, + generics, + data, + } = &input; + // Check that item the macro was used on is valid + match data { + Data::Struct(struct_data) => struct_data, + Data::Enum(_) => panic!("Stateful struct cannot be derived from an enum."), + Data::Union(_) => panic!("Stateful struct cannot be derived from a union.") + }; + // Check if generics contains T + + // If it does make sure it is T: Copy + // If it isn't Copy, panic + + // If it doesn't contain T, add it for use in the type that will be generated + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let stateful_name = format!("{ident}Stateful"); + let stateful_ident = Ident::new(stateful_name.deref(), ident.span()); + + eprintln!("input: {input:#?}"); + eprintln!("impl_generics: {impl_generics:#?}"); + + let expanded = quote! { + #vis struct #stateful_ident #generics { + pub poll: #ident #ty_generics, + pub state: physical::transducer::State, + } + }; + + TokenStream::from(expanded) +} diff --git a/macros/node-poll-variants/tests/generate.rs b/macros/node-poll-variants/tests/generate.rs new file mode 100644 index 0000000..d24d483 --- /dev/null +++ b/macros/node-poll-variants/tests/generate.rs @@ -0,0 +1,11 @@ +use node_poll_variants::{PollVariants}; + +#[derive(PollVariants)] +struct ExamplePoll<'a, FirstT: Copy, SecondT> { + a: &'a i32, + b: i32, + first: FirstT, + second: SecondT, +} + +fn main() {} diff --git a/macros/node-poll-variants/tests/test_build.rs b/macros/node-poll-variants/tests/test_build.rs new file mode 100644 index 0000000..6661c9d --- /dev/null +++ b/macros/node-poll-variants/tests/test_build.rs @@ -0,0 +1,5 @@ +#[test] +fn tests() { + let t = trybuild::TestCases::new(); + t.pass("tests/generate.rs"); +} \ No newline at end of file diff --git a/src/transducer/output.rs b/src/transducer/output.rs index be5e963..eb43a0d 100644 --- a/src/transducer/output.rs +++ b/src/transducer/output.rs @@ -1,7 +1,9 @@ pub trait Output { type Value: Copy; - //TODO: return result - //TODO: Make maybe async - fn output(&mut self, setting: Self::Value); -} \ No newline at end of file + //TODO: Should this be maybe async? + fn output(&mut self, setting: Self::Value) -> Result<(), InvalidOutputSetting>; +} + +/// Indicates the setting given for an [Output] is statically known to be an impossible setting to achieve. +pub struct InvalidOutputSetting; \ No newline at end of file -- 2.43.0 From 9ea0e69d925fe4c5a62a46ca692b7d449c593762 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Sun, 18 Jun 2023 20:49:42 -0700 Subject: [PATCH 27/51] Device macro to generate stateful and publish variants of device specific poll inputs. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 8b6a8b5..6175e3f 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ cmake-build-*/ # File-based project format *.iws + +*.stderr \ No newline at end of file -- 2.43.0 From 0c7839491d2ff71253825230374644fea40fc566 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Mon, 19 Jun 2023 19:24:47 -0700 Subject: [PATCH 28/51] Device macro to generate stateful and publish variants of device specific poll inputs. --- macros/node-poll-variants/Cargo.toml | 4 +- macros/node-poll-variants/src/lib.rs | 160 +++++++++++++++++--- macros/node-poll-variants/tests/generate.rs | 22 ++- node/src/transducer/input.rs | 67 -------- node/src/transducer/mod.rs | 62 +------- node/src/transducer/sync/input.rs | 61 ++++++++ node/src/transducer/sync/mod.rs | 58 +++++++ src/transducer/input.rs | 5 +- 8 files changed, 283 insertions(+), 156 deletions(-) create mode 100644 node/src/transducer/sync/input.rs create mode 100644 node/src/transducer/sync/mod.rs diff --git a/macros/node-poll-variants/Cargo.toml b/macros/node-poll-variants/Cargo.toml index 85c0a16..332edc4 100644 --- a/macros/node-poll-variants/Cargo.toml +++ b/macros/node-poll-variants/Cargo.toml @@ -20,4 +20,6 @@ workspace = true workspace = true [dev-dependencies.trybuild] -workspace = true \ No newline at end of file +workspace = true +[dev-dependencies.physical-node] +path = "../../node" \ No newline at end of file diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index a75bde3..03675f3 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -1,45 +1,155 @@ use proc_macro::TokenStream; -use std::ops::Deref; use quote::quote; -use syn::{parse_macro_input, DeriveInput, Data, Ident}; +use std::ops::Deref; +use std::path::Path; +use syn::__private::Span; +use syn::punctuated::Punctuated; +use syn::token::{Colon, PathSep, Plus}; +use syn::{parse_macro_input, Data, DeriveInput, GenericParam, Ident, PathSegment, Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, Meta, Expr, Lit}; // Struct name: Ads1256PollStateful, Ads1256PollPublish, Ads1256PollStatePub -#[proc_macro_derive(PollVariants)] -pub fn transducer_macro(input: TokenStream) -> TokenStream { +#[proc_macro_derive(PollVariants, attributes(value_type))] +pub fn poll_variant_macro(input: TokenStream) -> TokenStream { + // ----- Parse input ---------------------------------- let input = parse_macro_input!(input as DeriveInput); - let DeriveInput { - attrs, - vis, - ident, - generics, - data, - } = &input; - // Check that item the macro was used on is valid + let attrs = &input.attrs; + let vis = &input.vis; + let ident = &input.ident; + let data = &input.data; + let og_generics = &input.generics; + let (og_impl_generics, og_type_generics, og_where_clause) = &og_generics.split_for_impl(); + + // ----- Check that item the macro was used on is a struct ---------------------------------- match data { Data::Struct(struct_data) => struct_data, Data::Enum(_) => panic!("Stateful struct cannot be derived from an enum."), - Data::Union(_) => panic!("Stateful struct cannot be derived from a union.") + Data::Union(_) => panic!("Stateful struct cannot be derived from a union."), }; - // Check if generics contains T - // If it does make sure it is T: Copy - // If it isn't Copy, panic + // ----- Extract value type attribute ---------------------------------- + const VALUE_T_NAME: &str = "value_type"; + let mut value_type_ident: Option = None; + for attribute in attrs.iter() { + // if the attribute is a named value + if let Meta::NameValue(meta) = &attribute.meta { + // if the name of the attribute is value_t + if meta.path.segments[0].ident == VALUE_T_NAME { + // if the value of the attribute is a literal + if let Expr::Lit(lit) = &meta.value { + // if the literal is a string + if let Lit::Str(lit) = &lit.lit { + value_type_ident = Some(Ident::new(lit.value().deref(), Span::call_site())); + } else { + panic!("{VALUE_T_NAME} must be set with a string literal.") + } + } else { + panic!("{VALUE_T_NAME} must be set with a literal.") + } + } else { + continue; + } + } else { + continue; + } + }; + let value_type_ident = if let Some(val) = value_type_ident { + val + } else { + panic!("Need attribute {VALUE_T_NAME}: #[{VALUE_T_NAME} = \"type\"]") + }; - // If it doesn't contain T, add it for use in the type that will be generated + // ----- Build publisher generics ---------------------------------- + //TODO: Get rid of all this adding T stuff - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // // Check if generics contains T + // const T_IDENT_STR: &str = "T"; + // const COPY_IDENT_STR: &str = "Copy"; + // let mut first_type_index: Option = None; + // let mut has_t = false; + // for (index, param) in og_generics.params.iter().enumerate() { + // // If the generic parameter is a type + // if let GenericParam::Type(param) = param { + // if first_type_index.is_none() { + // first_type_index = Some(index); + // } + // // If the generic parameter is T + // if param.ident == T_IDENT_STR { + // has_t = true; + // } + // } + // } - let stateful_name = format!("{ident}Stateful"); - let stateful_ident = Ident::new(stateful_name.deref(), ident.span()); + // let mut generics_with_t = og_generics.clone(); + // // If T is not a generic parameter, add it + // if !has_t { + // let first_type_index = first_type_index.unwrap_or(0); + // let t_ident = Ident::new(T_IDENT_STR, Span::call_site()); + // let copy_ident = Ident::new(COPY_IDENT_STR, Span::call_site()); + // let mut t_bounds: Punctuated = Punctuated::new(); + // + // let t_bound = TraitBound { + // paren_token: None, + // modifier: TraitBoundModifier::None, + // lifetimes: None, + // path: copy_ident.into(), + // }; + // t_bounds.push(t_bound.into()); + // + // let t_param: TypeParam = TypeParam { + // attrs: Vec::new(), + // ident: t_ident, + // colon_token: Some(Colon::default()), + // bounds: t_bounds, + // eq_token: None, + // default: None, + // }; + // generics_with_t + // .params + // .insert(first_type_index, t_param.into()); + // } - eprintln!("input: {input:#?}"); - eprintln!("impl_generics: {impl_generics:#?}"); + let stateful_ident = Ident::new(format!("{ident}Stateful").deref(), ident.span()); let expanded = quote! { - #vis struct #stateful_ident #generics { - pub poll: #ident #ty_generics, - pub state: physical::transducer::State, + // ----- Stateful struct ---------------------------------- + #vis struct #stateful_ident #og_generics #og_where_clause { + pub poll: #ident #og_type_generics, + pub state: physical_node::transducer::State<#value_type_ident>, } + + // ----- Stateful impls ---------------------------------- + impl #og_impl_generics physical_node::transducer::Stateful for #stateful_ident #og_type_generics #og_where_clause { + type Value = #value_type_ident; + + #[inline(always)] + fn state_cell(&self) -> physical_node::cell::CellView { + self.state.state_cell() + } + + #[inline(always)] + fn state(&self) -> Self::Value { + self.state.state() + } + } + + impl #og_impl_generics physical_node::transducer::input::Poll for #stateful_ident #og_type_generics #og_where_clause { + type Value = #value_type_ident; + + #[inline(always)] + async fn poll(&self) -> Self::Value { + let value = self.poll.poll().await; + self.state.update(value); + value + } + } + + // ----- Publish struct ---------------------------------- + + // ----- Publish impl ---------------------------------- + + // ----- StatePub struct ---------------------------------- + + // ----- StatePub impl ---------------------------------- }; TokenStream::from(expanded) diff --git a/macros/node-poll-variants/tests/generate.rs b/macros/node-poll-variants/tests/generate.rs index d24d483..01cf375 100644 --- a/macros/node-poll-variants/tests/generate.rs +++ b/macros/node-poll-variants/tests/generate.rs @@ -1,11 +1,29 @@ -use node_poll_variants::{PollVariants}; +#![feature(async_fn_in_trait, impl_trait_projections)] + +use node_poll_variants::PollVariants; +use physical_node::transducer::input::Poll; #[derive(PollVariants)] -struct ExamplePoll<'a, FirstT: Copy, SecondT> { +#[value_type = "SecondT"] +struct ExamplePoll<'a, FirstT, SecondT> +where + SecondT: Copy, +{ a: &'a i32, b: i32, first: FirstT, second: SecondT, } +impl<'a, FirstT, SecondT> Poll for ExamplePoll<'a, FirstT, SecondT> +where + SecondT: Copy, +{ + type Value = SecondT; + + async fn poll(&self) -> Self::Value { + self.second + } +} + fn main() {} diff --git a/node/src/transducer/input.rs b/node/src/transducer/input.rs index dedbf72..84eb5d9 100644 --- a/node/src/transducer/input.rs +++ b/node/src/transducer/input.rs @@ -1,68 +1 @@ -use crate::cell::CellView; -use crate::transducer::{Publish, Publisher}; -use core::cell::Cell; -#[cfg(feature = "embassy-sync")] -use embassy_sync::blocking_mutex::raw::RawMutex; -#[cfg(feature = "embassy-sync")] -use embassy_sync::pubsub::{Error, PubSubChannel, Subscriber}; pub use physical::transducer::input::*; -use physical::transducer::{State, Stateful}; - -#[cfg(feature = "embassy-sync")] -pub struct StatefulPublisher< - T: Copy, - MutexT: RawMutex, - const CAPACITY: usize, - const NUM_SUBS: usize, -> { - pub state: State, - pub publisher: Publisher, -} - -impl - StatefulPublisher -{ - #[inline(always)] - pub fn new( - state: State, - publisher: Publisher, - ) -> Self { - Self { state, publisher } - } - - #[inline(always)] - pub fn update(&self, value: T) { - self.state.update(value); - self.publisher.update(value); - } -} - -impl Stateful - for StatefulPublisher -{ - type Value = T; - - #[inline(always)] - fn state_cell(&self) -> CellView { - self.state.state_cell() - } - - #[inline(always)] - fn state(&self) -> Self::Value { - self.state.state() - } -} - -impl - Publish for StatefulPublisher -{ - type Value = T; - type Mutex = MutexT; - - #[inline(always)] - fn subscribe( - &self, - ) -> Result, Error> { - self.publisher.subscribe() - } -} diff --git a/node/src/transducer/mod.rs b/node/src/transducer/mod.rs index a9ac243..e8e7c3d 100644 --- a/node/src/transducer/mod.rs +++ b/node/src/transducer/mod.rs @@ -1,64 +1,8 @@ pub mod input; pub mod output; +#[cfg(feature = "embassy-sync")] +mod sync; pub use physical::transducer::*; - #[cfg(feature = "embassy-sync")] -use embassy_sync::blocking_mutex::raw::RawMutex; -#[cfg(feature = "embassy-sync")] -use embassy_sync::pubsub; -#[cfg(feature = "embassy-sync")] -use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel, Subscriber}; - -#[cfg(feature = "embassy-sync")] -pub trait Publish { - type Value: Copy; - type Mutex: RawMutex; - - fn subscribe( - &self, - ) -> Result, pubsub::Error>; -} - -#[cfg(feature = "embassy-sync")] -pub struct Publisher { - channel: PubSubChannel, -} - -impl - Publisher -{ - #[inline(always)] - pub fn new(channel: PubSubChannel) -> Self { - Self { channel } - } - - #[inline(always)] - pub fn update(&self, value: T) { - self.channel.publish_immediate(value); - } -} - -impl - Publish for Publisher -{ - type Value = T; - type Mutex = MutexT; - - #[inline(always)] - fn subscribe( - &self, - ) -> Result, pubsub::Error> { - self.channel.subscriber() - } -} - -impl -From> -for Publisher -{ - #[inline(always)] - fn from(channel: PubSubChannel) -> Self { - Publisher::new(channel) - } -} +pub use sync::*; diff --git a/node/src/transducer/sync/input.rs b/node/src/transducer/sync/input.rs new file mode 100644 index 0000000..e66a306 --- /dev/null +++ b/node/src/transducer/sync/input.rs @@ -0,0 +1,61 @@ +use crate::cell::CellView; +use crate::transducer::{Publish, Publisher}; +use core::cell::Cell; +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::pubsub::{Error, PubSubChannel, Subscriber}; +use physical::transducer::{State, Stateful}; + +pub struct StatefulPublisher< + T: Copy, + MutexT: RawMutex, + const CAPACITY: usize, + const NUM_SUBS: usize, +> { + pub state: State, + pub publisher: Publisher, +} + +impl + StatefulPublisher +{ + #[inline(always)] + pub fn new(state: State, publisher: Publisher) -> Self { + Self { state, publisher } + } + + #[inline(always)] + pub fn update(&self, value: T) { + self.state.update(value); + self.publisher.update(value); + } +} + +impl Stateful + for StatefulPublisher +{ + type Value = T; + + #[inline(always)] + fn state_cell(&self) -> CellView { + self.state.state_cell() + } + + #[inline(always)] + fn state(&self) -> Self::Value { + self.state.state() + } +} + +impl + Publish for StatefulPublisher +{ + type Value = T; + type Mutex = MutexT; + + #[inline(always)] + fn subscribe( + &self, + ) -> Result, Error> { + self.publisher.subscribe() + } +} diff --git a/node/src/transducer/sync/mod.rs b/node/src/transducer/sync/mod.rs new file mode 100644 index 0000000..0ac16ba --- /dev/null +++ b/node/src/transducer/sync/mod.rs @@ -0,0 +1,58 @@ +mod input; + +pub use input::*; + +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::pubsub; +use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel, Subscriber}; + +pub trait Publish { + type Value: Copy; + type Mutex: RawMutex; + + fn subscribe( + &self, + ) -> Result, pubsub::Error>; +} + +pub struct Publisher { + channel: PubSubChannel, +} + +impl +Publisher +{ + #[inline(always)] + pub fn new(channel: PubSubChannel) -> Self { + Self { channel } + } + + #[inline(always)] + pub fn update(&self, value: T) { + self.channel.publish_immediate(value); + } +} + +impl +Publish for Publisher +{ + type Value = T; + type Mutex = MutexT; + + #[inline(always)] + fn subscribe( + &self, + ) -> Result, pubsub::Error> { + self.channel.subscriber() + } +} + +impl +From> +for Publisher +{ + #[inline(always)] + fn from(channel: PubSubChannel) -> Self { + Publisher::new(channel) + } +} \ No newline at end of file diff --git a/src/transducer/input.rs b/src/transducer/input.rs index d03cc81..43b8fa4 100644 --- a/src/transducer/input.rs +++ b/src/transducer/input.rs @@ -1,4 +1,5 @@ +pub trait Poll { + type Value: Copy; -pub trait Poll { - async fn poll() -> T; + async fn poll(&self) -> Self::Value; } -- 2.43.0 From ec37a343f65731c9c275a64651c32f74082372c1 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 20 Jun 2023 15:35:39 -0700 Subject: [PATCH 29/51] Device macro to generate stateful and publish variants of device specific poll inputs. --- macros/node-poll-variants/src/lib.rs | 164 +++++++++++++++++---------- 1 file changed, 105 insertions(+), 59 deletions(-) diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index 03675f3..490a7af 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -1,11 +1,13 @@ use proc_macro::TokenStream; use quote::quote; use std::ops::Deref; -use std::path::Path; use syn::__private::Span; use syn::punctuated::Punctuated; use syn::token::{Colon, PathSep, Plus}; -use syn::{parse_macro_input, Data, DeriveInput, GenericParam, Ident, PathSegment, Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, Meta, Expr, Lit}; +use syn::{ + parse_macro_input, Data, DeriveInput, Expr, GenericParam, Ident, Lit, Meta, Path, PathSegment, + Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, +}; // Struct name: Ads1256PollStateful, Ads1256PollPublish, Ads1256PollStatePub #[proc_macro_derive(PollVariants, attributes(value_type))] @@ -31,10 +33,10 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { let mut value_type_ident: Option = None; for attribute in attrs.iter() { // if the attribute is a named value - if let Meta::NameValue(meta) = &attribute.meta { - // if the name of the attribute is value_t - if meta.path.segments[0].ident == VALUE_T_NAME { - // if the value of the attribute is a literal + if let Meta::NameValue(meta) = &attribute.meta { + // if the name of the attribute is value_t + if meta.path.segments[0].ident == VALUE_T_NAME { + // if the value of the attribute is a literal if let Expr::Lit(lit) = &meta.value { // if the literal is a string if let Lit::Str(lit) = &lit.lit { @@ -45,13 +47,13 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } else { panic!("{VALUE_T_NAME} must be set with a literal.") } - } else { - continue; - } - } else { - continue; - } - }; + } else { + continue; + } + } else { + continue; + } + } let value_type_ident = if let Some(val) = value_type_ident { val } else { @@ -59,56 +61,95 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { }; // ----- Build publisher generics ---------------------------------- - //TODO: Get rid of all this adding T stuff + // Check if generics contains T + const MUTEX_T_IDENT_STR: &str = "MutexT"; + let mut num_lifetimes: usize = 0; + let mut num_types: usize = 0; + let mut num_const: usize = 0; + let mut has_mutex_t = false; + for param in og_generics.params.iter() { + match param { + GenericParam::Lifetime(_) => num_lifetimes += 1, + GenericParam::Type(param) => { + num_types += 1; - // // Check if generics contains T - // const T_IDENT_STR: &str = "T"; - // const COPY_IDENT_STR: &str = "Copy"; - // let mut first_type_index: Option = None; - // let mut has_t = false; - // for (index, param) in og_generics.params.iter().enumerate() { - // // If the generic parameter is a type - // if let GenericParam::Type(param) = param { - // if first_type_index.is_none() { - // first_type_index = Some(index); - // } - // // If the generic parameter is T - // if param.ident == T_IDENT_STR { - // has_t = true; - // } - // } - // } + // If the generic parameter is T + if param.ident == MUTEX_T_IDENT_STR { + has_mutex_t = true; + } + }, + GenericParam::Const(_) => num_const += 1, + } + } - // let mut generics_with_t = og_generics.clone(); - // // If T is not a generic parameter, add it - // if !has_t { - // let first_type_index = first_type_index.unwrap_or(0); - // let t_ident = Ident::new(T_IDENT_STR, Span::call_site()); - // let copy_ident = Ident::new(COPY_IDENT_STR, Span::call_site()); - // let mut t_bounds: Punctuated = Punctuated::new(); - // - // let t_bound = TraitBound { - // paren_token: None, - // modifier: TraitBoundModifier::None, - // lifetimes: None, - // path: copy_ident.into(), - // }; - // t_bounds.push(t_bound.into()); - // - // let t_param: TypeParam = TypeParam { - // attrs: Vec::new(), - // ident: t_ident, - // colon_token: Some(Colon::default()), - // bounds: t_bounds, - // eq_token: None, - // default: None, - // }; - // generics_with_t - // .params - // .insert(first_type_index, t_param.into()); - // } + let mut publish_generics = og_generics.clone(); + // If MutexT is not a generic parameter, add it + if !has_mutex_t { + let mutex_t_ident = Ident::new(MUTEX_T_IDENT_STR, Span::call_site()); + let mut mutex_t_bounds: Punctuated = Punctuated::new(); + + let mut path: Punctuated = Punctuated::new(); + path.push(Ident::new("embassy_sync", Span::call_site()).into()); + path.push(Ident::new("blocking_mutex", Span::call_site()).into()); + path.push(Ident::new("raw", Span::call_site()).into()); + path.push(Ident::new("RawMutex", Span::call_site()).into()); + let raw_mutex_path = Path { + leading_colon: None, + segments: path, + }; + + let mutex_t_bound = TraitBound { + paren_token: None, + modifier: TraitBoundModifier::None, + lifetimes: None, + path: raw_mutex_path, + }; + mutex_t_bounds.push(mutex_t_bound.into()); + + let mutex_t_param: TypeParam = TypeParam { + attrs: Vec::new(), + ident: mutex_t_ident, + colon_token: Some(Colon::default()), + bounds: mutex_t_bounds, + eq_token: None, + default: None, + }; + + let num_generics = num_lifetimes + num_types + num_const; + // If there are generics + if num_generics > 0 { + // If all generics are lifetimes + if num_lifetimes == num_generics { + // Add MutexT after the lifetimes + publish_generics.params.push(mutex_t_param.into()); + // If no generics are lifetimes + } else if num_lifetimes == 0 { + // Insert MutexT at the front + publish_generics + .params + .insert(0, mutex_t_param.into()); + // If there are lifetimes and other generics + } else { + // Insert MutexT after the lifetimes + publish_generics + .params + .insert(num_lifetimes, mutex_t_param.into()); + } + // If there are no generics + } else { + // Add MutexT + publish_generics.params.push(mutex_t_param.into()); + } + } + // const generics + + + let (publ_impl_generics, publ_type_generics, publ_where_clause) = + &publish_generics.split_for_impl(); let stateful_ident = Ident::new(format!("{ident}Stateful").deref(), ident.span()); + let publish_ident = Ident::new(format!("{ident}Publish").deref(), ident.span()); + let state_pub_ident = Ident::new(format!("{ident}StatePub").deref(), ident.span()); let expanded = quote! { // ----- Stateful struct ---------------------------------- @@ -144,6 +185,11 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } // ----- Publish struct ---------------------------------- + #[cfg(feature = "embassy-sync")] + #vis struct #publish_ident #publish_generics #publ_where_clause { + pub poll: #ident #og_type_generics, + pub publisher: physical_node::transducer::Publisher<#value_type_ident, MutexT, CAPACITY, NUM_SUBS>, + } // ----- Publish impl ---------------------------------- -- 2.43.0 From 8557ed19fd64ce8a0301730bcad1fc17eb769836 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 20 Jun 2023 16:59:28 -0700 Subject: [PATCH 30/51] Device macro to generate stateful and publish variants of device specific poll inputs. --- Cargo.toml | 2 +- macros/node-poll-variants/Cargo.toml | 5 +- macros/node-poll-variants/src/lib.rs | 69 ++++++++++------------------ 3 files changed, 29 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6bcb4e7..5245fb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ features = ["defmt", "unstable-traits", "nightly"] # Macros [workspace.dependencies.syn] version = "2.0.*" -features = ["extra-traits"] +features = ["extra-traits", "parsing"] [workspace.dependencies.quote] version = "1.0.*" [workspace.dependencies.trybuild] diff --git a/macros/node-poll-variants/Cargo.toml b/macros/node-poll-variants/Cargo.toml index 332edc4..46ea342 100644 --- a/macros/node-poll-variants/Cargo.toml +++ b/macros/node-poll-variants/Cargo.toml @@ -22,4 +22,7 @@ workspace = true [dev-dependencies.trybuild] workspace = true [dev-dependencies.physical-node] -path = "../../node" \ No newline at end of file +path = "../../node" +features = ["embassy-sync"] +[dev-dependencies.embassy-sync] +workspace = true \ No newline at end of file diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index 490a7af..e968db1 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -5,8 +5,8 @@ use syn::__private::Span; use syn::punctuated::Punctuated; use syn::token::{Colon, PathSep, Plus}; use syn::{ - parse_macro_input, Data, DeriveInput, Expr, GenericParam, Ident, Lit, Meta, Path, PathSegment, - Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, + parse_macro_input, parse_quote, Data, DeriveInput, Expr, GenericParam, Ident, Lit, Meta, Path, + PathSegment, Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, }; // Struct name: Ads1256PollStateful, Ads1256PollPublish, Ads1256PollStatePub @@ -60,9 +60,14 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { panic!("Need attribute {VALUE_T_NAME}: #[{VALUE_T_NAME} = \"type\"]") }; - // ----- Build publisher generics ---------------------------------- - // Check if generics contains T - const MUTEX_T_IDENT_STR: &str = "MutexT"; + // ----- Add publisher generics ---------------------------------- + // MutexT + const MUTEX_T_NAME: &str = "MutexT"; + let mutex_t_ident = Ident::new(MUTEX_T_NAME, Span::call_site()); + const CAPACITY_NAME: &str = "CAPACITY"; + let capacity_ident = Ident::new(CAPACITY_NAME, Span::call_site()); + const NUM_SUBS_NAME: &str = "NUM_SUBS"; + let num_subs_ident = Ident::new(NUM_SUBS_NAME, Span::call_site()); let mut num_lifetimes: usize = 0; let mut num_types: usize = 0; let mut num_const: usize = 0; @@ -73,8 +78,8 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { GenericParam::Type(param) => { num_types += 1; - // If the generic parameter is T - if param.ident == MUTEX_T_IDENT_STR { + // If the generic parameter is MutexT + if param.ident == MUTEX_T_NAME { has_mutex_t = true; } }, @@ -85,35 +90,8 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { let mut publish_generics = og_generics.clone(); // If MutexT is not a generic parameter, add it if !has_mutex_t { - let mutex_t_ident = Ident::new(MUTEX_T_IDENT_STR, Span::call_site()); - let mut mutex_t_bounds: Punctuated = Punctuated::new(); - - let mut path: Punctuated = Punctuated::new(); - path.push(Ident::new("embassy_sync", Span::call_site()).into()); - path.push(Ident::new("blocking_mutex", Span::call_site()).into()); - path.push(Ident::new("raw", Span::call_site()).into()); - path.push(Ident::new("RawMutex", Span::call_site()).into()); - let raw_mutex_path = Path { - leading_colon: None, - segments: path, - }; - - let mutex_t_bound = TraitBound { - paren_token: None, - modifier: TraitBoundModifier::None, - lifetimes: None, - path: raw_mutex_path, - }; - mutex_t_bounds.push(mutex_t_bound.into()); - - let mutex_t_param: TypeParam = TypeParam { - attrs: Vec::new(), - ident: mutex_t_ident, - colon_token: Some(Colon::default()), - bounds: mutex_t_bounds, - eq_token: None, - default: None, - }; + let mutex_t_param: GenericParam = + parse_quote!(#mutex_t_ident: embassy_sync::blocking_mutex::raw::RawMutex); let num_generics = num_lifetimes + num_types + num_const; // If there are generics @@ -121,28 +99,29 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { // If all generics are lifetimes if num_lifetimes == num_generics { // Add MutexT after the lifetimes - publish_generics.params.push(mutex_t_param.into()); + publish_generics.params.push(mutex_t_param); // If no generics are lifetimes } else if num_lifetimes == 0 { // Insert MutexT at the front - publish_generics - .params - .insert(0, mutex_t_param.into()); + publish_generics.params.insert(0, mutex_t_param); // If there are lifetimes and other generics } else { // Insert MutexT after the lifetimes publish_generics .params - .insert(num_lifetimes, mutex_t_param.into()); + .insert(num_lifetimes, mutex_t_param); } // If there are no generics } else { // Add MutexT - publish_generics.params.push(mutex_t_param.into()); + publish_generics.params.push(mutex_t_param); } } // const generics - + let capacity_param: GenericParam = parse_quote!(const #capacity_ident: usize); + let num_subs_param: GenericParam = parse_quote!(const #num_subs_ident: usize); + publish_generics.params.push(capacity_param); + publish_generics.params.push(num_subs_param); let (publ_impl_generics, publ_type_generics, publ_where_clause) = &publish_generics.split_for_impl(); @@ -185,10 +164,10 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } // ----- Publish struct ---------------------------------- - #[cfg(feature = "embassy-sync")] + //#[cfg(feature = "embassy-sync")] #vis struct #publish_ident #publish_generics #publ_where_clause { pub poll: #ident #og_type_generics, - pub publisher: physical_node::transducer::Publisher<#value_type_ident, MutexT, CAPACITY, NUM_SUBS>, + pub publisher: physical_node::transducer::Publisher<#value_type_ident, #mutex_t_ident, #capacity_ident, #num_subs_ident>, } // ----- Publish impl ---------------------------------- -- 2.43.0 From 996bc6643120db9a87419c2639558061a5015346 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 20 Jun 2023 18:17:11 -0700 Subject: [PATCH 31/51] Device macro to generate stateful and publish variants of device specific poll inputs. --- macros/node-poll-variants/src/lib.rs | 71 ++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index e968db1..b8e4015 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -3,10 +3,11 @@ use quote::quote; use std::ops::Deref; use syn::__private::Span; use syn::punctuated::Punctuated; -use syn::token::{Colon, PathSep, Plus}; +use syn::token::{Colon, Comma, PathSep, Plus}; use syn::{ - parse_macro_input, parse_quote, Data, DeriveInput, Expr, GenericParam, Ident, Lit, Meta, Path, - PathSegment, Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, + parse_macro_input, parse_quote, parse_quote_spanned, Data, DeriveInput, Expr, GenericParam, + Generics, Ident, Lit, Meta, Path, PathSegment, Token, TraitBound, TraitBoundModifier, + TypeParam, TypeParamBound, }; // Struct name: Ads1256PollStateful, Ads1256PollPublish, Ads1256PollStatePub @@ -126,19 +127,37 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { let (publ_impl_generics, publ_type_generics, publ_where_clause) = &publish_generics.split_for_impl(); - let stateful_ident = Ident::new(format!("{ident}Stateful").deref(), ident.span()); - let publish_ident = Ident::new(format!("{ident}Publish").deref(), ident.span()); - let state_pub_ident = Ident::new(format!("{ident}StatePub").deref(), ident.span()); + let pubsub_error_path: Path = parse_quote!(embassy_sync::pubsub::Error); + let pubsub_sub_path: Path = parse_quote!(embassy_sync::pubsub::Subscriber); + + let stateful_variant_ident = Ident::new(format!("{ident}Stateful").deref(), ident.span()); + let publish_variant_ident = Ident::new(format!("{ident}Publish").deref(), ident.span()); + let state_pub_variant_ident = Ident::new(format!("{ident}StatePub").deref(), ident.span()); + + let poll_path: Path = parse_quote!(physical_node::transducer::input::Poll); + let stateful_path: Path = parse_quote!(physical_node::transducer::Stateful); + let publish_path: Path = parse_quote!(physical_node::transducer::Publish); let expanded = quote! { // ----- Stateful struct ---------------------------------- - #vis struct #stateful_ident #og_generics #og_where_clause { + #vis struct #stateful_variant_ident #og_generics #og_where_clause { pub poll: #ident #og_type_generics, pub state: physical_node::transducer::State<#value_type_ident>, } // ----- Stateful impls ---------------------------------- - impl #og_impl_generics physical_node::transducer::Stateful for #stateful_ident #og_type_generics #og_where_clause { + impl #og_impl_generics #poll_path for #stateful_variant_ident #og_type_generics #og_where_clause { + type Value = #value_type_ident; + + #[inline(always)] + async fn poll(&self) -> Self::Value { + let value = self.poll.poll().await; + self.state.update(value); + value + } + } + + impl #og_impl_generics #stateful_path for #stateful_variant_ident #og_type_generics #og_where_clause { type Value = #value_type_ident; #[inline(always)] @@ -152,25 +171,37 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } } - impl #og_impl_generics physical_node::transducer::input::Poll for #stateful_ident #og_type_generics #og_where_clause { - type Value = #value_type_ident; - - #[inline(always)] - async fn poll(&self) -> Self::Value { - let value = self.poll.poll().await; - self.state.update(value); - value - } - } - // ----- Publish struct ---------------------------------- //#[cfg(feature = "embassy-sync")] - #vis struct #publish_ident #publish_generics #publ_where_clause { + #vis struct #publish_variant_ident #publish_generics #publ_where_clause { pub poll: #ident #og_type_generics, pub publisher: physical_node::transducer::Publisher<#value_type_ident, #mutex_t_ident, #capacity_ident, #num_subs_ident>, } // ----- Publish impl ---------------------------------- + //#[cfg(feature = "embassy-sync")] + impl #publ_impl_generics #poll_path for #publish_variant_ident #publ_type_generics #publ_where_clause { + type Value = #value_type_ident; + + #[inline(always)] + async fn poll(&self) -> Self::Value { + let value = self.poll.poll().await; + self.publisher.update(value); + value + } + } + + impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #publish_variant_ident #publ_type_generics #publ_where_clause { + type Value = #value_type_ident; + type Mutex = #mutex_t_ident; + + #[inline(always)] + fn subscribe( + &self, + ) -> Result<#pubsub_sub_path, #pubsub_error_path> { + self.publisher.subscribe() + } + } // ----- StatePub struct ---------------------------------- -- 2.43.0 From 456fbba236f3217fb81dbbe55132da644be5b958 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 20 Jun 2023 19:33:03 -0700 Subject: [PATCH 32/51] Device macro to generate stateful and publish variants of device specific poll inputs. --- macros/node-poll-variants/src/lib.rs | 60 ++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index b8e4015..4b1dcfc 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -138,11 +138,15 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { let stateful_path: Path = parse_quote!(physical_node::transducer::Stateful); let publish_path: Path = parse_quote!(physical_node::transducer::Publish); + let state_path: Path = parse_quote!(physical_node::transducer::State); + let publisher_path: Path = parse_quote!(physical_node::transducer::Publisher); + let cellview_path: Path = parse_quote!(physical_node::cell::CellView); + let expanded = quote! { // ----- Stateful struct ---------------------------------- #vis struct #stateful_variant_ident #og_generics #og_where_clause { pub poll: #ident #og_type_generics, - pub state: physical_node::transducer::State<#value_type_ident>, + pub state: #state_path<#value_type_ident>, } // ----- Stateful impls ---------------------------------- @@ -161,7 +165,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { type Value = #value_type_ident; #[inline(always)] - fn state_cell(&self) -> physical_node::cell::CellView { + fn state_cell(&self) -> #cellview_path { self.state.state_cell() } @@ -175,7 +179,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { //#[cfg(feature = "embassy-sync")] #vis struct #publish_variant_ident #publish_generics #publ_where_clause { pub poll: #ident #og_type_generics, - pub publisher: physical_node::transducer::Publisher<#value_type_ident, #mutex_t_ident, #capacity_ident, #num_subs_ident>, + pub publisher: #publisher_path<#value_type_ident, #mutex_t_ident, #capacity_ident, #num_subs_ident>, } // ----- Publish impl ---------------------------------- @@ -191,6 +195,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } } + //#[cfg(feature = "embassy-sync")] impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #publish_variant_ident #publ_type_generics #publ_where_clause { type Value = #value_type_ident; type Mutex = #mutex_t_ident; @@ -204,8 +209,55 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } // ----- StatePub struct ---------------------------------- + //#[cfg(feature = "embassy-sync")] + #vis struct #state_pub_variant_ident #publish_generics #publ_where_clause { + pub poll: #ident #og_type_generics, + pub state: #state_path<#value_type_ident>, + pub publisher: #publisher_path<#value_type_ident, #mutex_t_ident, #capacity_ident, #num_subs_ident>, + } + + //#[cfg(feature = "embassy-sync")] + impl #publ_impl_generics #poll_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause { + type Value = #value_type_ident; + + #[inline] + async fn poll(&self) -> Self::Value { + let value = self.poll.poll().await; + self.state.update(value); + self.publisher.update(value); + value + } + } + + //#[cfg(feature = "embassy-sync")] + impl #publ_impl_generics #stateful_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause { + type Value = #value_type_ident; + + #[inline(always)] + fn state_cell(&self) -> #cellview_path { + self.state.state_cell() + } + + #[inline(always)] + fn state(&self) -> Self::Value { + self.state.state() + } + } + + //#[cfg(feature = "embassy-sync")] + impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #state_pub_variant_ident #publ_type_generics #publ_where_clause { + type Value = #value_type_ident; + type Mutex = #mutex_t_ident; + + #[inline(always)] + fn subscribe( + &self, + ) -> Result<#pubsub_sub_path, #pubsub_error_path> { + self.publisher.subscribe() + } + } + - // ----- StatePub impl ---------------------------------- }; TokenStream::from(expanded) -- 2.43.0 From 831fc744727baac213bfb04a56a356d4aca76298 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 20 Jun 2023 19:33:39 -0700 Subject: [PATCH 33/51] Device macro to generate stateful and publish variants of device specific poll inputs. --- macros/node-poll-variants/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index 4b1dcfc..eca7be8 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -176,14 +176,14 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } // ----- Publish struct ---------------------------------- - //#[cfg(feature = "embassy-sync")] + #[cfg(feature = "embassy-sync")] #vis struct #publish_variant_ident #publish_generics #publ_where_clause { pub poll: #ident #og_type_generics, pub publisher: #publisher_path<#value_type_ident, #mutex_t_ident, #capacity_ident, #num_subs_ident>, } // ----- Publish impl ---------------------------------- - //#[cfg(feature = "embassy-sync")] + #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #poll_path for #publish_variant_ident #publ_type_generics #publ_where_clause { type Value = #value_type_ident; @@ -195,7 +195,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } } - //#[cfg(feature = "embassy-sync")] + #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #publish_variant_ident #publ_type_generics #publ_where_clause { type Value = #value_type_ident; type Mutex = #mutex_t_ident; @@ -209,14 +209,14 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } // ----- StatePub struct ---------------------------------- - //#[cfg(feature = "embassy-sync")] + #[cfg(feature = "embassy-sync")] #vis struct #state_pub_variant_ident #publish_generics #publ_where_clause { pub poll: #ident #og_type_generics, pub state: #state_path<#value_type_ident>, pub publisher: #publisher_path<#value_type_ident, #mutex_t_ident, #capacity_ident, #num_subs_ident>, } - //#[cfg(feature = "embassy-sync")] + #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #poll_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause { type Value = #value_type_ident; @@ -229,7 +229,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } } - //#[cfg(feature = "embassy-sync")] + #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #stateful_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause { type Value = #value_type_ident; @@ -244,7 +244,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } } - //#[cfg(feature = "embassy-sync")] + #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #state_pub_variant_ident #publ_type_generics #publ_where_clause { type Value = #value_type_ident; type Mutex = #mutex_t_ident; -- 2.43.0 From e3c44bde10207fc8fb3152696cd51941da7ce921 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 20 Jun 2023 19:34:10 -0700 Subject: [PATCH 34/51] Device macro to generate stateful and publish variants of device specific poll inputs. --- macros/node-poll-variants/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index eca7be8..c6d8b99 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -108,9 +108,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { // If there are lifetimes and other generics } else { // Insert MutexT after the lifetimes - publish_generics - .params - .insert(num_lifetimes, mutex_t_param); + publish_generics.params.insert(num_lifetimes, mutex_t_param); } // If there are no generics } else { -- 2.43.0 From 1ede18a716b01bcdd79d38fef6c23d000813630f Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 20 Jun 2023 19:39:40 -0700 Subject: [PATCH 35/51] ADS1256 poll input. --- peripheral-components/ads1256/node/Cargo.toml | 4 ++++ peripheral-components/ads1256/node/src/lib.rs | 1 + peripheral-components/ads1256/node/src/poll.rs | 0 3 files changed, 5 insertions(+) create mode 100644 peripheral-components/ads1256/node/src/poll.rs diff --git a/peripheral-components/ads1256/node/Cargo.toml b/peripheral-components/ads1256/node/Cargo.toml index dfe59d3..32df4a4 100644 --- a/peripheral-components/ads1256/node/Cargo.toml +++ b/peripheral-components/ads1256/node/Cargo.toml @@ -8,12 +8,16 @@ readme.workspace = true license.workspace = true [features] +poll = ["node-poll-variants"] config = ["physical-ads1256-types/config"] standard-input = ["physical-ads1256-types/standard-input"] standard-multiplexer = ["standard-input"] [dependencies.physical-node] path = "../../../node" +[dependencies.node-poll-variants] +path = "../../../macros/node-poll-variants" +optional = true [dependencies.physical-ads1256-types] path = "../types" features = ["defmt"] diff --git a/peripheral-components/ads1256/node/src/lib.rs b/peripheral-components/ads1256/node/src/lib.rs index 23a18d5..72729a0 100644 --- a/peripheral-components/ads1256/node/src/lib.rs +++ b/peripheral-components/ads1256/node/src/lib.rs @@ -2,5 +2,6 @@ #[cfg(feature = "standard-multiplexer")] mod standard_multiplexer; +mod poll; pub use physical_ads1256_types::*; \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/poll.rs b/peripheral-components/ads1256/node/src/poll.rs new file mode 100644 index 0000000..e69de29 -- 2.43.0 From 8f2d2b6679b151871402b6267848640c3d814422 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Thu, 22 Jun 2023 19:57:01 -0700 Subject: [PATCH 36/51] ADS1256 poll input. --- macros/node-poll-variants/src/lib.rs | 2 +- peripheral-components/ads1256/node/Cargo.toml | 9 +- peripheral-components/ads1256/node/src/lib.rs | 2 + .../ads1256/node/src/poll.rs | 0 .../ads1256/node/src/standard_multiplexer.rs | 0 .../node/src/standard_multiplexer/mod.rs | 4 + .../node/src/standard_multiplexer/sync/mod.rs | 2 + .../src/standard_multiplexer/sync/poll.rs | 249 ++++++++++++++++++ 8 files changed, 264 insertions(+), 4 deletions(-) delete mode 100644 peripheral-components/ads1256/node/src/poll.rs delete mode 100644 peripheral-components/ads1256/node/src/standard_multiplexer.rs create mode 100644 peripheral-components/ads1256/node/src/standard_multiplexer/mod.rs create mode 100644 peripheral-components/ads1256/node/src/standard_multiplexer/sync/mod.rs create mode 100644 peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index c6d8b99..53f2597 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -63,7 +63,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { // ----- Add publisher generics ---------------------------------- // MutexT - const MUTEX_T_NAME: &str = "MutexT"; + const MUTEX_T_NAME: &str = "PublishMutexT"; let mutex_t_ident = Ident::new(MUTEX_T_NAME, Span::call_site()); const CAPACITY_NAME: &str = "CAPACITY"; let capacity_ident = Ident::new(CAPACITY_NAME, Span::call_site()); diff --git a/peripheral-components/ads1256/node/Cargo.toml b/peripheral-components/ads1256/node/Cargo.toml index 32df4a4..b4f8d3d 100644 --- a/peripheral-components/ads1256/node/Cargo.toml +++ b/peripheral-components/ads1256/node/Cargo.toml @@ -8,7 +8,8 @@ readme.workspace = true license.workspace = true [features] -poll = ["node-poll-variants"] +embassy-sync = ["dep:embassy-sync", "ads1256/embassy-sync"] +poll = ["standard-multiplexer", "embassy-sync"] config = ["physical-ads1256-types/config"] standard-input = ["physical-ads1256-types/standard-input"] standard-multiplexer = ["standard-input"] @@ -17,7 +18,6 @@ standard-multiplexer = ["standard-input"] path = "../../../node" [dependencies.node-poll-variants] path = "../../../macros/node-poll-variants" -optional = true [dependencies.physical-ads1256-types] path = "../types" features = ["defmt"] @@ -30,4 +30,7 @@ workspace = true [dependencies.defmt] workspace = true [dependencies.uom] -workspace = true \ No newline at end of file +workspace = true +[dependencies.embassy-sync] +workspace = true +optional = true \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/lib.rs b/peripheral-components/ads1256/node/src/lib.rs index 72729a0..646b486 100644 --- a/peripheral-components/ads1256/node/src/lib.rs +++ b/peripheral-components/ads1256/node/src/lib.rs @@ -1,7 +1,9 @@ #![no_std] +#![feature(async_fn_in_trait, impl_trait_projections)] #[cfg(feature = "standard-multiplexer")] mod standard_multiplexer; +#[cfg(feature = "poll")] mod poll; pub use physical_ads1256_types::*; \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/poll.rs b/peripheral-components/ads1256/node/src/poll.rs deleted file mode 100644 index e69de29..0000000 diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer.rs b/peripheral-components/ads1256/node/src/standard_multiplexer.rs deleted file mode 100644 index e69de29..0000000 diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer/mod.rs b/peripheral-components/ads1256/node/src/standard_multiplexer/mod.rs new file mode 100644 index 0000000..2c842b4 --- /dev/null +++ b/peripheral-components/ads1256/node/src/standard_multiplexer/mod.rs @@ -0,0 +1,4 @@ +mod sync; + +#[cfg(feature = "embassy-sync")] +pub use sync::*; \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer/sync/mod.rs b/peripheral-components/ads1256/node/src/standard_multiplexer/sync/mod.rs new file mode 100644 index 0000000..5e18f2b --- /dev/null +++ b/peripheral-components/ads1256/node/src/standard_multiplexer/sync/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "poll")] +pub mod poll; \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs b/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs new file mode 100644 index 0000000..8eaddaa --- /dev/null +++ b/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs @@ -0,0 +1,249 @@ +use core::ops::DerefMut; +use ads1256::{ + AdControl, Ads1256, BlockingDelay, DataRate, Multiplexer, OutputPin, SpiBus, Status, Wait, +}; +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::mutex::Mutex; +use physical_node::transducer::input::Poll; + +pub struct AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> +where + DeviceMutexT: RawMutex, + ModInT: ModInput, + DelayerT: BlockingDelay, + SST: OutputPin, + DrdyT: Wait, + SpiT: SpiBus, +{ + input_mod: ModInT, + ads1256: &'a Mutex>, + spi: &'a Mutex, +} + +impl<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> Poll + for AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> +where + DeviceMutexT: RawMutex, + ModInT: ModInput, + DelayerT: BlockingDelay, + SST: OutputPin, + DrdyT: Wait, + SpiT: SpiBus, +{ + type Value = (); + + async fn poll(&self) -> Self::Value { + todo!() + } +} + +pub trait ModInput { + fn multiplexer(self) -> Multiplexer; + + fn status(self) -> Option; + + fn ad_control(self) -> Option; + + fn data_rate(self) -> Option; +} + +/// buffer +pub struct ModInStatus { + pub multiplexer: Multiplexer, + pub status: Status, +} + +impl ModInput for ModInStatus { + #[inline(always)] + fn multiplexer(self) -> Multiplexer { + self.multiplexer + } + + #[inline(always)] + fn status(self) -> Option { + Some(self.status) + } + + #[inline(always)] + fn ad_control(self) -> Option { + None + } + + #[inline(always)] + fn data_rate(self) -> Option { + None + } +} + +/// gain +pub struct ModInAdControl { + pub multiplexer: Multiplexer, + pub ad_control: AdControl, +} + +impl ModInput for ModInAdControl { + #[inline(always)] + fn multiplexer(self) -> Multiplexer { + self.multiplexer + } + + #[inline(always)] + fn status(self) -> Option { + None + } + + #[inline(always)] + fn ad_control(self) -> Option { + Some(self.ad_control) + } + + #[inline(always)] + fn data_rate(self) -> Option { + None + } +} + +/// data rate +pub struct ModInDataRate { + pub multiplexer: Multiplexer, + pub data_rate: DataRate, +} + +impl ModInput for ModInDataRate { + #[inline(always)] + fn multiplexer(self) -> Multiplexer { + self.multiplexer + } + + #[inline(always)] + fn status(self) -> Option { + None + } + + #[inline(always)] + fn ad_control(self) -> Option { + None + } + + #[inline(always)] + fn data_rate(self) -> Option { + Some(self.data_rate) + } +} + +/// buffer, gain +pub struct ModInStatAdc { + pub multiplexer: Multiplexer, + pub status: Status, + pub ad_control: AdControl, +} + +impl ModInput for ModInStatAdc { + #[inline(always)] + fn multiplexer(self) -> Multiplexer { + self.multiplexer + } + + #[inline(always)] + fn status(self) -> Option { + Some(self.status) + } + + #[inline(always)] + fn ad_control(self) -> Option { + Some(self.ad_control) + } + + #[inline(always)] + fn data_rate(self) -> Option { + None + } +} + +/// buffer, data rate +pub struct ModInStatDrate { + pub multiplexer: Multiplexer, + pub status: Status, + pub data_rate: DataRate, +} + +impl ModInput for ModInStatDrate { + #[inline(always)] + fn multiplexer(self) -> Multiplexer { + self.multiplexer + } + + #[inline(always)] + fn status(self) -> Option { + Some(self.status) + } + + #[inline(always)] + fn ad_control(self) -> Option { + None + } + + #[inline(always)] + fn data_rate(self) -> Option { + Some(self.data_rate) + } +} + +// gain, data rate +pub struct ModInAdcDrate { + pub multiplexer: Multiplexer, + pub ad_control: AdControl, + pub data_rate: DataRate, +} + +impl ModInput for ModInAdcDrate { + #[inline(always)] + fn multiplexer(self) -> Multiplexer { + self.multiplexer + } + + #[inline(always)] + fn status(self) -> Option { + None + } + + #[inline(always)] + fn ad_control(self) -> Option { + Some(self.ad_control) + } + + #[inline(always)] + fn data_rate(self) -> Option { + Some(self.data_rate) + } +} + +/// buffer, gain, data rate +pub struct ModInAll { + pub multiplexer: Multiplexer, + pub status: Status, + pub ad_control: AdControl, + pub data_rate: DataRate, +} + +impl ModInput for ModInAll { + #[inline(always)] + fn multiplexer(self) -> Multiplexer { + self.multiplexer + } + + #[inline(always)] + fn status(self) -> Option { + Some(self.status) + } + + #[inline(always)] + fn ad_control(self) -> Option { + Some(self.ad_control) + } + + #[inline(always)] + fn data_rate(self) -> Option { + Some(self.data_rate) + } +} -- 2.43.0 From 07cb49bee5d12bb9bb3221fbf4568e1a0faae98a Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 23 Jun 2023 14:15:43 -0700 Subject: [PATCH 37/51] Changes to Derive(PollVariants) to accept any path for the value type. --- macros/node-poll-variants/src/lib.rs | 54 ++++++++++--------- peripheral-components/ads1256/node/Cargo.toml | 2 +- .../src/standard_multiplexer/sync/poll.rs | 22 ++++++-- src/error.rs | 12 +++++ src/lib.rs | 3 ++ 5 files changed, 63 insertions(+), 30 deletions(-) create mode 100644 src/error.rs diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index 53f2597..2f3936e 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -5,12 +5,11 @@ use syn::__private::Span; use syn::punctuated::Punctuated; use syn::token::{Colon, Comma, PathSep, Plus}; use syn::{ - parse_macro_input, parse_quote, parse_quote_spanned, Data, DeriveInput, Expr, GenericParam, - Generics, Ident, Lit, Meta, Path, PathSegment, Token, TraitBound, TraitBoundModifier, - TypeParam, TypeParamBound, + parse_macro_input, parse_quote, parse_quote_spanned, parse_str, Data, DeriveInput, Expr, + GenericParam, Generics, Ident, Lit, Meta, Path, PathSegment, Token, TraitBound, + TraitBoundModifier, TypeParam, TypeParamBound, }; -// Struct name: Ads1256PollStateful, Ads1256PollPublish, Ads1256PollStatePub #[proc_macro_derive(PollVariants, attributes(value_type))] pub fn poll_variant_macro(input: TokenStream) -> TokenStream { // ----- Parse input ---------------------------------- @@ -31,7 +30,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { // ----- Extract value type attribute ---------------------------------- const VALUE_T_NAME: &str = "value_type"; - let mut value_type_ident: Option = None; + let mut value_type_path: Option = None; for attribute in attrs.iter() { // if the attribute is a named value if let Meta::NameValue(meta) = &attribute.meta { @@ -41,25 +40,28 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { if let Expr::Lit(lit) = &meta.value { // if the literal is a string if let Lit::Str(lit) = &lit.lit { - value_type_ident = Some(Ident::new(lit.value().deref(), Span::call_site())); + let span = lit.span(); + let string = lit.token().to_string(); + let mut segments: Punctuated = Punctuated::new(); + string + .trim_matches('"') + .split("::") + .map(|segment| Ident::new(segment, span)) + .for_each(|ident| segments.push(parse_quote_spanned!(span=> #ident))); + + let path: Path = parse_quote_spanned!(span=> #segments); + value_type_path = Some(path); } else { panic!("{VALUE_T_NAME} must be set with a string literal.") } } else { panic!("{VALUE_T_NAME} must be set with a literal.") } - } else { - continue; } - } else { - continue; } } - let value_type_ident = if let Some(val) = value_type_ident { - val - } else { - panic!("Need attribute {VALUE_T_NAME}: #[{VALUE_T_NAME} = \"type\"]") - }; + let value_type_path = value_type_path + .expect(format!("Need attribute {VALUE_T_NAME}: #[{VALUE_T_NAME} = \"type\"]").deref()); // ----- Add publisher generics ---------------------------------- // MutexT @@ -144,12 +146,12 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { // ----- Stateful struct ---------------------------------- #vis struct #stateful_variant_ident #og_generics #og_where_clause { pub poll: #ident #og_type_generics, - pub state: #state_path<#value_type_ident>, + pub state: #state_path<#value_type_path>, } // ----- Stateful impls ---------------------------------- impl #og_impl_generics #poll_path for #stateful_variant_ident #og_type_generics #og_where_clause { - type Value = #value_type_ident; + type Value = #value_type_path; #[inline(always)] async fn poll(&self) -> Self::Value { @@ -160,7 +162,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } impl #og_impl_generics #stateful_path for #stateful_variant_ident #og_type_generics #og_where_clause { - type Value = #value_type_ident; + type Value = #value_type_path; #[inline(always)] fn state_cell(&self) -> #cellview_path { @@ -177,13 +179,13 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] #vis struct #publish_variant_ident #publish_generics #publ_where_clause { pub poll: #ident #og_type_generics, - pub publisher: #publisher_path<#value_type_ident, #mutex_t_ident, #capacity_ident, #num_subs_ident>, + pub publisher: #publisher_path<#value_type_path, #mutex_t_ident, #capacity_ident, #num_subs_ident>, } // ----- Publish impl ---------------------------------- #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #poll_path for #publish_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_ident; + type Value = #value_type_path; #[inline(always)] async fn poll(&self) -> Self::Value { @@ -195,7 +197,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #publish_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_ident; + type Value = #value_type_path; type Mutex = #mutex_t_ident; #[inline(always)] @@ -210,13 +212,13 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] #vis struct #state_pub_variant_ident #publish_generics #publ_where_clause { pub poll: #ident #og_type_generics, - pub state: #state_path<#value_type_ident>, - pub publisher: #publisher_path<#value_type_ident, #mutex_t_ident, #capacity_ident, #num_subs_ident>, + pub state: #state_path<#value_type_path>, + pub publisher: #publisher_path<#value_type_path, #mutex_t_ident, #capacity_ident, #num_subs_ident>, } #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #poll_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_ident; + type Value = #value_type_path; #[inline] async fn poll(&self) -> Self::Value { @@ -229,7 +231,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #stateful_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_ident; + type Value = #value_type_path; #[inline(always)] fn state_cell(&self) -> #cellview_path { @@ -244,7 +246,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #state_pub_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_ident; + type Value = #value_type_path; type Mutex = #mutex_t_ident; #[inline(always)] diff --git a/peripheral-components/ads1256/node/Cargo.toml b/peripheral-components/ads1256/node/Cargo.toml index b4f8d3d..d5169ea 100644 --- a/peripheral-components/ads1256/node/Cargo.toml +++ b/peripheral-components/ads1256/node/Cargo.toml @@ -8,7 +8,7 @@ readme.workspace = true license.workspace = true [features] -embassy-sync = ["dep:embassy-sync", "ads1256/embassy-sync"] +embassy-sync = ["dep:embassy-sync", "ads1256/embassy-sync", "physical-node/embassy-sync"] poll = ["standard-multiplexer", "embassy-sync"] config = ["physical-ads1256-types/config"] standard-input = ["physical-ads1256-types/standard-input"] diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs b/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs index 8eaddaa..62513eb 100644 --- a/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs +++ b/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs @@ -1,11 +1,15 @@ -use core::ops::DerefMut; use ads1256::{ AdControl, Ads1256, BlockingDelay, DataRate, Multiplexer, OutputPin, SpiBus, Status, Wait, }; +use core::ops::DerefMut; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; +use node_poll_variants::PollVariants; use physical_node::transducer::input::Poll; +use uom::si::f32; +#[derive(PollVariants)] +#[value_type = "f32::ElectricPotential"] pub struct AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> where DeviceMutexT: RawMutex, @@ -30,10 +34,22 @@ where DrdyT: Wait, SpiT: SpiBus, { - type Value = (); + type Value = f32::ElectricPotential; async fn poll(&self) -> Self::Value { - todo!() + self.ads1256 + .lock() + .await + .deref_mut() + .autocal_convert_m( + self.spi, + self.input_mod.multiplexer(), + self.input_mod.status(), + self.input_mod.ad_control(), + self.input_mod.data_rate(), + true, + ) + .await } } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..99b3012 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,12 @@ +/// An error that it is likely impossible to recover. This error should only be created in +/// situations where attempts to recover have already been attempted and have failed. Error handling +/// should consist of attempting to alert another system for maintenance and attempting to shut down +/// any systems that depend on the correct functionality of the component having errors. +/// +/// In many systems there can be a single function for handling any critical error as a critical +/// error always means everything needs to be stopped. +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum CriticalError { + /// Critical communication failed and retries are either impossible or also failed. + Communication, +} diff --git a/src/lib.rs b/src/lib.rs index 9fe79e6..d700e0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,3 +3,6 @@ pub mod transducer; pub mod cell; +mod error; + +pub use error::*; -- 2.43.0 From 2a0013481ca579cc35ae77695f1d917b9889613a Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 23 Jun 2023 16:55:33 -0700 Subject: [PATCH 38/51] PollInput implementation --- macros/node-poll-variants/src/lib.rs | 38 ++++++---- macros/node-poll-variants/tests/generate.rs | 5 +- node/src/lib.rs | 2 + .../src/standard_multiplexer/sync/poll.rs | 70 +++++++++++++++++-- src/lib.rs | 2 +- src/transducer/input.rs | 4 +- 6 files changed, 97 insertions(+), 24 deletions(-) diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index 2f3936e..92c5a7e 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -142,6 +142,8 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { let publisher_path: Path = parse_quote!(physical_node::transducer::Publisher); let cellview_path: Path = parse_quote!(physical_node::cell::CellView); + let error_path: Path = parse_quote!(physical_node::CriticalError); + let expanded = quote! { // ----- Stateful struct ---------------------------------- #vis struct #stateful_variant_ident #og_generics #og_where_clause { @@ -153,11 +155,13 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { impl #og_impl_generics #poll_path for #stateful_variant_ident #og_type_generics #og_where_clause { type Value = #value_type_path; - #[inline(always)] - async fn poll(&self) -> Self::Value { - let value = self.poll.poll().await; - self.state.update(value); - value + #[inline] + async fn poll(&self) -> Result { + let result = self.poll.poll().await; + if let Ok(value) = result { + self.state.update(value); + } + result } } @@ -187,11 +191,13 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { impl #publ_impl_generics #poll_path for #publish_variant_ident #publ_type_generics #publ_where_clause { type Value = #value_type_path; - #[inline(always)] - async fn poll(&self) -> Self::Value { - let value = self.poll.poll().await; - self.publisher.update(value); - value + #[inline] + async fn poll(&self) -> Result { + let result = self.poll.poll().await; + if let Ok(value) = result { + self.publisher.update(value); + } + result } } @@ -221,11 +227,13 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { type Value = #value_type_path; #[inline] - async fn poll(&self) -> Self::Value { - let value = self.poll.poll().await; - self.state.update(value); - self.publisher.update(value); - value + async fn poll(&self) -> Result { + let result = self.poll.poll().await; + if let Ok(value) = result { + self.state.update(value); + self.publisher.update(value); + } + result } } diff --git a/macros/node-poll-variants/tests/generate.rs b/macros/node-poll-variants/tests/generate.rs index 01cf375..73c119a 100644 --- a/macros/node-poll-variants/tests/generate.rs +++ b/macros/node-poll-variants/tests/generate.rs @@ -1,6 +1,7 @@ #![feature(async_fn_in_trait, impl_trait_projections)] use node_poll_variants::PollVariants; +use physical_node::CriticalError; use physical_node::transducer::input::Poll; #[derive(PollVariants)] @@ -21,8 +22,8 @@ where { type Value = SecondT; - async fn poll(&self) -> Self::Value { - self.second + async fn poll(&self) -> Result { + Ok(self.second) } } diff --git a/node/src/lib.rs b/node/src/lib.rs index e712a34..ff5daeb 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -6,3 +6,5 @@ pub mod transducer; pub mod cell { pub use physical::cell::*; } + +pub use physical::CriticalError; diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs b/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs index 62513eb..ea30b64 100644 --- a/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs +++ b/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs @@ -1,11 +1,12 @@ use ads1256::{ - AdControl, Ads1256, BlockingDelay, DataRate, Multiplexer, OutputPin, SpiBus, Status, Wait, + AdControl, Ads1256, BlockingDelay, DataRate, Gain, Multiplexer, OutputPin, SpiBus, Status, Wait, }; use core::ops::DerefMut; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; use node_poll_variants::PollVariants; use physical_node::transducer::input::Poll; +use physical_node::CriticalError; use uom::si::f32; #[derive(PollVariants)] @@ -36,8 +37,9 @@ where { type Value = f32::ElectricPotential; - async fn poll(&self) -> Self::Value { - self.ads1256 + async fn poll(&self) -> Result { + let result = self + .ads1256 .lock() .await .deref_mut() @@ -49,13 +51,20 @@ where self.input_mod.data_rate(), true, ) - .await + .await; + + match result { + Ok(conversion) => Ok(conversion.to_voltage(self.input_mod.gain())), + Err(_) => Err(CriticalError::Communication), + } } } -pub trait ModInput { +pub trait ModInput: Copy { fn multiplexer(self) -> Multiplexer; + fn gain(self) -> Gain; + fn status(self) -> Option; fn ad_control(self) -> Option; @@ -64,8 +73,12 @@ pub trait ModInput { } /// buffer +#[derive(Copy, Clone, Eq, PartialEq)] pub struct ModInStatus { pub multiplexer: Multiplexer, + /// Only used for converting to voltage, this value must match what is set on the ADS1256, it + /// will not *be* set unlike the other fields. + pub gain: Gain, pub status: Status, } @@ -75,6 +88,11 @@ impl ModInput for ModInStatus { self.multiplexer } + #[inline(always)] + fn gain(self) -> Gain { + self.gain + } + #[inline(always)] fn status(self) -> Option { Some(self.status) @@ -92,6 +110,7 @@ impl ModInput for ModInStatus { } /// gain +#[derive(Copy, Clone, Eq, PartialEq)] pub struct ModInAdControl { pub multiplexer: Multiplexer, pub ad_control: AdControl, @@ -103,6 +122,11 @@ impl ModInput for ModInAdControl { self.multiplexer } + #[inline(always)] + fn gain(self) -> Gain { + self.ad_control.gain() + } + #[inline(always)] fn status(self) -> Option { None @@ -120,8 +144,12 @@ impl ModInput for ModInAdControl { } /// data rate +#[derive(Copy, Clone, Eq, PartialEq)] pub struct ModInDataRate { pub multiplexer: Multiplexer, + /// Only used for converting to voltage, this value must match what is set on the ADS1256, it + /// will not *be* set unlike the other fields. + pub gain: Gain, pub data_rate: DataRate, } @@ -131,6 +159,11 @@ impl ModInput for ModInDataRate { self.multiplexer } + #[inline(always)] + fn gain(self) -> Gain { + self.gain + } + #[inline(always)] fn status(self) -> Option { None @@ -148,6 +181,7 @@ impl ModInput for ModInDataRate { } /// buffer, gain +#[derive(Copy, Clone, Eq, PartialEq)] pub struct ModInStatAdc { pub multiplexer: Multiplexer, pub status: Status, @@ -160,6 +194,11 @@ impl ModInput for ModInStatAdc { self.multiplexer } + #[inline(always)] + fn gain(self) -> Gain { + self.ad_control.gain() + } + #[inline(always)] fn status(self) -> Option { Some(self.status) @@ -177,8 +216,12 @@ impl ModInput for ModInStatAdc { } /// buffer, data rate +#[derive(Copy, Clone, Eq, PartialEq)] pub struct ModInStatDrate { pub multiplexer: Multiplexer, + /// Only used for converting to voltage, this value must match what is set on the ADS1256, it + /// will not *be* set unlike the other fields. + pub gain: Gain, pub status: Status, pub data_rate: DataRate, } @@ -189,6 +232,11 @@ impl ModInput for ModInStatDrate { self.multiplexer } + #[inline(always)] + fn gain(self) -> Gain { + self.gain + } + #[inline(always)] fn status(self) -> Option { Some(self.status) @@ -206,6 +254,7 @@ impl ModInput for ModInStatDrate { } // gain, data rate +#[derive(Copy, Clone, Eq, PartialEq)] pub struct ModInAdcDrate { pub multiplexer: Multiplexer, pub ad_control: AdControl, @@ -218,6 +267,11 @@ impl ModInput for ModInAdcDrate { self.multiplexer } + #[inline(always)] + fn gain(self) -> Gain { + self.ad_control.gain() + } + #[inline(always)] fn status(self) -> Option { None @@ -235,6 +289,7 @@ impl ModInput for ModInAdcDrate { } /// buffer, gain, data rate +#[derive(Copy, Clone, Eq, PartialEq)] pub struct ModInAll { pub multiplexer: Multiplexer, pub status: Status, @@ -248,6 +303,11 @@ impl ModInput for ModInAll { self.multiplexer } + #[inline(always)] + fn gain(self) -> Gain { + self.ad_control.gain() + } + #[inline(always)] fn status(self) -> Option { Some(self.status) diff --git a/src/lib.rs b/src/lib.rs index d700e0c..c8d494c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,4 +5,4 @@ pub mod transducer; pub mod cell; mod error; -pub use error::*; +pub use error::CriticalError; diff --git a/src/transducer/input.rs b/src/transducer/input.rs index 43b8fa4..4664d79 100644 --- a/src/transducer/input.rs +++ b/src/transducer/input.rs @@ -1,5 +1,7 @@ +use crate::CriticalError; + pub trait Poll { type Value: Copy; - async fn poll(&self) -> Self::Value; + async fn poll(&self) -> Result; } -- 2.43.0 From c46680247bf69060fae623c68c508dc54a2b5875 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 23 Jun 2023 20:32:01 -0700 Subject: [PATCH 39/51] Cleanup --- Cargo.toml | 2 +- examples/ads1256/Cargo.toml | 2 +- examples/ads1256/src/bin/multiplex.rs | 5 +- examples/ads1256/src/bin/poll.rs | 180 ++++++++++++++++++ peripheral-components/ads1256/node/Cargo.toml | 9 +- peripheral-components/ads1256/node/src/lib.rs | 7 +- .../ads1256/node/src/standard/mod.rs | 2 + .../multiplexer}/mod.rs | 0 .../multiplexer}/sync/mod.rs | 0 .../multiplexer}/sync/poll.rs | 9 +- .../ads1256/types/Cargo.toml | 18 -- .../ads1256/types/src/config.rs | 2 - .../ads1256/types/src/lib.rs | 11 -- .../ads1256/types/src/standard_input.rs | 39 ---- .../standalone-ads1256/node/Cargo.toml | 25 --- .../standalone-ads1256/node/src/lib.rs | 1 - .../standalone-ads1256/types/Cargo.toml | 13 -- .../standalone-ads1256/types/src/lib.rs | 1 - 18 files changed, 193 insertions(+), 133 deletions(-) create mode 100644 examples/ads1256/src/bin/poll.rs create mode 100644 peripheral-components/ads1256/node/src/standard/mod.rs rename peripheral-components/ads1256/node/src/{standard_multiplexer => standard/multiplexer}/mod.rs (100%) rename peripheral-components/ads1256/node/src/{standard_multiplexer => standard/multiplexer}/sync/mod.rs (100%) rename peripheral-components/ads1256/node/src/{standard_multiplexer => standard/multiplexer}/sync/poll.rs (98%) delete mode 100644 peripheral-components/ads1256/types/Cargo.toml delete mode 100644 peripheral-components/ads1256/types/src/config.rs delete mode 100644 peripheral-components/ads1256/types/src/lib.rs delete mode 100644 peripheral-components/ads1256/types/src/standard_input.rs delete mode 100644 peripherals/standalone-ads1256/node/Cargo.toml delete mode 100644 peripherals/standalone-ads1256/node/src/lib.rs delete mode 100644 peripherals/standalone-ads1256/types/Cargo.toml delete mode 100644 peripherals/standalone-ads1256/types/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 5245fb9..513db1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ "node", "commander", # Peripherals - "peripherals/standalone-ads1256/*", + # Peripheral components "peripheral-components/ads1256/*", # Macros diff --git a/examples/ads1256/Cargo.toml b/examples/ads1256/Cargo.toml index 194ac18..c847d66 100644 --- a/examples/ads1256/Cargo.toml +++ b/examples/ads1256/Cargo.toml @@ -12,7 +12,7 @@ path = "../../node" features = ["embassy-sync"] [dependencies.physical-ads1256] path = "../../peripheral-components/ads1256/node" -features = ["standard-input"] +features = ["poll"] [dependencies.ads1256] workspace = true [dependencies.uom] diff --git a/examples/ads1256/src/bin/multiplex.rs b/examples/ads1256/src/bin/multiplex.rs index df5a1ac..229aa35 100644 --- a/examples/ads1256/src/bin/multiplex.rs +++ b/examples/ads1256/src/bin/multiplex.rs @@ -15,6 +15,7 @@ use ads1256::{ DataRate, DigitalIo, DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, MuxInput, OutputPin, Sdcs, SpiBus, Status, Wait, }; +use ads1256::standard::input::SingleEnded; use embassy_time::{Delay, Duration, Timer}; use executor::Spawner; use stm32::dma::NoDma; @@ -32,9 +33,7 @@ use embassy_executor::_export::StaticCell; use embassy_stm32::peripherals::{EXTI6, PF6, PF7, SPI3}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pubsub::PubSubChannel; -use physical_ads1256::{standard_input, SingleEnded}; -use physical_node::transducer::input::StatefulPublisher; -use physical_node::transducer::Publish; +use physical_node::transducer::{Publish, StatefulPublisher}; const AUTOCAL_CONF: Config = Config { status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), diff --git a/examples/ads1256/src/bin/poll.rs b/examples/ads1256/src/bin/poll.rs new file mode 100644 index 0000000..229aa35 --- /dev/null +++ b/examples/ads1256/src/bin/poll.rs @@ -0,0 +1,180 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait, async_fn_in_trait)] + +use core::cell::Cell; +use cortex_m::prelude::{ + _embedded_hal_blocking_delay_DelayMs, _embedded_hal_blocking_delay_DelayUs, +}; +use {defmt_rtt as _, panic_probe as _}; + +use {embassy_executor as executor, embassy_stm32 as stm32}; + +use ads1256::{ + AdControl, Ads1256, AutoCal, BitOrder, BlockingDelay, Buffer, ClockOut, Config, DState, + DataRate, DigitalIo, DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, + MuxInput, OutputPin, Sdcs, SpiBus, Status, Wait, +}; +use ads1256::standard::input::SingleEnded; +use embassy_time::{Delay, Duration, Timer}; +use executor::Spawner; +use stm32::dma::NoDma; +use stm32::exti::ExtiInput; +use stm32::gpio::{Input, Level, Output, Pull, Speed}; +use stm32::spi::Spi; +use stm32::time::Hertz; +use stm32::{pac, spi}; + +use uom::si::electric_potential::volt; +use uom::si::f32; + +use defmt::{debug, error, info, trace, unwrap}; +use embassy_executor::_export::StaticCell; +use embassy_stm32::peripherals::{EXTI6, PF6, PF7, SPI3}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::pubsub::PubSubChannel; +use physical_node::transducer::{Publish, StatefulPublisher}; + +const AUTOCAL_CONF: Config = Config { + status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), + multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::Common), + ad_control: AdControl::setting(Gain::X2, Sdcs::Off, ClockOut::Off), + data_rate: DataRate::Sps2_5, + digital_io: DigitalIo::setting(DigitalIoState::default(), DigitalIoDirection::default()), +}; + +struct Ads1256Delay; + +impl ads1256::BlockingDelay for Ads1256Delay { + #[inline] + fn t6_delay(&mut self) { + Delay.delay_us(1u32); + } + + fn t11_1_delay(&mut self) {} + + fn t11_2_delay(&mut self) {} +} + +struct Inputs { + ai1: StatefulPublisher, + ai2: StatefulPublisher, + ai3: StatefulPublisher, +} + +// Inputs +static ANALOG_INPUTS: StaticCell = StaticCell::new(); +static ADS_1256: StaticCell, ExtiInput>> = StaticCell::new(); +static SPI: StaticCell> = StaticCell::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + unsafe { + pac::FLASH.acr().modify(|v| { + v.set_prften(true); + v.set_icen(true); + v.set_dcen(true); + }); + } + + let p = embassy_stm32::init(Default::default()); + + let mut spi_conf = spi::Config::default(); + spi_conf.mode = spi::MODE_1; + spi_conf.bit_order = spi::BitOrder::MsbFirst; + + let ads1256_data_ready = ExtiInput::new(Input::new(p.PF6, Pull::Up), p.EXTI6); + let select_ads1256 = Output::new(p.PF7, Level::High, Speed::VeryHigh); + + let spi = SPI.init(Spi::new( + p.SPI3, + p.PC10, + p.PC12, + p.PC11, + NoDma, + NoDma, + Hertz(ads1256::defaults::SPI_CLK_HZ), + spi_conf, + )); + + let ads_1256 = ADS_1256.init(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready)); + + let inputs = &*ANALOG_INPUTS.init(Inputs { + ai1: StatefulPublisher::new( + Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), + PubSubChannel::new().into(), + ), + ai2: StatefulPublisher::new( + Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), + PubSubChannel::new().into(), + ), + ai3: StatefulPublisher::new( + Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), + PubSubChannel::new().into(), + ), + }); + + spawner.spawn(log_task(inputs)).unwrap(); + spawner + .spawn(drive_inputs_task(ads_1256, spi, inputs)) + .unwrap(); +} + +#[embassy_executor::task] +async fn drive_inputs_task( + ads_1256: &'static mut Ads1256, ExtiInput<'static, PF6>>, + spi: &'static mut Spi<'static, SPI3, NoDma, NoDma>, + inputs: &'static Inputs, +) { + let Inputs { ai1, ai2, ai3 } = inputs; + + loop { + let mut accumulator = f32::ElectricPotential::new::(0.0); + + let voltage = ads_1256 + .autocal_convert(spi, SingleEnded::AIn0.into(), None, None, None, false) + .await + .unwrap() + .to_voltage(AUTOCAL_CONF.ad_control.gain()); + ai1.update(voltage); + accumulator += voltage; + + let voltage = ads_1256 + .autocal_convert(spi, SingleEnded::AIn1.into(), None, None, None, false) + .await + .unwrap() + .to_voltage(AUTOCAL_CONF.ad_control.gain()); + ai2.update(voltage); + accumulator += voltage; + + let voltage = ads_1256 + .autocal_convert(spi, SingleEnded::AIn2.into(), None, None, None, false) + .await + .unwrap() + .to_voltage(AUTOCAL_CONF.ad_control.gain()); + ai3.update(voltage); + accumulator += voltage; + + let accum_volts = accumulator.get::(); + info!("Immediate loop iteration result, combined volts: {}", accum_volts); + } +} + +#[embassy_executor::task] +async fn log_task(inputs: &'static Inputs) { + let Inputs { ai1, ai2, ai3 } = inputs; + let mut ai1_sub = ai1.subscribe().unwrap(); + let mut ai2_sub = ai2.subscribe().unwrap(); + let mut ai3_sub = ai3.subscribe().unwrap(); + + loop { + let msg = ai1_sub.next_message_pure().await.get::(); + info!("Log task ai1: {}", msg); + + let msg = ai2_sub.next_message_pure().await.get::(); + info!("Log task ai2: {}", msg); + + let msg = ai3_sub.next_message_pure().await.get::(); + info!("Log task ai3: {}", msg); + } +} diff --git a/peripheral-components/ads1256/node/Cargo.toml b/peripheral-components/ads1256/node/Cargo.toml index d5169ea..5b64fff 100644 --- a/peripheral-components/ads1256/node/Cargo.toml +++ b/peripheral-components/ads1256/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "physical-ads1256" -description = "Shared abstractions for ADS1256 components." +description = "Shared node abstractions for ADS1256 components." version.workspace = true edition.workspace = true repository.workspace = true @@ -10,17 +10,12 @@ license.workspace = true [features] embassy-sync = ["dep:embassy-sync", "ads1256/embassy-sync", "physical-node/embassy-sync"] poll = ["standard-multiplexer", "embassy-sync"] -config = ["physical-ads1256-types/config"] -standard-input = ["physical-ads1256-types/standard-input"] -standard-multiplexer = ["standard-input"] +standard-multiplexer = [] [dependencies.physical-node] path = "../../../node" [dependencies.node-poll-variants] path = "../../../macros/node-poll-variants" -[dependencies.physical-ads1256-types] -path = "../types" -features = ["defmt"] [dependencies.ads1256] workspace = true [dependencies.embedded-hal] diff --git a/peripheral-components/ads1256/node/src/lib.rs b/peripheral-components/ads1256/node/src/lib.rs index 646b486..ed52f0c 100644 --- a/peripheral-components/ads1256/node/src/lib.rs +++ b/peripheral-components/ads1256/node/src/lib.rs @@ -1,9 +1,4 @@ #![no_std] #![feature(async_fn_in_trait, impl_trait_projections)] -#[cfg(feature = "standard-multiplexer")] -mod standard_multiplexer; -#[cfg(feature = "poll")] -mod poll; - -pub use physical_ads1256_types::*; \ No newline at end of file +pub mod standard; \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/standard/mod.rs b/peripheral-components/ads1256/node/src/standard/mod.rs new file mode 100644 index 0000000..8c85608 --- /dev/null +++ b/peripheral-components/ads1256/node/src/standard/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "standard-input")] +pub mod multiplexer; \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer/mod.rs b/peripheral-components/ads1256/node/src/standard/multiplexer/mod.rs similarity index 100% rename from peripheral-components/ads1256/node/src/standard_multiplexer/mod.rs rename to peripheral-components/ads1256/node/src/standard/multiplexer/mod.rs diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer/sync/mod.rs b/peripheral-components/ads1256/node/src/standard/multiplexer/sync/mod.rs similarity index 100% rename from peripheral-components/ads1256/node/src/standard_multiplexer/sync/mod.rs rename to peripheral-components/ads1256/node/src/standard/multiplexer/sync/mod.rs diff --git a/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs b/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs similarity index 98% rename from peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs rename to peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs index ea30b64..4a5d8ae 100644 --- a/peripheral-components/ads1256/node/src/standard_multiplexer/sync/poll.rs +++ b/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs @@ -38,11 +38,10 @@ where type Value = f32::ElectricPotential; async fn poll(&self) -> Result { - let result = self - .ads1256 - .lock() - .await - .deref_mut() + let mut ads1256_guard = self.ads1256.lock().await; + let ads1256 = ads1256_guard.deref_mut(); + + let result = ads1256 .autocal_convert_m( self.spi, self.input_mod.multiplexer(), diff --git a/peripheral-components/ads1256/types/Cargo.toml b/peripheral-components/ads1256/types/Cargo.toml deleted file mode 100644 index 065bf12..0000000 --- a/peripheral-components/ads1256/types/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "physical-ads1256-types" -description = "ADS1256 Physical types." -version.workspace = true -edition.workspace = true -repository.workspace = true -readme.workspace = true -license.workspace = true - -[features] -config = [] -standard-input = [] - -[dependencies.ads1256-types] -workspace = true -[dependencies.defmt] -workspace = true -optional = true \ No newline at end of file diff --git a/peripheral-components/ads1256/types/src/config.rs b/peripheral-components/ads1256/types/src/config.rs deleted file mode 100644 index d162cab..0000000 --- a/peripheral-components/ads1256/types/src/config.rs +++ /dev/null @@ -1,2 +0,0 @@ -use ads1256_types::{Buffer, Config, DataRate, Gain}; - diff --git a/peripheral-components/ads1256/types/src/lib.rs b/peripheral-components/ads1256/types/src/lib.rs deleted file mode 100644 index ec78e3c..0000000 --- a/peripheral-components/ads1256/types/src/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![no_std] - -#[cfg(feature = "standard-input")] -pub mod standard_input; -#[cfg(feature = "config")] -mod config; - -#[cfg(feature = "config")] -pub use config::*; -#[cfg(feature = "standard-input")] -pub use standard_input::*; diff --git a/peripheral-components/ads1256/types/src/standard_input.rs b/peripheral-components/ads1256/types/src/standard_input.rs deleted file mode 100644 index b4fa825..0000000 --- a/peripheral-components/ads1256/types/src/standard_input.rs +++ /dev/null @@ -1,39 +0,0 @@ -use ads1256_types::{Buffer, Config, DataRate, Gain, Multiplexer, MuxInput}; - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[repr(u8)] -pub enum Differential { - AIn0 = Multiplexer::setting(MuxInput::AIn0, MuxInput::AIn1).0, - AIn1 = Multiplexer::setting(MuxInput::AIn2, MuxInput::AIn3).0, - AIn2 = Multiplexer::setting(MuxInput::AIn4, MuxInput::AIn5).0, - AIn3 = Multiplexer::setting(MuxInput::AIn6, MuxInput::AIn7).0, -} - -impl Into for Differential { - #[inline(always)] - fn into(self) -> Multiplexer { - Multiplexer(self as u8) - } -} - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[repr(u8)] -pub enum SingleEnded { - AIn0 = Multiplexer::setting(MuxInput::AIn0, MuxInput::Common).0, - AIn1 = Multiplexer::setting(MuxInput::AIn1, MuxInput::Common).0, - AIn2 = Multiplexer::setting(MuxInput::AIn2, MuxInput::Common).0, - AIn3 = Multiplexer::setting(MuxInput::AIn3, MuxInput::Common).0, - AIn4 = Multiplexer::setting(MuxInput::AIn4, MuxInput::Common).0, - AIn5 = Multiplexer::setting(MuxInput::AIn5, MuxInput::Common).0, - AIn6 = Multiplexer::setting(MuxInput::AIn6, MuxInput::Common).0, - AIn7 = Multiplexer::setting(MuxInput::AIn7, MuxInput::Common).0, -} - -impl Into for SingleEnded { - #[inline(always)] - fn into(self) -> Multiplexer { - Multiplexer(self as u8) - } -} diff --git a/peripherals/standalone-ads1256/node/Cargo.toml b/peripherals/standalone-ads1256/node/Cargo.toml deleted file mode 100644 index b88e589..0000000 --- a/peripherals/standalone-ads1256/node/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "physical-standalone-ads1256" -description = "Support for dedicated ADS1256 boards (i.e. boards that have no other functionality other than to expose the ADS1256)" -version.workspace = true -edition.workspace = true -repository.workspace = true -readme.workspace = true -license.workspace = true - -[dependencies.physical-node] -path = "../../../node" -[dependencies.physical-standalone-ads1256-types] -path = "../types" -[dependencies.physical-ads1256] -path = "../../../peripheral-components/ads1256/node" -[dependencies.ads1256] -workspace = true -[dependencies.embedded-hal] -workspace = true -[dependencies.embedded-hal-async] -workspace = true -[dependencies.defmt] -workspace = true -[dependencies.uom] -workspace = true \ No newline at end of file diff --git a/peripherals/standalone-ads1256/node/src/lib.rs b/peripherals/standalone-ads1256/node/src/lib.rs deleted file mode 100644 index 2e7f0d4..0000000 --- a/peripherals/standalone-ads1256/node/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -#![no_std] \ No newline at end of file diff --git a/peripherals/standalone-ads1256/types/Cargo.toml b/peripherals/standalone-ads1256/types/Cargo.toml deleted file mode 100644 index 1dd8682..0000000 --- a/peripherals/standalone-ads1256/types/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "physical-standalone-ads1256-types" -description = "Support for dedicated ADS1256 boards (i.e. boards that have no other functionality other than to expose the ADS1256)" -version.workspace = true -edition.workspace = true -repository.workspace = true -readme.workspace = true -license.workspace = true - -[dependencies.ads1256-types] -workspace = true -[dependencies.physical-ads1256-types] -path = "../../../peripheral-components/ads1256/types" \ No newline at end of file diff --git a/peripherals/standalone-ads1256/types/src/lib.rs b/peripherals/standalone-ads1256/types/src/lib.rs deleted file mode 100644 index 0c9ac1a..0000000 --- a/peripherals/standalone-ads1256/types/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -#![no_std] -- 2.43.0 From 359b75cba8233bb31cf872ee525ef2fadfc426bf Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Sun, 25 Jun 2023 14:30:43 -0700 Subject: [PATCH 40/51] Poll example --- examples/ads1256/src/bin/multiplex.rs | 6 +- examples/ads1256/src/bin/poll.rs | 151 ++++++++---------- .../ads1256/node/src/standard/mod.rs | 2 +- .../src/standard/multiplexer/sync/poll.rs | 55 +++++++ 4 files changed, 125 insertions(+), 89 deletions(-) diff --git a/examples/ads1256/src/bin/multiplex.rs b/examples/ads1256/src/bin/multiplex.rs index 229aa35..4460cc2 100644 --- a/examples/ads1256/src/bin/multiplex.rs +++ b/examples/ads1256/src/bin/multiplex.rs @@ -101,15 +101,15 @@ async fn main(spawner: Spawner) { let inputs = &*ANALOG_INPUTS.init(Inputs { ai1: StatefulPublisher::new( - Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), + Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), PubSubChannel::new().into(), ), ai2: StatefulPublisher::new( - Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), + Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), PubSubChannel::new().into(), ), ai3: StatefulPublisher::new( - Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), + Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), PubSubChannel::new().into(), ), }); diff --git a/examples/ads1256/src/bin/poll.rs b/examples/ads1256/src/bin/poll.rs index 229aa35..6e5e225 100644 --- a/examples/ads1256/src/bin/poll.rs +++ b/examples/ads1256/src/bin/poll.rs @@ -10,12 +10,12 @@ use {defmt_rtt as _, panic_probe as _}; use {embassy_executor as executor, embassy_stm32 as stm32}; +use ads1256::standard::input::SingleEnded; use ads1256::{ AdControl, Ads1256, AutoCal, BitOrder, BlockingDelay, Buffer, ClockOut, Config, DState, DataRate, DigitalIo, DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, MuxInput, OutputPin, Sdcs, SpiBus, Status, Wait, }; -use ads1256::standard::input::SingleEnded; use embassy_time::{Delay, Duration, Timer}; use executor::Spawner; use stm32::dma::NoDma; @@ -32,8 +32,12 @@ use defmt::{debug, error, info, trace, unwrap}; use embassy_executor::_export::StaticCell; use embassy_stm32::peripherals::{EXTI6, PF6, PF7, SPI3}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::mutex::Mutex; use embassy_sync::pubsub::PubSubChannel; -use physical_node::transducer::{Publish, StatefulPublisher}; +use physical_ads1256::standard::multiplexer::poll::{ + AutocalPoll, AutocalPollStatePub, ModInputOnly, +}; +use physical_node::transducer::{Publish, Publisher, StatefulPublisher}; const AUTOCAL_CONF: Config = Config { status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), @@ -56,16 +60,31 @@ impl ads1256::BlockingDelay for Ads1256Delay { fn t11_2_delay(&mut self) {} } +type ExampleInput = AutocalPollStatePub< + 'static, + NoopRawMutex, + NoopRawMutex, + ModInputOnly, + Ads1256Delay, + Output<'static, PF7>, + ExtiInput<'static, PF6>, + Spi<'static, SPI3, NoDma, NoDma>, + 10, + 1, +>; + struct Inputs { - ai1: StatefulPublisher, - ai2: StatefulPublisher, - ai3: StatefulPublisher, + ai1: ExampleInput, + ai2: ExampleInput, + ai3: ExampleInput, } // Inputs static ANALOG_INPUTS: StaticCell = StaticCell::new(); -static ADS_1256: StaticCell, ExtiInput>> = StaticCell::new(); -static SPI: StaticCell> = StaticCell::new(); +static ADS_1256: StaticCell< + Mutex, ExtiInput>>, +> = StaticCell::new(); +static SPI: StaticCell>> = StaticCell::new(); #[embassy_executor::main] async fn main(spawner: Spawner) { @@ -86,7 +105,7 @@ async fn main(spawner: Spawner) { let ads1256_data_ready = ExtiInput::new(Input::new(p.PF6, Pull::Up), p.EXTI6); let select_ads1256 = Output::new(p.PF7, Level::High, Speed::VeryHigh); - let spi = SPI.init(Spi::new( + let spi = &*SPI.init(Mutex::new(Spi::new( p.SPI3, p.PC10, p.PC12, @@ -95,86 +114,48 @@ async fn main(spawner: Spawner) { NoDma, Hertz(ads1256::defaults::SPI_CLK_HZ), spi_conf, - )); + ))); - let ads_1256 = ADS_1256.init(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready)); + let ads_1256 = + &*ADS_1256.init(Mutex::new(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready))); let inputs = &*ANALOG_INPUTS.init(Inputs { - ai1: StatefulPublisher::new( - Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), - PubSubChannel::new().into(), - ), - ai2: StatefulPublisher::new( - Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), - PubSubChannel::new().into(), - ), - ai3: StatefulPublisher::new( - Cell::new(f32::ElectricPotential::new::(-1000.0)).into(), - PubSubChannel::new().into(), - ), + ai1: AutocalPollStatePub { + poll: AutocalPoll::new( + ModInputOnly { + gain: Gain::X2, + multiplexer: SingleEnded::AIn1.into(), + }, + ads_1256, + spi, + ), + publisher: PubSubChannel::new().into(), + state: Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), + }, + ai2: AutocalPollStatePub { + poll: AutocalPoll::new( + ModInputOnly { + gain: Gain::X2, + multiplexer: SingleEnded::AIn2.into(), + }, + ads_1256, + spi, + ), + publisher: PubSubChannel::new().into(), + state: Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), + }, + ai3: AutocalPollStatePub { + poll: AutocalPoll::new( + ModInputOnly { + gain: Gain::X2, + multiplexer: SingleEnded::AIn3.into(), + }, + ads_1256, + spi, + ), + publisher: PubSubChannel::new().into(), + state: Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), + }, }); - spawner.spawn(log_task(inputs)).unwrap(); - spawner - .spawn(drive_inputs_task(ads_1256, spi, inputs)) - .unwrap(); -} - -#[embassy_executor::task] -async fn drive_inputs_task( - ads_1256: &'static mut Ads1256, ExtiInput<'static, PF6>>, - spi: &'static mut Spi<'static, SPI3, NoDma, NoDma>, - inputs: &'static Inputs, -) { - let Inputs { ai1, ai2, ai3 } = inputs; - - loop { - let mut accumulator = f32::ElectricPotential::new::(0.0); - - let voltage = ads_1256 - .autocal_convert(spi, SingleEnded::AIn0.into(), None, None, None, false) - .await - .unwrap() - .to_voltage(AUTOCAL_CONF.ad_control.gain()); - ai1.update(voltage); - accumulator += voltage; - - let voltage = ads_1256 - .autocal_convert(spi, SingleEnded::AIn1.into(), None, None, None, false) - .await - .unwrap() - .to_voltage(AUTOCAL_CONF.ad_control.gain()); - ai2.update(voltage); - accumulator += voltage; - - let voltage = ads_1256 - .autocal_convert(spi, SingleEnded::AIn2.into(), None, None, None, false) - .await - .unwrap() - .to_voltage(AUTOCAL_CONF.ad_control.gain()); - ai3.update(voltage); - accumulator += voltage; - - let accum_volts = accumulator.get::(); - info!("Immediate loop iteration result, combined volts: {}", accum_volts); - } -} - -#[embassy_executor::task] -async fn log_task(inputs: &'static Inputs) { - let Inputs { ai1, ai2, ai3 } = inputs; - let mut ai1_sub = ai1.subscribe().unwrap(); - let mut ai2_sub = ai2.subscribe().unwrap(); - let mut ai3_sub = ai3.subscribe().unwrap(); - - loop { - let msg = ai1_sub.next_message_pure().await.get::(); - info!("Log task ai1: {}", msg); - - let msg = ai2_sub.next_message_pure().await.get::(); - info!("Log task ai2: {}", msg); - - let msg = ai3_sub.next_message_pure().await.get::(); - info!("Log task ai3: {}", msg); - } } diff --git a/peripheral-components/ads1256/node/src/standard/mod.rs b/peripheral-components/ads1256/node/src/standard/mod.rs index 8c85608..490e4c6 100644 --- a/peripheral-components/ads1256/node/src/standard/mod.rs +++ b/peripheral-components/ads1256/node/src/standard/mod.rs @@ -1,2 +1,2 @@ -#[cfg(feature = "standard-input")] +#[cfg(feature = "standard-multiplexer")] pub mod multiplexer; \ No newline at end of file diff --git a/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs b/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs index 4a5d8ae..1038272 100644 --- a/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs +++ b/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs @@ -25,6 +25,30 @@ where spi: &'a Mutex, } +impl<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> + AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> +where + DeviceMutexT: RawMutex, + ModInT: ModInput, + DelayerT: BlockingDelay, + SST: OutputPin, + DrdyT: Wait, + SpiT: SpiBus, +{ + #[inline(always)] + pub fn new( + input_mod: ModInT, + ads1256: &'a Mutex>, + spi: &'a Mutex, + ) -> Self { + Self { + input_mod, + ads1256, + spi, + } + } +} + impl<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> Poll for AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> where @@ -40,6 +64,7 @@ where async fn poll(&self) -> Result { let mut ads1256_guard = self.ads1256.lock().await; let ads1256 = ads1256_guard.deref_mut(); + ads1256.data_ready.wait_for_low().await; let result = ads1256 .autocal_convert_m( @@ -71,6 +96,36 @@ pub trait ModInput: Copy { fn data_rate(self) -> Option; } +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct ModInputOnly { + pub multiplexer: Multiplexer, + /// Only used for converting to voltage, this value must match what is set on the ADS1256, it + /// will not *be* set unlike the other fields. + pub gain: Gain, +} + +impl ModInput for ModInputOnly { + fn multiplexer(self) -> Multiplexer { + self.multiplexer + } + + fn gain(self) -> Gain { + self.gain + } + + fn status(self) -> Option { + None + } + + fn ad_control(self) -> Option { + None + } + + fn data_rate(self) -> Option { + None + } +} + /// buffer #[derive(Copy, Clone, Eq, PartialEq)] pub struct ModInStatus { -- 2.43.0 From abfa21ff018d7840ec149696bef53028e71fe4df Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Sun, 25 Jun 2023 17:33:03 -0700 Subject: [PATCH 41/51] Poll example --- examples/ads1256/src/bin/poll.rs | 26 ++++++++++++++++++- .../src/standard/multiplexer/sync/poll.rs | 3 ++- src/error.rs | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/examples/ads1256/src/bin/poll.rs b/examples/ads1256/src/bin/poll.rs index 6e5e225..917629d 100644 --- a/examples/ads1256/src/bin/poll.rs +++ b/examples/ads1256/src/bin/poll.rs @@ -38,6 +38,7 @@ use physical_ads1256::standard::multiplexer::poll::{ AutocalPoll, AutocalPollStatePub, ModInputOnly, }; use physical_node::transducer::{Publish, Publisher, StatefulPublisher}; +use physical_node::transducer::input::Poll; const AUTOCAL_CONF: Config = Config { status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), @@ -157,5 +158,28 @@ async fn main(spawner: Spawner) { state: Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), }, }); - + spawner.spawn(log_task(&inputs.ai1, 1)).unwrap(); + spawner.spawn(log_task(&inputs.ai2, 2)).unwrap(); + spawner.spawn(log_task(&inputs.ai3, 3)).unwrap(); + spawner.spawn(poll_task(&inputs.ai1, 1, Duration::from_secs(5))).unwrap(); + spawner.spawn(poll_task(&inputs.ai2, 2, Duration::from_secs(10))).unwrap(); + spawner.spawn(poll_task(&inputs.ai3, 3, Duration::from_secs(20))).unwrap(); +} + +#[embassy_executor::task(pool_size = 3)] +async fn poll_task(input: &'static ExampleInput, input_num: u8, every: Duration,) { + loop { + Timer::after(every).await; + let result = input.poll().await.unwrap().get::(); + info!("ai{} poll result: {} volts", input_num, result); + } +} + +#[embassy_executor::task(pool_size = 3)] +async fn log_task(input: &'static ExampleInput, input_num: u8) { + let mut subscriber = input.subscribe().unwrap(); + loop { + let msg = subscriber.next_message_pure().await.get::(); + info!("Log task ai{}: {}", input_num, msg); + } } diff --git a/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs b/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs index 1038272..d5f0f1a 100644 --- a/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs +++ b/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs @@ -64,7 +64,8 @@ where async fn poll(&self) -> Result { let mut ads1256_guard = self.ads1256.lock().await; let ads1256 = ads1256_guard.deref_mut(); - ads1256.data_ready.wait_for_low().await; + //TODO: ADS1256 documentation seems to say we should be waiting for drdy low after putting + // issuing standby command but it never goes low. let result = ads1256 .autocal_convert_m( diff --git a/src/error.rs b/src/error.rs index 99b3012..ca1d807 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,7 +5,7 @@ /// /// In many systems there can be a single function for handling any critical error as a critical /// error always means everything needs to be stopped. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum CriticalError { /// Critical communication failed and retries are either impossible or also failed. Communication, -- 2.43.0 From 1c25e51a8a04d4a86e25a12bb03a771015318677 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Mon, 26 Jun 2023 13:54:00 -0700 Subject: [PATCH 42/51] Poll changes --- macros/node-poll-variants/src/lib.rs | 88 ++++++++++++------- macros/node-poll-variants/tests/generate.rs | 7 +- .../src/standard/multiplexer/sync/poll.rs | 4 +- src/error.rs | 31 ++++++- src/lib.rs | 2 +- src/transducer/input.rs | 5 +- src/transducer/mod.rs | 7 ++ src/transducer/output.rs | 7 +- 8 files changed, 105 insertions(+), 46 deletions(-) diff --git a/macros/node-poll-variants/src/lib.rs b/macros/node-poll-variants/src/lib.rs index 92c5a7e..242c1cf 100644 --- a/macros/node-poll-variants/src/lib.rs +++ b/macros/node-poll-variants/src/lib.rs @@ -1,16 +1,13 @@ use proc_macro::TokenStream; use quote::quote; use std::ops::Deref; -use syn::__private::Span; +use quote::__private::parse_spanned; +use syn::__private::{str, Span}; use syn::punctuated::Punctuated; use syn::token::{Colon, Comma, PathSep, Plus}; -use syn::{ - parse_macro_input, parse_quote, parse_quote_spanned, parse_str, Data, DeriveInput, Expr, - GenericParam, Generics, Ident, Lit, Meta, Path, PathSegment, Token, TraitBound, - TraitBoundModifier, TypeParam, TypeParamBound, -}; +use syn::{parse_macro_input, parse_quote, parse_quote_spanned, parse_str, Data, DeriveInput, Expr, GenericParam, Generics, Ident, Lit, LitStr, Meta, Path, PathSegment, Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, Type}; -#[proc_macro_derive(PollVariants, attributes(value_type))] +#[proc_macro_derive(PollVariants, attributes(value_type, error_type))] pub fn poll_variant_macro(input: TokenStream) -> TokenStream { // ----- Parse input ---------------------------------- let input = parse_macro_input!(input as DeriveInput); @@ -28,13 +25,15 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { Data::Union(_) => panic!("Stateful struct cannot be derived from a union."), }; - // ----- Extract value type attribute ---------------------------------- + // ----- Extract attribute information ---------------------------------- const VALUE_T_NAME: &str = "value_type"; - let mut value_type_path: Option = None; + const ERROR_T_NAME: &str = "error_type"; + let mut value_type: Option = None; + let mut error_type: Option = None; for attribute in attrs.iter() { // if the attribute is a named value if let Meta::NameValue(meta) = &attribute.meta { - // if the name of the attribute is value_t + // if the name of the attribute is value_type if meta.path.segments[0].ident == VALUE_T_NAME { // if the value of the attribute is a literal if let Expr::Lit(lit) = &meta.value { @@ -42,15 +41,12 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { if let Lit::Str(lit) = &lit.lit { let span = lit.span(); let string = lit.token().to_string(); - let mut segments: Punctuated = Punctuated::new(); - string - .trim_matches('"') - .split("::") - .map(|segment| Ident::new(segment, span)) - .for_each(|ident| segments.push(parse_quote_spanned!(span=> #ident))); + let string = string.trim_matches('"').to_string(); + let _value_type: Type = parse_str(string.deref()).unwrap(); + let _value_type: Type = parse_quote_spanned!(span=> #_value_type); + + value_type = Some(_value_type); - let path: Path = parse_quote_spanned!(span=> #segments); - value_type_path = Some(path); } else { panic!("{VALUE_T_NAME} must be set with a string literal.") } @@ -58,10 +54,33 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { panic!("{VALUE_T_NAME} must be set with a literal.") } } + + // if the name of the attribute is error_type + if meta.path.segments[0].ident == ERROR_T_NAME { + // if the value of the attribute is a literal + if let Expr::Lit(lit) = &meta.value { + // if the literal is a string + if let Lit::Str(lit) = &lit.lit { + let span = lit.span(); + let string = lit.token().to_string(); + let string = string.trim_matches('"').to_string(); + let _error_type: Type = parse_str(string.deref()).unwrap(); + let _error_type: Type = parse_quote_spanned!(span=> #_error_type); + + error_type = Some(_error_type); + } else { + panic!("{ERROR_T_NAME} must be set with a string literal.") + } + } else { + panic!("{ERROR_T_NAME} must be set with a literal.") + } + } } } - let value_type_path = value_type_path + let value_type = value_type .expect(format!("Need attribute {VALUE_T_NAME}: #[{VALUE_T_NAME} = \"type\"]").deref()); + let error_type = error_type + .expect(format!("Need attribute {ERROR_T_NAME}: #[{ERROR_T_NAME} = \"type\"]").deref()); // ----- Add publisher generics ---------------------------------- // MutexT @@ -148,15 +167,16 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { // ----- Stateful struct ---------------------------------- #vis struct #stateful_variant_ident #og_generics #og_where_clause { pub poll: #ident #og_type_generics, - pub state: #state_path<#value_type_path>, + pub state: #state_path<#value_type>, } // ----- Stateful impls ---------------------------------- impl #og_impl_generics #poll_path for #stateful_variant_ident #og_type_generics #og_where_clause { - type Value = #value_type_path; + type Value = #value_type; + type Error = #error_type; #[inline] - async fn poll(&self) -> Result { + async fn poll(&self) -> Result { let result = self.poll.poll().await; if let Ok(value) = result { self.state.update(value); @@ -166,7 +186,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { } impl #og_impl_generics #stateful_path for #stateful_variant_ident #og_type_generics #og_where_clause { - type Value = #value_type_path; + type Value = #value_type; #[inline(always)] fn state_cell(&self) -> #cellview_path { @@ -183,16 +203,17 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] #vis struct #publish_variant_ident #publish_generics #publ_where_clause { pub poll: #ident #og_type_generics, - pub publisher: #publisher_path<#value_type_path, #mutex_t_ident, #capacity_ident, #num_subs_ident>, + pub publisher: #publisher_path<#value_type, #mutex_t_ident, #capacity_ident, #num_subs_ident>, } // ----- Publish impl ---------------------------------- #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #poll_path for #publish_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_path; + type Value = #value_type; + type Error = #error_type; #[inline] - async fn poll(&self) -> Result { + async fn poll(&self) -> Result { let result = self.poll.poll().await; if let Ok(value) = result { self.publisher.update(value); @@ -203,7 +224,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #publish_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_path; + type Value = #value_type; type Mutex = #mutex_t_ident; #[inline(always)] @@ -218,16 +239,17 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] #vis struct #state_pub_variant_ident #publish_generics #publ_where_clause { pub poll: #ident #og_type_generics, - pub state: #state_path<#value_type_path>, - pub publisher: #publisher_path<#value_type_path, #mutex_t_ident, #capacity_ident, #num_subs_ident>, + pub state: #state_path<#value_type>, + pub publisher: #publisher_path<#value_type, #mutex_t_ident, #capacity_ident, #num_subs_ident>, } #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #poll_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_path; + type Value = #value_type; + type Error = #error_type; #[inline] - async fn poll(&self) -> Result { + async fn poll(&self) -> Result { let result = self.poll.poll().await; if let Ok(value) = result { self.state.update(value); @@ -239,7 +261,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #stateful_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_path; + type Value = #value_type; #[inline(always)] fn state_cell(&self) -> #cellview_path { @@ -254,7 +276,7 @@ pub fn poll_variant_macro(input: TokenStream) -> TokenStream { #[cfg(feature = "embassy-sync")] impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #state_pub_variant_ident #publ_type_generics #publ_where_clause { - type Value = #value_type_path; + type Value = #value_type; type Mutex = #mutex_t_ident; #[inline(always)] diff --git a/macros/node-poll-variants/tests/generate.rs b/macros/node-poll-variants/tests/generate.rs index 73c119a..98df1d5 100644 --- a/macros/node-poll-variants/tests/generate.rs +++ b/macros/node-poll-variants/tests/generate.rs @@ -1,11 +1,11 @@ -#![feature(async_fn_in_trait, impl_trait_projections)] +#![feature(async_fn_in_trait, impl_trait_projections, never_type)] use node_poll_variants::PollVariants; -use physical_node::CriticalError; use physical_node::transducer::input::Poll; #[derive(PollVariants)] #[value_type = "SecondT"] +#[error_type = "!"] struct ExamplePoll<'a, FirstT, SecondT> where SecondT: Copy, @@ -21,8 +21,9 @@ where SecondT: Copy, { type Value = SecondT; + type Error = !; - async fn poll(&self) -> Result { + async fn poll(&self) -> Result { Ok(self.second) } } diff --git a/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs b/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs index d5f0f1a..d180353 100644 --- a/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs +++ b/peripheral-components/ads1256/node/src/standard/multiplexer/sync/poll.rs @@ -11,6 +11,7 @@ use uom::si::f32; #[derive(PollVariants)] #[value_type = "f32::ElectricPotential"] +#[error_type = "CriticalError"] pub struct AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> where DeviceMutexT: RawMutex, @@ -60,11 +61,12 @@ where SpiT: SpiBus, { type Value = f32::ElectricPotential; + type Error = CriticalError; async fn poll(&self) -> Result { let mut ads1256_guard = self.ads1256.lock().await; let ads1256 = ads1256_guard.deref_mut(); - //TODO: ADS1256 documentation seems to say we should be waiting for drdy low after putting + //TODO: ADS1256 documentation seems to say we should be waiting for drdy low after // issuing standby command but it never goes low. let result = ads1256 diff --git a/src/error.rs b/src/error.rs index ca1d807..096e009 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,6 @@ -/// An error that it is likely impossible to recover. This error should only be created in +use crate::transducer::InvalidValue; + +/// An error that it is likely impossible to recover from. This error should only be created in /// situations where attempts to recover have already been attempted and have failed. Error handling /// should consist of attempting to alert another system for maintenance and attempting to shut down /// any systems that depend on the correct functionality of the component having errors. @@ -9,4 +11,31 @@ pub enum CriticalError { /// Critical communication failed and retries are either impossible or also failed. Communication, + InvalidValue(InvalidValue), +} + +impl CriticalError { + pub fn emergency_procedure(self, procedure: impl FnOnce(CriticalError) -> !) -> ! { + procedure(self); + } +} + +/// [Result] where error type is [CriticalError]. +pub trait CriticalErrResult: Copy { + type Value: Copy; + + /// Execute emergency procedure in the event of a critical, the emergency procedure cannot + /// return. It should usually terminate the program, potentially rebooting the device in some sort of recovery mode. + fn err_emproc(self, procedure: impl FnOnce(CriticalError) -> !) -> Self::Value; +} + +impl CriticalErrResult for Result { + type Value = T; + + fn err_emproc(self, procedure: impl FnOnce(CriticalError) -> !) -> Self::Value { + match self { + Ok(val) => val, + Err(error) => error.emergency_procedure(procedure), + } + } } diff --git a/src/lib.rs b/src/lib.rs index c8d494c..b064117 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(async_fn_in_trait)] +#![feature(async_fn_in_trait, never_type)] pub mod transducer; pub mod cell; diff --git a/src/transducer/input.rs b/src/transducer/input.rs index 4664d79..40e3cce 100644 --- a/src/transducer/input.rs +++ b/src/transducer/input.rs @@ -1,7 +1,6 @@ -use crate::CriticalError; - pub trait Poll { type Value: Copy; + type Error: Copy; - async fn poll(&self) -> Result; + async fn poll(&self) -> Result; } diff --git a/src/transducer/mod.rs b/src/transducer/mod.rs index 8948ca0..6f907f7 100644 --- a/src/transducer/mod.rs +++ b/src/transducer/mod.rs @@ -51,3 +51,10 @@ impl From> for State { State::new(state_cell) } } + +// --------------------------------------------------------------------------------------------------------------------- +// ----- Error ------------------------ +// --------------------------------------------------------------------------------------------------------------------- +/// Indicates the transducer value is statically known to be impossible. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct InvalidValue; diff --git a/src/transducer/output.rs b/src/transducer/output.rs index eb43a0d..3512b8f 100644 --- a/src/transducer/output.rs +++ b/src/transducer/output.rs @@ -1,9 +1,8 @@ +use crate::transducer::InvalidValue; + pub trait Output { type Value: Copy; //TODO: Should this be maybe async? - fn output(&mut self, setting: Self::Value) -> Result<(), InvalidOutputSetting>; + fn output(&mut self, setting: Self::Value) -> Result<(), InvalidValue>; } - -/// Indicates the setting given for an [Output] is statically known to be an impossible setting to achieve. -pub struct InvalidOutputSetting; \ No newline at end of file -- 2.43.0 From 5386d2a2bfcb79da7f52add0a1560fa71f8228c9 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Thu, 6 Jul 2023 22:40:54 -0700 Subject: [PATCH 43/51] Thermocouple type K conversion --- Cargo.toml | 3 + src/transducer/conversion/mod.rs | 4 ++ .../conversion/thermocouples/mod.rs | 2 + .../conversion/thermocouples/type_k.rs | 66 +++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 src/transducer/conversion/thermocouples/mod.rs create mode 100644 src/transducer/conversion/thermocouples/type_k.rs diff --git a/Cargo.toml b/Cargo.toml index 513db1c..fd9fb6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -126,6 +126,9 @@ repository.workspace = true readme.workspace = true license.workspace = true +[features] +thermocouple_k = [] + [dependencies] uom = { workspace = true } parity-scale-codec = { workspace = true } diff --git a/src/transducer/conversion/mod.rs b/src/transducer/conversion/mod.rs index e69de29..fe6f177 100644 --- a/src/transducer/conversion/mod.rs +++ b/src/transducer/conversion/mod.rs @@ -0,0 +1,4 @@ +mod thermocouples; + +#[cfg(feature = "thermocouple_k")] +pub use thermocouples::type_k as thermocouple_k; \ No newline at end of file diff --git a/src/transducer/conversion/thermocouples/mod.rs b/src/transducer/conversion/thermocouples/mod.rs new file mode 100644 index 0000000..9ce86f4 --- /dev/null +++ b/src/transducer/conversion/thermocouples/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "thermocouple_k")] +pub mod type_k; \ No newline at end of file diff --git a/src/transducer/conversion/thermocouples/type_k.rs b/src/transducer/conversion/thermocouples/type_k.rs new file mode 100644 index 0000000..e59b39e --- /dev/null +++ b/src/transducer/conversion/thermocouples/type_k.rs @@ -0,0 +1,66 @@ +use crate::transducer::InvalidValue; +use uom::si::electric_potential::millivolt; +use uom::si::f32; +use uom::si::thermodynamic_temperature::degree_celsius; + +/// Convert from a voltage produced by a type k thermocouple to a temperature. +/// +/// This function uses the [NIST type K thermocouple linearisation polynomial](https://srdata.nist.gov/its90/type_k/kcoefficients_inverse.html). +pub fn convert( + voltage: f32::ElectricPotential, + cold_junction: f32::ThermodynamicTemperature, +) -> Result { + //TODO: Add cold junction correction + let mv = voltage.get::(); + let mv_pow2 = mv * mv; + let mv_pow3 = mv_pow2 * mv; + let mv_pow4 = mv_pow3 * mv; + let mv_pow5 = mv_pow4 * mv; + let mv_pow6 = mv_pow5 * mv; + + if mv >= -5.891 && mv <= 0.0 { + let mv_pow7 = mv_pow6 * mv; + let mv_pow8 = mv_pow7 * mv; + + let celsius = 1.0 + + 2.5173462e1 * mv + + -1.1662878 * mv_pow2 + + -1.0833638 * mv_pow3 + + -8.9773540e-1 * mv_pow4 + + -3.7342377e-1 * mv_pow5 + + -8.6632643e-2 * mv_pow6 + + -1.0450598e-2 * mv_pow7 + + -5.1920577e-4 * mv_pow8; + + Ok(f32::ThermodynamicTemperature::new::(celsius)) + } else if mv > 0.0 && mv < 20.644 { + let mv_pow7 = mv_pow6 * mv; + let mv_pow8 = mv_pow7 * mv; + let mv_pow9 = mv_pow8 * mv; + + let celsius = 1.0 + + 2.508355e1 * mv + + 7.860106e-2 * mv_pow2 + + -2.503131e-1 * mv_pow3 + + 8.315270e-2 * mv_pow4 + + -1.228034e-2 * mv_pow5 + + 9.804036e-4 * mv_pow6 + + -4.413030e-5 * mv_pow7 + + 1.057734e-6 * mv_pow8 + + -1.052755e-8 * mv_pow9; + + Ok(f32::ThermodynamicTemperature::new::(celsius)) + } else if mv >= 20.644 && mv <= 54.886 { + let celsius = 1.318058e2 + + 4.830222e1 * mv + + -1.646031 * mv_pow2 + + 5.464731e-2 * mv_pow3 + + -9.650715e-4 * mv_pow4 + + 8.802193e-6 * mv_pow5 + + -3.110810e-8 * mv_pow6; + + Ok(f32::ThermodynamicTemperature::new::(celsius)) + } else { + Err(InvalidValue) + } +} -- 2.43.0 From ad45ea71754582f03c8f0d0946f690f8e8e1362f Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 7 Jul 2023 14:41:50 -0700 Subject: [PATCH 44/51] Thermocouple type K conversion --- .../conversion/thermocouples/type_k.rs | 91 ++++++++++++++----- 1 file changed, 66 insertions(+), 25 deletions(-) diff --git a/src/transducer/conversion/thermocouples/type_k.rs b/src/transducer/conversion/thermocouples/type_k.rs index e59b39e..8bc742e 100644 --- a/src/transducer/conversion/thermocouples/type_k.rs +++ b/src/transducer/conversion/thermocouples/type_k.rs @@ -1,17 +1,21 @@ use crate::transducer::InvalidValue; -use uom::si::electric_potential::millivolt; +use uom::si::electric_potential::{millivolt, volt}; use uom::si::f32; use uom::si::thermodynamic_temperature::degree_celsius; -/// Convert from a voltage produced by a type k thermocouple to a temperature. +/// Convert from a voltage produced by a type k thermocouple to a temperature using polynomial with +/// NIST coefficients, this method has a maximum error of 0.06°C in addition to the underlying +/// thermocouple inaccuracy. /// /// This function uses the [NIST type K thermocouple linearisation polynomial](https://srdata.nist.gov/its90/type_k/kcoefficients_inverse.html). pub fn convert( voltage: f32::ElectricPotential, - cold_junction: f32::ThermodynamicTemperature, + r_junction: f32::ThermodynamicTemperature, + r_junction_offset: fn( + r_junction: f32::ThermodynamicTemperature, + ) -> Result, ) -> Result { - //TODO: Add cold junction correction - let mv = voltage.get::(); + let mv = voltage.get::() + r_junction_offset(r_junction)?.get::(); let mv_pow2 = mv * mv; let mv_pow3 = mv_pow2 * mv; let mv_pow4 = mv_pow3 * mv; @@ -23,14 +27,14 @@ pub fn convert( let mv_pow8 = mv_pow7 * mv; let celsius = 1.0 - + 2.5173462e1 * mv + + 2.5173462E+1 * mv + -1.1662878 * mv_pow2 + -1.0833638 * mv_pow3 - + -8.9773540e-1 * mv_pow4 - + -3.7342377e-1 * mv_pow5 - + -8.6632643e-2 * mv_pow6 - + -1.0450598e-2 * mv_pow7 - + -5.1920577e-4 * mv_pow8; + + -8.9773540E-1 * mv_pow4 + + -3.7342377E-1 * mv_pow5 + + -8.6632643E-2 * mv_pow6 + + -1.0450598E-2 * mv_pow7 + + -5.1920577E-4 * mv_pow8; Ok(f32::ThermodynamicTemperature::new::(celsius)) } else if mv > 0.0 && mv < 20.644 { @@ -39,28 +43,65 @@ pub fn convert( let mv_pow9 = mv_pow8 * mv; let celsius = 1.0 - + 2.508355e1 * mv - + 7.860106e-2 * mv_pow2 - + -2.503131e-1 * mv_pow3 - + 8.315270e-2 * mv_pow4 - + -1.228034e-2 * mv_pow5 - + 9.804036e-4 * mv_pow6 - + -4.413030e-5 * mv_pow7 - + 1.057734e-6 * mv_pow8 - + -1.052755e-8 * mv_pow9; + + 2.508355E+1 * mv + + 7.860106E-2 * mv_pow2 + + -2.503131E-1 * mv_pow3 + + 8.315270E-2 * mv_pow4 + + -1.228034E-2 * mv_pow5 + + 9.804036E-4 * mv_pow6 + + -4.413030E-5 * mv_pow7 + + 1.057734E-6 * mv_pow8 + + -1.052755E-8 * mv_pow9; Ok(f32::ThermodynamicTemperature::new::(celsius)) } else if mv >= 20.644 && mv <= 54.886 { let celsius = 1.318058e2 - + 4.830222e1 * mv + + 4.830222E+1 * mv + -1.646031 * mv_pow2 - + 5.464731e-2 * mv_pow3 - + -9.650715e-4 * mv_pow4 - + 8.802193e-6 * mv_pow5 - + -3.110810e-8 * mv_pow6; + + 5.464731E-2 * mv_pow3 + + -9.650715E-4 * mv_pow4 + + 8.802193E-6 * mv_pow5 + + -3.110810E-8 * mv_pow6; Ok(f32::ThermodynamicTemperature::new::(celsius)) } else { Err(InvalidValue) } } + +pub fn r_junction_offset_poly( + temperature: f32::ThermodynamicTemperature, +) -> Result { + const T0: f32 = 2.5000000E+01; + const V0: f32 = 1.0003453E+00; + const P1: f32 = 4.0514854E-02; + const P2: f32 = -3.8789638E-05; + const P3: f32 = -2.8608478E-06; + const P4: f32 = -9.5367041E-10; + const Q1: f32 = -1.3948675E-03; + const Q2: f32 = -6.7976627E-05; + + let celsius = temperature.get::(); + if celsius >= -20.0 && celsius <= 70.0 { + let offset_tcj = celsius - T0; + let numerator = offset_tcj + (P1 + offset_tcj * (P2 + offset_tcj * (P3 + P4 * offset_tcj))); + let denominator = 1.0 + offset_tcj * (Q1 + Q2 * offset_tcj); + let mv = V0 + numerator / denominator; + + Ok(f32::ElectricPotential::new::(mv)) + } else { + Err(InvalidValue) + } +} + +pub fn r_junction_offset_lin( + temperature: f32::ThermodynamicTemperature, +) -> Result { + let celsius = temperature.get::(); + if celsius >= -2.0 && celsius <= 1000.0 { + let mv = 0.041 * celsius; + Ok(f32::ElectricPotential::new::(mv)) + } else { + Err(InvalidValue) + } +} -- 2.43.0 From 6173b8c306cbf0c378da2fe5619be8c121541001 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 7 Jul 2023 16:27:59 -0700 Subject: [PATCH 45/51] Thermocouple type K conversion --- Cargo.toml | 3 +- .../conversion/thermocouples/type_k.rs | 64 +++++++++++++------ 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd9fb6f..9064c91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ version = "0.3.*" version = "0.4.*" # Serialization [workspace.dependencies.parity-scale-codec] -version = "3.5.*" +version = "3.6.*" default-features = false # Embedded-HAL [workspace.dependencies.embedded-hal] @@ -132,6 +132,7 @@ thermocouple_k = [] [dependencies] uom = { workspace = true } parity-scale-codec = { workspace = true } +libm = { workspace = true } #--------------------------------------------------------------------------------------------------------------------- #----- Profiles ------------------------ diff --git a/src/transducer/conversion/thermocouples/type_k.rs b/src/transducer/conversion/thermocouples/type_k.rs index 8bc742e..7356d1d 100644 --- a/src/transducer/conversion/thermocouples/type_k.rs +++ b/src/transducer/conversion/thermocouples/type_k.rs @@ -1,4 +1,5 @@ use crate::transducer::InvalidValue; +use libm::powf; use uom::si::electric_potential::{millivolt, volt}; use uom::si::f32; use uom::si::thermodynamic_temperature::degree_celsius; @@ -26,8 +27,7 @@ pub fn convert( let mv_pow7 = mv_pow6 * mv; let mv_pow8 = mv_pow7 * mv; - let celsius = 1.0 - + 2.5173462E+1 * mv + let celsius = 2.5173462E+1 * mv + -1.1662878 * mv_pow2 + -1.0833638 * mv_pow3 + -8.9773540E-1 * mv_pow4 @@ -42,8 +42,7 @@ pub fn convert( let mv_pow8 = mv_pow7 * mv; let mv_pow9 = mv_pow8 * mv; - let celsius = 1.0 - + 2.508355E+1 * mv + let celsius = 2.508355E+1 * mv + 7.860106E-2 * mv_pow2 + -2.503131E-1 * mv_pow3 + 8.315270E-2 * mv_pow4 @@ -72,21 +71,47 @@ pub fn convert( pub fn r_junction_offset_poly( temperature: f32::ThermodynamicTemperature, ) -> Result { - const T0: f32 = 2.5000000E+01; - const V0: f32 = 1.0003453E+00; - const P1: f32 = 4.0514854E-02; - const P2: f32 = -3.8789638E-05; - const P3: f32 = -2.8608478E-06; - const P4: f32 = -9.5367041E-10; - const Q1: f32 = -1.3948675E-03; - const Q2: f32 = -6.7976627E-05; - let celsius = temperature.get::(); - if celsius >= -20.0 && celsius <= 70.0 { - let offset_tcj = celsius - T0; - let numerator = offset_tcj + (P1 + offset_tcj * (P2 + offset_tcj * (P3 + P4 * offset_tcj))); - let denominator = 1.0 + offset_tcj * (Q1 + Q2 * offset_tcj); - let mv = V0 + numerator / denominator; + let cel_pow2 = celsius * celsius; + let cel_pow3 = cel_pow2 * celsius; + let cel_pow4 = cel_pow3 * celsius; + let cel_pow5 = cel_pow4 * celsius; + let cel_pow6 = cel_pow5 * celsius; + let cel_pow7 = cel_pow6 * celsius; + let cel_pow8 = cel_pow7 * celsius; + let cel_pow9 = cel_pow8 * celsius; + + if celsius >= -270.0 && celsius < 0.0 { + let cel_pow10 = cel_pow9 * celsius; + + let mv = 0.394501280250E-01 * celsius + + 0.236223735980E-04 * cel_pow2 + + -0.328589067840E-06 * cel_pow3 + + -0.499048287770E-08 * cel_pow4 + + -0.675090591730E-10 * cel_pow5 + + -0.574103274280E-12 * cel_pow6 + + -0.310888728940E-14 * cel_pow7 + + -0.104516093650E-16 * cel_pow8 + + -0.198892668780E-19 * cel_pow9 + + -0.163226974860E-22 * cel_pow10; + + Ok(f32::ElectricPotential::new::(mv)) + } else if celsius >= 0.0 && celsius <= 1372.0 { + let base = celsius - 0.126968600000E+03; + let exp = -0.118343200000E-03 * (base * base); + let addition = powf(0.1185976, exp); + + let mv = -0.176004136860E-01 + + 0.389212049750E-01 * celsius + + 0.185587700320E-04 * cel_pow2 + + -0.994575928740E-07 * cel_pow3 + + 0.318409457190E-09 * cel_pow4 + + -0.560728448890E-12 * cel_pow5 + + 0.560750590590E-15 * cel_pow6 + + -0.320207200030E-18 * cel_pow7 + + 0.971511471520E-22 * cel_pow8 + + -0.121047212750E-25 * cel_pow9 + + addition; Ok(f32::ElectricPotential::new::(mv)) } else { @@ -94,11 +119,12 @@ pub fn r_junction_offset_poly( } } +#[inline] pub fn r_junction_offset_lin( temperature: f32::ThermodynamicTemperature, ) -> Result { let celsius = temperature.get::(); - if celsius >= -2.0 && celsius <= 1000.0 { + if celsius >= -2.0 && celsius <= 800.0 { let mv = 0.041 * celsius; Ok(f32::ElectricPotential::new::(mv)) } else { -- 2.43.0 From c6f909ea1da01ec3dbf70921d4a068c73c73e884 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 14 Jul 2023 13:07:56 -0700 Subject: [PATCH 46/51] Example tweaks --- examples/ads1256/src/bin/multiplex.rs | 32 +++++++++++++-------------- examples/ads1256/src/bin/poll.rs | 25 +++++++++++++++------ 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/examples/ads1256/src/bin/multiplex.rs b/examples/ads1256/src/bin/multiplex.rs index 4460cc2..1abbef5 100644 --- a/examples/ads1256/src/bin/multiplex.rs +++ b/examples/ads1256/src/bin/multiplex.rs @@ -10,12 +10,12 @@ use {defmt_rtt as _, panic_probe as _}; use {embassy_executor as executor, embassy_stm32 as stm32}; +use ads1256::standard::input::SingleEnded; use ads1256::{ AdControl, Ads1256, AutoCal, BitOrder, BlockingDelay, Buffer, ClockOut, Config, DState, DataRate, DigitalIo, DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, MuxInput, OutputPin, Sdcs, SpiBus, Status, Wait, }; -use ads1256::standard::input::SingleEnded; use embassy_time::{Delay, Duration, Timer}; use executor::Spawner; use stm32::dma::NoDma; @@ -57,9 +57,9 @@ impl ads1256::BlockingDelay for Ads1256Delay { } struct Inputs { + ai0: StatefulPublisher, ai1: StatefulPublisher, ai2: StatefulPublisher, - ai3: StatefulPublisher, } // Inputs @@ -98,8 +98,12 @@ async fn main(spawner: Spawner) { )); let ads_1256 = ADS_1256.init(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready)); - + ads_1256.write_config(spi, AUTOCAL_CONF).unwrap(); let inputs = &*ANALOG_INPUTS.init(Inputs { + ai0: StatefulPublisher::new( + Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), + PubSubChannel::new().into(), + ), ai1: StatefulPublisher::new( Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), PubSubChannel::new().into(), @@ -108,10 +112,6 @@ async fn main(spawner: Spawner) { Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), PubSubChannel::new().into(), ), - ai3: StatefulPublisher::new( - Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), - PubSubChannel::new().into(), - ), }); spawner.spawn(log_task(inputs)).unwrap(); @@ -126,7 +126,7 @@ async fn drive_inputs_task( spi: &'static mut Spi<'static, SPI3, NoDma, NoDma>, inputs: &'static Inputs, ) { - let Inputs { ai1, ai2, ai3 } = inputs; + let Inputs { ai0, ai1, ai2 } = inputs; loop { let mut accumulator = f32::ElectricPotential::new::(0.0); @@ -136,7 +136,7 @@ async fn drive_inputs_task( .await .unwrap() .to_voltage(AUTOCAL_CONF.ad_control.gain()); - ai1.update(voltage); + ai0.update(voltage); accumulator += voltage; let voltage = ads_1256 @@ -144,7 +144,7 @@ async fn drive_inputs_task( .await .unwrap() .to_voltage(AUTOCAL_CONF.ad_control.gain()); - ai2.update(voltage); + ai1.update(voltage); accumulator += voltage; let voltage = ads_1256 @@ -152,7 +152,7 @@ async fn drive_inputs_task( .await .unwrap() .to_voltage(AUTOCAL_CONF.ad_control.gain()); - ai3.update(voltage); + ai2.update(voltage); accumulator += voltage; let accum_volts = accumulator.get::(); @@ -162,19 +162,19 @@ async fn drive_inputs_task( #[embassy_executor::task] async fn log_task(inputs: &'static Inputs) { - let Inputs { ai1, ai2, ai3 } = inputs; + let Inputs { ai0, ai1, ai2 } = inputs; + let mut ai0_sub = ai0.subscribe().unwrap(); let mut ai1_sub = ai1.subscribe().unwrap(); let mut ai2_sub = ai2.subscribe().unwrap(); - let mut ai3_sub = ai3.subscribe().unwrap(); loop { + let msg = ai0_sub.next_message_pure().await.get::(); + info!("Log task ai0: {}", msg); + let msg = ai1_sub.next_message_pure().await.get::(); info!("Log task ai1: {}", msg); let msg = ai2_sub.next_message_pure().await.get::(); info!("Log task ai2: {}", msg); - - let msg = ai3_sub.next_message_pure().await.get::(); - info!("Log task ai3: {}", msg); } } diff --git a/examples/ads1256/src/bin/poll.rs b/examples/ads1256/src/bin/poll.rs index 917629d..8469b96 100644 --- a/examples/ads1256/src/bin/poll.rs +++ b/examples/ads1256/src/bin/poll.rs @@ -3,6 +3,7 @@ #![feature(type_alias_impl_trait, async_fn_in_trait)] use core::cell::Cell; +use core::ops::DerefMut; use cortex_m::prelude::{ _embedded_hal_blocking_delay_DelayMs, _embedded_hal_blocking_delay_DelayUs, }; @@ -37,8 +38,8 @@ use embassy_sync::pubsub::PubSubChannel; use physical_ads1256::standard::multiplexer::poll::{ AutocalPoll, AutocalPollStatePub, ModInputOnly, }; -use physical_node::transducer::{Publish, Publisher, StatefulPublisher}; use physical_node::transducer::input::Poll; +use physical_node::transducer::{Publish, Publisher, StatefulPublisher}; const AUTOCAL_CONF: Config = Config { status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), @@ -106,7 +107,7 @@ async fn main(spawner: Spawner) { let ads1256_data_ready = ExtiInput::new(Input::new(p.PF6, Pull::Up), p.EXTI6); let select_ads1256 = Output::new(p.PF7, Level::High, Speed::VeryHigh); - let spi = &*SPI.init(Mutex::new(Spi::new( + let spi = SPI.init(Mutex::new(Spi::new( p.SPI3, p.PC10, p.PC12, @@ -118,7 +119,11 @@ async fn main(spawner: Spawner) { ))); let ads_1256 = - &*ADS_1256.init(Mutex::new(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready))); + ADS_1256.init(Mutex::new(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready))); + ads_1256 + .get_mut() + .write_config(spi.get_mut(), AUTOCAL_CONF) + .unwrap(); let inputs = &*ANALOG_INPUTS.init(Inputs { ai1: AutocalPollStatePub { @@ -161,13 +166,19 @@ async fn main(spawner: Spawner) { spawner.spawn(log_task(&inputs.ai1, 1)).unwrap(); spawner.spawn(log_task(&inputs.ai2, 2)).unwrap(); spawner.spawn(log_task(&inputs.ai3, 3)).unwrap(); - spawner.spawn(poll_task(&inputs.ai1, 1, Duration::from_secs(5))).unwrap(); - spawner.spawn(poll_task(&inputs.ai2, 2, Duration::from_secs(10))).unwrap(); - spawner.spawn(poll_task(&inputs.ai3, 3, Duration::from_secs(20))).unwrap(); + spawner + .spawn(poll_task(&inputs.ai1, 1, Duration::from_secs(5))) + .unwrap(); + spawner + .spawn(poll_task(&inputs.ai2, 2, Duration::from_secs(10))) + .unwrap(); + spawner + .spawn(poll_task(&inputs.ai3, 3, Duration::from_secs(20))) + .unwrap(); } #[embassy_executor::task(pool_size = 3)] -async fn poll_task(input: &'static ExampleInput, input_num: u8, every: Duration,) { +async fn poll_task(input: &'static ExampleInput, input_num: u8, every: Duration) { loop { Timer::after(every).await; let result = input.poll().await.unwrap().get::(); -- 2.43.0 From bfd9c3e4e69534df4138889509b31b37942aa572 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 14 Jul 2023 13:31:27 -0700 Subject: [PATCH 47/51] Example tweaks --- examples/ads1256/src/bin/multiplex.rs | 1 + examples/ads1256/src/bin/poll.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/ads1256/src/bin/multiplex.rs b/examples/ads1256/src/bin/multiplex.rs index 1abbef5..91f9bb9 100644 --- a/examples/ads1256/src/bin/multiplex.rs +++ b/examples/ads1256/src/bin/multiplex.rs @@ -99,6 +99,7 @@ async fn main(spawner: Spawner) { let ads_1256 = ADS_1256.init(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready)); ads_1256.write_config(spi, AUTOCAL_CONF).unwrap(); + let inputs = &*ANALOG_INPUTS.init(Inputs { ai0: StatefulPublisher::new( Cell::new(f32::ElectricPotential::new::(f32::NAN)).into(), diff --git a/examples/ads1256/src/bin/poll.rs b/examples/ads1256/src/bin/poll.rs index 8469b96..5c2cfd1 100644 --- a/examples/ads1256/src/bin/poll.rs +++ b/examples/ads1256/src/bin/poll.rs @@ -120,6 +120,7 @@ async fn main(spawner: Spawner) { let ads_1256 = ADS_1256.init(Mutex::new(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready))); + ads_1256 .get_mut() .write_config(spi.get_mut(), AUTOCAL_CONF) -- 2.43.0 From abde12a8badd95314773de6ec1d961d782f6a6d2 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 14 Jul 2023 20:35:39 -0700 Subject: [PATCH 48/51] Thermocouple example --- examples/ads1256/Cargo.toml | 3 + examples/ads1256/src/bin/thermocouple.rs | 117 ++++++++++++++++++ src/transducer/conversion/mod.rs | 4 - src/transducer/mod.rs | 5 +- .../thermocouples => thermocouple}/mod.rs | 0 .../thermocouples => thermocouple}/type_k.rs | 1 + 6 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 examples/ads1256/src/bin/thermocouple.rs delete mode 100644 src/transducer/conversion/mod.rs rename src/transducer/{conversion/thermocouples => thermocouple}/mod.rs (100%) rename src/transducer/{conversion/thermocouples => thermocouple}/type_k.rs (99%) diff --git a/examples/ads1256/Cargo.toml b/examples/ads1256/Cargo.toml index c847d66..23fed7f 100644 --- a/examples/ads1256/Cargo.toml +++ b/examples/ads1256/Cargo.toml @@ -7,6 +7,9 @@ repository.workspace = true readme.workspace = true license.workspace = true +[dependencies.physical] +path = "../.." +features = ["thermocouple_k"] [dependencies.physical-node] path = "../../node" features = ["embassy-sync"] diff --git a/examples/ads1256/src/bin/thermocouple.rs b/examples/ads1256/src/bin/thermocouple.rs new file mode 100644 index 0000000..4e0e8a3 --- /dev/null +++ b/examples/ads1256/src/bin/thermocouple.rs @@ -0,0 +1,117 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait, async_fn_in_trait)] + +use core::cell::Cell; +use cortex_m::prelude::{ + _embedded_hal_blocking_delay_DelayMs, _embedded_hal_blocking_delay_DelayUs, +}; +use {defmt_rtt as _, panic_probe as _}; + +use {embassy_executor as executor, embassy_stm32 as stm32}; + +use ads1256::standard::input::SingleEnded; +use ads1256::{ + AdControl, Ads1256, AutoCal, BitOrder, BlockingDelay, Buffer, ClockOut, Config, DState, + DataRate, DigitalIo, DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, + MuxInput, OutputPin, Sdcs, SpiBus, Status, Wait, +}; +use embassy_time::{Delay, Duration, Timer}; +use executor::Spawner; +use stm32::dma::NoDma; +use stm32::exti::ExtiInput; +use stm32::gpio::{Input, Level, Output, Pull, Speed}; +use stm32::spi::Spi; +use stm32::time::Hertz; +use stm32::{pac, spi}; + +use uom::si::electric_potential::{millivolt, volt}; +use uom::si::f32; + +use defmt::{debug, error, info, trace, unwrap}; +use physical::transducer::thermocouple_k; +use uom::si::thermodynamic_temperature::degree_celsius; + +const AUTOCAL_CONF: Config = Config { + status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), + multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::Common), + ad_control: AdControl::setting(Gain::X64, Sdcs::Off, ClockOut::Off), + data_rate: DataRate::Sps2_5, + digital_io: DigitalIo::setting(DigitalIoState::default(), DigitalIoDirection::default()), +}; + +struct Ads1256Delay; + +impl ads1256::BlockingDelay for Ads1256Delay { + #[inline] + fn t6_delay(&mut self) { + Delay.delay_us(1u32); + } + + fn t11_1_delay(&mut self) {} + + fn t11_2_delay(&mut self) {} +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + unsafe { + pac::FLASH.acr().modify(|v| { + v.set_prften(true); + v.set_icen(true); + v.set_dcen(true); + }); + } + + let p = embassy_stm32::init(Default::default()); + + let mut spi_conf = spi::Config::default(); + spi_conf.mode = spi::MODE_1; + spi_conf.bit_order = spi::BitOrder::MsbFirst; + + let ads1256_data_ready = ExtiInput::new(Input::new(p.PF6, Pull::Up), p.EXTI6); + let select_ads1256 = Output::new(p.PF7, Level::High, Speed::VeryHigh); + + let mut spi = Spi::new( + p.SPI3, + p.PC10, + p.PC12, + p.PC11, + NoDma, + NoDma, + Hertz(ads1256::defaults::SPI_CLK_HZ), + spi_conf, + ); + + let mut ads_1256 = Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready); + ads_1256.write_config(&mut spi, AUTOCAL_CONF).unwrap(); + ads_1256.self_calibrate(&mut spi).await.unwrap(); + + let reference_temp = f32::ThermodynamicTemperature::new::(26.0); + + // Zero test + let zero_voltage = f32::ElectricPotential::new::(0.0); + let zero_temperature = thermocouple_k::convert( + zero_voltage, + reference_temp, + thermocouple_k::r_junction_offset_lin, + ) + .unwrap(); + let zero_celsius = zero_temperature.get::(); + info!("Zero test: {}", zero_celsius); + + loop { + Delay.delay_ms(1_000u16); + let voltage = ads_1256 + .read_data(&mut spi) + .await + .unwrap() + .to_voltage(AUTOCAL_CONF.ad_control.gain()); + let mv = voltage.get::(); + let temperature = + thermocouple_k::convert(voltage, reference_temp, thermocouple_k::r_junction_offset_lin) + .unwrap(); + let celsius = temperature.get::(); + info!("Temperature in degrees celsius: {}, millivolts: {}", celsius, mv); + } +} diff --git a/src/transducer/conversion/mod.rs b/src/transducer/conversion/mod.rs deleted file mode 100644 index fe6f177..0000000 --- a/src/transducer/conversion/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod thermocouples; - -#[cfg(feature = "thermocouple_k")] -pub use thermocouples::type_k as thermocouple_k; \ No newline at end of file diff --git a/src/transducer/mod.rs b/src/transducer/mod.rs index 6f907f7..b0cdd72 100644 --- a/src/transducer/mod.rs +++ b/src/transducer/mod.rs @@ -3,7 +3,10 @@ use crate::cell::CellView; pub mod input; pub mod output; -mod conversion; +mod thermocouple; + +#[cfg(feature = "thermocouple_k")] +pub use thermocouple::type_k as thermocouple_k; // Initialisation will always be async and won't complete until a state is available for all // stateful transducers. diff --git a/src/transducer/conversion/thermocouples/mod.rs b/src/transducer/thermocouple/mod.rs similarity index 100% rename from src/transducer/conversion/thermocouples/mod.rs rename to src/transducer/thermocouple/mod.rs diff --git a/src/transducer/conversion/thermocouples/type_k.rs b/src/transducer/thermocouple/type_k.rs similarity index 99% rename from src/transducer/conversion/thermocouples/type_k.rs rename to src/transducer/thermocouple/type_k.rs index 7356d1d..d95edac 100644 --- a/src/transducer/conversion/thermocouples/type_k.rs +++ b/src/transducer/thermocouple/type_k.rs @@ -68,6 +68,7 @@ pub fn convert( } } +//TODO: This is not working, check libm pow. pub fn r_junction_offset_poly( temperature: f32::ThermodynamicTemperature, ) -> Result { -- 2.43.0 From 45bc1707e9fc88816d79ec90512c4e10d8c41bb6 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Fri, 14 Jul 2023 22:45:59 -0700 Subject: [PATCH 49/51] Thermocouple type k conversion updates. --- examples/ads1256/src/bin/thermocouple.rs | 11 +--- src/transducer/thermocouple/type_k.rs | 65 +++++++++++++++++++----- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/examples/ads1256/src/bin/thermocouple.rs b/examples/ads1256/src/bin/thermocouple.rs index 4e0e8a3..250bcc0 100644 --- a/examples/ads1256/src/bin/thermocouple.rs +++ b/examples/ads1256/src/bin/thermocouple.rs @@ -91,12 +91,7 @@ async fn main(spawner: Spawner) { // Zero test let zero_voltage = f32::ElectricPotential::new::(0.0); - let zero_temperature = thermocouple_k::convert( - zero_voltage, - reference_temp, - thermocouple_k::r_junction_offset_lin, - ) - .unwrap(); + let zero_temperature = thermocouple_k::convert_direct(zero_voltage, reference_temp).unwrap(); let zero_celsius = zero_temperature.get::(); info!("Zero test: {}", zero_celsius); @@ -108,9 +103,7 @@ async fn main(spawner: Spawner) { .unwrap() .to_voltage(AUTOCAL_CONF.ad_control.gain()); let mv = voltage.get::(); - let temperature = - thermocouple_k::convert(voltage, reference_temp, thermocouple_k::r_junction_offset_lin) - .unwrap(); + let temperature = thermocouple_k::convert_direct(voltage, reference_temp).unwrap(); let celsius = temperature.get::(); info!("Temperature in degrees celsius: {}, millivolts: {}", celsius, mv); } diff --git a/src/transducer/thermocouple/type_k.rs b/src/transducer/thermocouple/type_k.rs index d95edac..44ff7da 100644 --- a/src/transducer/thermocouple/type_k.rs +++ b/src/transducer/thermocouple/type_k.rs @@ -4,19 +4,10 @@ use uom::si::electric_potential::{millivolt, volt}; use uom::si::f32; use uom::si::thermodynamic_temperature::degree_celsius; -/// Convert from a voltage produced by a type k thermocouple to a temperature using polynomial with -/// NIST coefficients, this method has a maximum error of 0.06°C in addition to the underlying -/// thermocouple inaccuracy. -/// -/// This function uses the [NIST type K thermocouple linearisation polynomial](https://srdata.nist.gov/its90/type_k/kcoefficients_inverse.html). -pub fn convert( +fn _convert( voltage: f32::ElectricPotential, - r_junction: f32::ThermodynamicTemperature, - r_junction_offset: fn( - r_junction: f32::ThermodynamicTemperature, - ) -> Result, ) -> Result { - let mv = voltage.get::() + r_junction_offset(r_junction)?.get::(); + let mv = voltage.get::(); let mv_pow2 = mv * mv; let mv_pow3 = mv_pow2 * mv; let mv_pow4 = mv_pow3 * mv; @@ -68,8 +59,56 @@ pub fn convert( } } +/// Convert from a voltage produced by a type k thermocouple to a temperature using polynomial and +/// directly adding the reference junction temperature to the result for offset compensation. +/// +/// Can be useful compared to [convert_seebeck] when the reference temperature or the temperature +/// being read by the thermocouple is fairly close to 0. +/// +/// This function uses the [NIST type K thermocouple linearisation polynomial](https://srdata.nist.gov/its90/type_k/kcoefficients_inverse.html). +#[inline] +pub fn convert_direct( + voltage: f32::ElectricPotential, + r_junction: f32::ThermodynamicTemperature, +) -> Result { + let base_celsius = _convert(voltage)?.get::(); + let r_junction_celsius = r_junction.get::(); + + Ok(f32::ThermodynamicTemperature::new::(base_celsius + r_junction_celsius)) +} + +/// Convert from a voltage produced by a type k thermocouple to a temperature using polynomial and +/// using a constant seebeck coefficient to correct the input voltage for offset compensation. +/// +/// Probably the right choice most of the time. +/// +/// This function uses the [NIST type K thermocouple linearisation polynomial](https://srdata.nist.gov/its90/type_k/kcoefficients_inverse.html). +#[inline] +pub fn convert_seebeck( + voltage: f32::ElectricPotential, + r_junction: f32::ThermodynamicTemperature, +) -> Result { + let voltage_correction = temp_to_voltage_seebeck(r_junction)?; + _convert(voltage + voltage_correction) +} + +/// Convert from a voltage produced by a type k thermocouple to a temperature using polynomial and +/// using a polynomial to correct the input voltage for offset compensation. +/// +/// This is the most accurate method but uses the most processor cycles by a wide margin. +/// +/// This function uses the [NIST type K thermocouple linearisation polynomial](https://srdata.nist.gov/its90/type_k/kcoefficients_inverse.html). +#[inline] +pub fn convert_polynomial( + voltage: f32::ElectricPotential, + r_junction: f32::ThermodynamicTemperature, +) -> Result { + let voltage_correction = temp_to_voltage_poly(r_junction)?; + _convert(voltage + voltage_correction) +} + //TODO: This is not working, check libm pow. -pub fn r_junction_offset_poly( +pub fn temp_to_voltage_poly( temperature: f32::ThermodynamicTemperature, ) -> Result { let celsius = temperature.get::(); @@ -121,7 +160,7 @@ pub fn r_junction_offset_poly( } #[inline] -pub fn r_junction_offset_lin( +pub fn temp_to_voltage_seebeck( temperature: f32::ThermodynamicTemperature, ) -> Result { let celsius = temperature.get::(); -- 2.43.0 From 5e4611697684fadbf0b7dcc6b48b0644c631c49d Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 18 Jul 2023 19:52:50 -0700 Subject: [PATCH 50/51] Added LM35 --- Cargo.toml | 1 + examples/ads1256/Cargo.toml | 2 +- examples/ads1256/src/bin/thermocouple.rs | 46 +++++++++++++------ src/transducer/mod.rs | 5 +- src/transducer/part/lm35.rs | 22 +++++++++ src/transducer/part/mod.rs | 7 +++ src/transducer/{ => part}/thermocouple/mod.rs | 0 .../{ => part}/thermocouple/type_k.rs | 0 8 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 src/transducer/part/lm35.rs create mode 100644 src/transducer/part/mod.rs rename src/transducer/{ => part}/thermocouple/mod.rs (100%) rename src/transducer/{ => part}/thermocouple/type_k.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 9064c91..87ff90b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,6 +128,7 @@ license.workspace = true [features] thermocouple_k = [] +lm35 = [] [dependencies] uom = { workspace = true } diff --git a/examples/ads1256/Cargo.toml b/examples/ads1256/Cargo.toml index 23fed7f..b75e37d 100644 --- a/examples/ads1256/Cargo.toml +++ b/examples/ads1256/Cargo.toml @@ -9,7 +9,7 @@ license.workspace = true [dependencies.physical] path = "../.." -features = ["thermocouple_k"] +features = ["thermocouple_k", "lm35"] [dependencies.physical-node] path = "../../node" features = ["embassy-sync"] diff --git a/examples/ads1256/src/bin/thermocouple.rs b/examples/ads1256/src/bin/thermocouple.rs index 250bcc0..7a32bc6 100644 --- a/examples/ads1256/src/bin/thermocouple.rs +++ b/examples/ads1256/src/bin/thermocouple.rs @@ -10,7 +10,7 @@ use {defmt_rtt as _, panic_probe as _}; use {embassy_executor as executor, embassy_stm32 as stm32}; -use ads1256::standard::input::SingleEnded; +use ads1256::standard::input::{Differential, SingleEnded}; use ads1256::{ AdControl, Ads1256, AutoCal, BitOrder, BlockingDelay, Buffer, ClockOut, Config, DState, DataRate, DigitalIo, DigitalIoDirection, DigitalIoState, DioDirection, Gain, Multiplexer, @@ -29,12 +29,12 @@ use uom::si::electric_potential::{millivolt, volt}; use uom::si::f32; use defmt::{debug, error, info, trace, unwrap}; -use physical::transducer::thermocouple_k; +use physical::transducer::{lm35, thermocouple_k}; use uom::si::thermodynamic_temperature::degree_celsius; const AUTOCAL_CONF: Config = Config { status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst), - multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::Common), + multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::AIn1), ad_control: AdControl::setting(Gain::X64, Sdcs::Off, ClockOut::Off), data_rate: DataRate::Sps2_5, digital_io: DigitalIo::setting(DigitalIoState::default(), DigitalIoDirection::default()), @@ -87,24 +87,40 @@ async fn main(spawner: Spawner) { ads_1256.write_config(&mut spi, AUTOCAL_CONF).unwrap(); ads_1256.self_calibrate(&mut spi).await.unwrap(); - let reference_temp = f32::ThermodynamicTemperature::new::(26.0); - - // Zero test - let zero_voltage = f32::ElectricPotential::new::(0.0); - let zero_temperature = thermocouple_k::convert_direct(zero_voltage, reference_temp).unwrap(); - let zero_celsius = zero_temperature.get::(); - info!("Zero test: {}", zero_celsius); - loop { - Delay.delay_ms(1_000u16); + let gain = Gain::X2; + let reference = ads_1256 + .autocal_convert( + &mut spi, + Differential::AIn0.into(), + None, + Some(AUTOCAL_CONF.ad_control.with_gain(gain)), + None, + false, + ) + .await + .unwrap() + .to_voltage(gain); + let reference = lm35::convert(reference).unwrap(); + let reference_celsius = reference.get::(); + info!("Reference junction temperature in degrees celsius: {}", reference_celsius); + + let gain = Gain::X64; let voltage = ads_1256 - .read_data(&mut spi) + .autocal_convert( + &mut spi, + Differential::AIn1.into(), + None, + Some(AUTOCAL_CONF.ad_control.with_gain(gain)), + None, + false, + ) .await .unwrap() .to_voltage(AUTOCAL_CONF.ad_control.gain()); let mv = voltage.get::(); - let temperature = thermocouple_k::convert_direct(voltage, reference_temp).unwrap(); + let temperature = thermocouple_k::convert_direct(voltage, reference).unwrap(); let celsius = temperature.get::(); - info!("Temperature in degrees celsius: {}, millivolts: {}", celsius, mv); + info!("Thermocouple temperature in degrees celsius: {}, millivolts: {}", celsius, mv); } } diff --git a/src/transducer/mod.rs b/src/transducer/mod.rs index b0cdd72..d79c453 100644 --- a/src/transducer/mod.rs +++ b/src/transducer/mod.rs @@ -3,10 +3,9 @@ use crate::cell::CellView; pub mod input; pub mod output; -mod thermocouple; +mod part; -#[cfg(feature = "thermocouple_k")] -pub use thermocouple::type_k as thermocouple_k; +pub use part::*; // Initialisation will always be async and won't complete until a state is available for all // stateful transducers. diff --git a/src/transducer/part/lm35.rs b/src/transducer/part/lm35.rs new file mode 100644 index 0000000..0f492da --- /dev/null +++ b/src/transducer/part/lm35.rs @@ -0,0 +1,22 @@ +use crate::transducer::InvalidValue; +use uom::si::electric_potential::volt; +use uom::si::f32; +use uom::si::thermodynamic_temperature::degree_celsius; + +const MIN_VOLTS: f32 = -0.55; +const MAX_VOLTS: f32 = 1.50; +const SCALE_FACTOR: f32 = 100.0; + +#[inline] +pub fn convert( + voltage: f32::ElectricPotential, +) -> Result { + let volts = voltage.get::(); + + if volts >= MIN_VOLTS && volts <= MAX_VOLTS { + let celsius = volts * SCALE_FACTOR; + Ok(f32::ThermodynamicTemperature::new::(celsius)) + } else { + Err(InvalidValue) + } +} diff --git a/src/transducer/part/mod.rs b/src/transducer/part/mod.rs new file mode 100644 index 0000000..cd42413 --- /dev/null +++ b/src/transducer/part/mod.rs @@ -0,0 +1,7 @@ +mod thermocouple; + +#[cfg(feature = "lm35")] +pub mod lm35; + +#[cfg(feature = "thermocouple_k")] +pub use thermocouple::type_k as thermocouple_k; \ No newline at end of file diff --git a/src/transducer/thermocouple/mod.rs b/src/transducer/part/thermocouple/mod.rs similarity index 100% rename from src/transducer/thermocouple/mod.rs rename to src/transducer/part/thermocouple/mod.rs diff --git a/src/transducer/thermocouple/type_k.rs b/src/transducer/part/thermocouple/type_k.rs similarity index 100% rename from src/transducer/thermocouple/type_k.rs rename to src/transducer/part/thermocouple/type_k.rs -- 2.43.0 From c2c9348c3c3e0dab3788732305032e7e71e95cc2 Mon Sep 17 00:00:00 2001 From: Zachary Sunforge Date: Tue, 18 Jul 2023 22:26:01 -0700 Subject: [PATCH 51/51] Added LM35 --- examples/ads1256/src/bin/thermocouple.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/ads1256/src/bin/thermocouple.rs b/examples/ads1256/src/bin/thermocouple.rs index 7a32bc6..5fe194f 100644 --- a/examples/ads1256/src/bin/thermocouple.rs +++ b/examples/ads1256/src/bin/thermocouple.rs @@ -88,11 +88,11 @@ async fn main(spawner: Spawner) { ads_1256.self_calibrate(&mut spi).await.unwrap(); loop { - let gain = Gain::X2; + let gain = Gain::X4; let reference = ads_1256 .autocal_convert( &mut spi, - Differential::AIn0.into(), + SingleEnded::AIn2.into(), None, Some(AUTOCAL_CONF.ad_control.with_gain(gain)), None, @@ -103,13 +103,13 @@ async fn main(spawner: Spawner) { .to_voltage(gain); let reference = lm35::convert(reference).unwrap(); let reference_celsius = reference.get::(); - info!("Reference junction temperature in degrees celsius: {}", reference_celsius); + info!("Reference junction temperature: {}°C", reference_celsius); let gain = Gain::X64; let voltage = ads_1256 .autocal_convert( &mut spi, - Differential::AIn1.into(), + Differential::AIn0.into(), None, Some(AUTOCAL_CONF.ad_control.with_gain(gain)), None, @@ -121,6 +121,6 @@ async fn main(spawner: Spawner) { let mv = voltage.get::(); let temperature = thermocouple_k::convert_direct(voltage, reference).unwrap(); let celsius = temperature.get::(); - info!("Thermocouple temperature in degrees celsius: {}, millivolts: {}", celsius, mv); + info!("Thermocouple temperature: {}°C, millivolts: {}", celsius, mv); } } -- 2.43.0