From aa1d4a93710767bd0403bd42e58736cab452c9b4 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 30 Aug 2024 23:16:31 -0400 Subject: [PATCH] split into hardware dependent / independent --- .gitignore | 4 ++ gem-remotes-esp32/.gitignore | 4 -- gem-remotes-esp32/Cargo.toml | 1 + gem-remotes-esp32/src/ble_server.rs | 17 ++++--- gem-remotes-esp32/src/main.rs | 23 +++++----- gem-remotes-esp32/src/message_timer.rs | 6 ++- gem-remotes-esp32/src/motor_driver.rs | 32 ++++++------- gem-remotes-esp32/src/pair_button_driver.rs | 8 ++-- gem-remotes-esp32/src/test_console.rs | 7 ++- gem-remotes-lib/Cargo.toml | 11 +++++ .../src/commands.rs | 8 ---- .../src/dispatch.rs | 17 ++++--- gem-remotes-lib/src/lib.rs | 45 +++++++++++++++++++ .../src/motor_controller.rs | 28 +++++++----- 14 files changed, 142 insertions(+), 69 deletions(-) create mode 100644 .gitignore delete mode 100644 gem-remotes-esp32/.gitignore create mode 100644 gem-remotes-lib/Cargo.toml rename {gem-remotes-esp32 => gem-remotes-lib}/src/commands.rs (94%) rename {gem-remotes-esp32 => gem-remotes-lib}/src/dispatch.rs (82%) create mode 100644 gem-remotes-lib/src/lib.rs rename {gem-remotes-esp32 => gem-remotes-lib}/src/motor_controller.rs (96%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50a07d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vscode +.embuild +target +Cargo.lock diff --git a/gem-remotes-esp32/.gitignore b/gem-remotes-esp32/.gitignore deleted file mode 100644 index 73a638b..0000000 --- a/gem-remotes-esp32/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.vscode -/.embuild -/target -/Cargo.lock diff --git a/gem-remotes-esp32/Cargo.toml b/gem-remotes-esp32/Cargo.toml index 7d9128b..e6455bf 100644 --- a/gem-remotes-esp32/Cargo.toml +++ b/gem-remotes-esp32/Cargo.toml @@ -28,6 +28,7 @@ experimental = ["esp-idf-svc/experimental"] embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"] [dependencies] +gem-remotes-lib = { path = "../gem-remotes-lib" } log = { version = "0.4", default-features = false, features = ["max_level_trace"] } esp-idf-svc = { version = "0.49", default-features = false } esp32-nimble = "0.7.0" diff --git a/gem-remotes-esp32/src/ble_server.rs b/gem-remotes-esp32/src/ble_server.rs index 04db446..abdd83b 100644 --- a/gem-remotes-esp32/src/ble_server.rs +++ b/gem-remotes-esp32/src/ble_server.rs @@ -8,8 +8,13 @@ use bitflags::bitflags; use std::str::FromStr; use std::sync::Arc; -use crate::dispatch::{Dispatch, RecvQ, SendQ}; -use crate::commands::{Button, Commands}; +use gem_remotes_lib::{ + Dispatch, + DispatchRecvQ, + DispatchSendQ, + Commands, + Button, +}; // TODO HARDWARE: test these values to see if they are suitable const BLE_MIN_INTERVAL: u16 = 24; // x 1.25ms @@ -41,8 +46,8 @@ const BLE_BUTTON_RELEASE: u8 = 0; const BLE_BUTTON_PRESS: u8 = 1; pub struct BleServer { - send_q: SendQ, - recv_q: RecvQ, + send_q: DispatchSendQ, + recv_q: DispatchRecvQ, svc_flags: SvcFlags, dev_name: String, @@ -205,7 +210,7 @@ impl BleServer { } -fn on_bluetooth_cmd(sender: &SendQ, args: &mut OnWriteArgs, cmd: Commands) { +fn on_bluetooth_cmd(sender: &DispatchSendQ, args: &mut OnWriteArgs, cmd: Commands) { let v = args.recv_data(); // receiving incorrect data isn't fatal, but being unable to send events is. let attempt = match cmd { @@ -264,7 +269,7 @@ fn set_device_security(dev: &mut BLEDevice) { .resolve_rpa(); } -fn set_server_callbacks(server: &mut BLEServer, sender: SendQ) { +fn set_server_callbacks(server: &mut BLEServer, sender: DispatchSendQ) { server.on_connect(move |server, clntdesc| { // Print connected client data info!("client connected: {:?}", clntdesc); diff --git a/gem-remotes-esp32/src/main.rs b/gem-remotes-esp32/src/main.rs index 853ab4a..fbbccc3 100644 --- a/gem-remotes-esp32/src/main.rs +++ b/gem-remotes-esp32/src/main.rs @@ -1,6 +1,6 @@ -const BUTTON_HOLD_TIME_MS: u64 = 500; -const STOP_SAFETY_TIME_MS: u64 = 2000; -const PAIR_TIME_MS: u64 = 10000; //180000; TODO:Debug go back to 3 minutes from 10s +const BUTTON_HOLD_TIME_MS: u64 = 1_200; +const STOP_SAFETY_TIME_MS: u64 = 2_000; +const PAIR_TIME_MS: u64 = 30_000; // Crates used in release use log::*; //{trace, debug, info, warn, error} @@ -10,20 +10,23 @@ use futures_lite::future; use std::panic; use std::ops::Deref; +use gem_remotes_lib::{ + Commands, + Controller, + Dispatch, +}; + // Debug modules mod test_console; // Release modules -mod commands; -mod dispatch; -mod motor_controller; mod motor_driver; mod message_timer; mod ble_server; mod pair_button_driver; use crate::message_timer::MessageTimer; -use crate::commands::Commands; +//use crate::commands::Commands; //TODO: limit switch driver, would be good in long run if it checked limit switches periodically (every 1s?) to ensure they are still functioning @@ -67,14 +70,14 @@ async fn main_loop() -> Result<()> { info!("Entering main loop"); // Create dispatch early so it can outlive most other things - let mut dp = dispatch::Dispatch::new(); + let mut dp = Dispatch::new(); // Debug Drivers (TODO DEBUG: remove debug) let motor_driver = motor_driver::MotorDriverDebug::new(); // Setup of various drivers that need to out-live the executor - let m_chan = motor_controller::Controller::prepare_controller(&mut dp); - let mut motor_control = motor_controller::Controller::new(m_chan, dp.get_cmd_channel(), motor_driver.get_endpoint()); + let m_chan = Controller::prepare_controller(&mut dp); + let mut motor_control = Controller::new(m_chan, dp.get_cmd_channel(), motor_driver.get_endpoint()); // Setup callback timers let mut button_timer = MessageTimer::::new_on_dispatch( Commands::ButtonTimerRestart, diff --git a/gem-remotes-esp32/src/message_timer.rs b/gem-remotes-esp32/src/message_timer.rs index 01e8a80..02785ea 100644 --- a/gem-remotes-esp32/src/message_timer.rs +++ b/gem-remotes-esp32/src/message_timer.rs @@ -6,8 +6,10 @@ use core::time::Duration; use async_channel::{Receiver, Sender}; use futures_lite::FutureExt; -use crate::dispatch::Dispatch; -use crate::commands::Commands; +use gem_remotes_lib::{ + Commands, + Dispatch +}; #[derive(Copy, Clone)] enum State { diff --git a/gem-remotes-esp32/src/motor_driver.rs b/gem-remotes-esp32/src/motor_driver.rs index 121c9e3..4a1d495 100644 --- a/gem-remotes-esp32/src/motor_driver.rs +++ b/gem-remotes-esp32/src/motor_driver.rs @@ -2,34 +2,26 @@ use log::*; //{trace, debug, info, warn, error} use anyhow::Result; -use async_channel::{unbounded, Receiver, Sender}; +use async_channel::unbounded; +use gem_remotes_lib::{MotorCommands, MotorRecvQ, MotorSendQ}; -#[derive(Clone, Copy, Debug)] -pub enum Commands { - StartUp, - StartDown, - Stop -} - -pub type SendQ = Sender; -pub type RecvQ = Receiver; pub struct MotorDriverDebug{ - endpoint: SendQ, // Endpoint to hand to dispatch or anyone else sending commands here. - recv_q: RecvQ, + endpoint: MotorSendQ, // Endpoint to hand to dispatch or anyone else sending commands here. + recv_q: MotorRecvQ, } /// Debug / example version of Motor Driver. impl MotorDriverDebug { pub fn new() -> Self { - let (s,r) = unbounded(); + let (s,r) = unbounded(); // TODO: reserve a reasonable amount for all unbounded? MotorDriverDebug { endpoint: s, recv_q: r, } } - pub fn get_endpoint(&self) -> SendQ { + pub fn get_endpoint(&self) -> MotorSendQ { self.endpoint.clone() } @@ -40,11 +32,11 @@ impl MotorDriverDebug { } } - async fn handle_cmd(&self, cmd: Commands) -> Result<()> { + async fn handle_cmd(&self, cmd: MotorCommands) -> Result<()> { match cmd { - Commands::StartUp => {self.start_up().await?;} - Commands::StartDown => {self.start_down().await?;} - Commands::Stop => {self.stop().await?;} + MotorCommands::StartUp => {self.start_up().await?;} + MotorCommands::StartDown => {self.start_down().await?;} + MotorCommands::Stop => {self.stop().await?;} } Ok(()) } @@ -62,3 +54,7 @@ impl MotorDriverDebug { Ok(()) } } + + +//TODO: we should fix panic to ensure that we shut down motors before rebooting ESP! +// Maybe by getting another endpoint and passing it to the panic handler? Add a different command that doesn't just stop, but stops and stops processing any new commands. \ No newline at end of file diff --git a/gem-remotes-esp32/src/pair_button_driver.rs b/gem-remotes-esp32/src/pair_button_driver.rs index df23b2a..7a2fb29 100644 --- a/gem-remotes-esp32/src/pair_button_driver.rs +++ b/gem-remotes-esp32/src/pair_button_driver.rs @@ -4,8 +4,10 @@ use async_channel::Sender; use esp_idf_svc::timer::EspTaskTimerService; use core::time::Duration; -use crate::commands::Commands; -use crate::dispatch::Dispatch; +use gem_remotes_lib::{ + Commands, + Dispatch, +}; type SendQ = Sender; @@ -25,7 +27,7 @@ impl PairButtonDriver { debug!("Waiting on pairing button presses"); loop { //TO DO: Watch for incoming pair button presses from the PIC and/or hardware buttons - async_timer.after(Duration::from_millis(10000)).await?; // no need to panic on test console driver timer failure + async_timer.after(Duration::from_millis(10_000)).await?; // no need to panic on test console driver timer failure //When we find a press, send PicRecvPair } } diff --git a/gem-remotes-esp32/src/test_console.rs b/gem-remotes-esp32/src/test_console.rs index 37785f5..3ed3250 100644 --- a/gem-remotes-esp32/src/test_console.rs +++ b/gem-remotes-esp32/src/test_console.rs @@ -24,10 +24,15 @@ use ::{ }, core::time::Duration, }; -use crate::commands::{Button, Commands}; + use async_channel::Sender; use log::*; //{trace, debug, info, warn, error} +use gem_remotes_lib::{ + Button, + Commands +}; + #[derive(Command)] pub enum Menu{//<'a> { /// Simulate the PIC controller sending aus n Up character diff --git a/gem-remotes-lib/Cargo.toml b/gem-remotes-lib/Cargo.toml new file mode 100644 index 0000000..4d07a7f --- /dev/null +++ b/gem-remotes-lib/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "gem-remotes-lib" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.86" +async-channel = "2.3.1" +log = "0.4.22" +strum = "0.26.3" +strum_macros = "0.26.4" diff --git a/gem-remotes-esp32/src/commands.rs b/gem-remotes-lib/src/commands.rs similarity index 94% rename from gem-remotes-esp32/src/commands.rs rename to gem-remotes-lib/src/commands.rs index 5fc6527..86f5df2 100644 --- a/gem-remotes-esp32/src/commands.rs +++ b/gem-remotes-lib/src/commands.rs @@ -46,14 +46,6 @@ pub enum Commands { EraseBleBonds, } -#[non_exhaustive] -/*pub struct Button; - -impl Button { - pub const PRESSED: u8 = 0x1; - pub const RELEASED: u8 = 0x0; -}*/ - #[derive(Copy, Clone, Debug)] pub enum Button { Released = 0, diff --git a/gem-remotes-esp32/src/dispatch.rs b/gem-remotes-lib/src/dispatch.rs similarity index 82% rename from gem-remotes-esp32/src/dispatch.rs rename to gem-remotes-lib/src/dispatch.rs index 26fe740..dd0e44c 100644 --- a/gem-remotes-esp32/src/dispatch.rs +++ b/gem-remotes-lib/src/dispatch.rs @@ -1,3 +1,6 @@ +/// Acts as a queue multiplexer. Accepts messages from entities that have taken an endpoint, and +/// broadcasts them to any entity that has subscribed for them. + use std::mem::discriminant; use std::collections::HashMap; @@ -8,15 +11,15 @@ use strum::EnumCount; use crate::commands::{Commands, CmdType}; use log::*; //{trace, debug, info, warn, error} -pub type SendQ = Sender; -pub type RecvQ = Receiver; +pub type DispatchSendQ = Sender; +pub type DispatchRecvQ = Receiver; //TODO: Making this generic over a for commands would make it a useful small event handler. pub struct Dispatch { - callbacks: HashMap >, - recv: RecvQ, // Channel to listen to incomming commands - endpoint: SendQ, // Endpoint to clone to hand to other modules, so that they can send commands + callbacks: HashMap >, + recv: DispatchRecvQ, // Channel to listen to incomming commands + endpoint: DispatchSendQ, // Endpoint to clone to hand to other modules, so that they can send commands } impl Dispatch { @@ -28,7 +31,7 @@ impl Dispatch { } /// Get a channel receiver that will get callbacks for all commands in the listen_for vec. - pub fn get_callback_channel(&mut self, listen_for: &Vec) -> RecvQ { + pub fn get_callback_channel(&mut self, listen_for: &Vec) -> DispatchRecvQ { let (send, rec) = unbounded(); // TODO: these could be bounded instead, as these calls are all non-blocking. for cmd in listen_for { let callback_list = self.callbacks.get_mut(&discriminant(&cmd)); @@ -49,7 +52,7 @@ impl Dispatch { } /// Get a channel sender that will send commands to this dispatcher - pub fn get_cmd_channel(&self) -> SendQ { + pub fn get_cmd_channel(&self) -> DispatchSendQ { self.endpoint.clone() } diff --git a/gem-remotes-lib/src/lib.rs b/gem-remotes-lib/src/lib.rs new file mode 100644 index 0000000..a7c1849 --- /dev/null +++ b/gem-remotes-lib/src/lib.rs @@ -0,0 +1,45 @@ + +/// Business logic (independent of hardware) for Gem Remotes ESP32 controller + +// Modules in this crate +pub mod commands; +pub mod dispatch; +pub mod motor_controller; + +// Re-published items +pub use commands::{ + Button, + Commands +}; +pub use motor_controller::{ + AutoMode, + Controller, + LimitState, + MotorCommands, + MotorRecvQ, + MotorSendQ, +}; +pub use dispatch::{ + Dispatch, + DispatchSendQ, + DispatchRecvQ, +}; + + + +// Test Code for whole module + +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/gem-remotes-esp32/src/motor_controller.rs b/gem-remotes-lib/src/motor_controller.rs similarity index 96% rename from gem-remotes-esp32/src/motor_controller.rs rename to gem-remotes-lib/src/motor_controller.rs index e81bcd7..d053f46 100644 --- a/gem-remotes-esp32/src/motor_controller.rs +++ b/gem-remotes-lib/src/motor_controller.rs @@ -2,14 +2,12 @@ /// command messages. use log::*; //{trace, debug, info, warn, error} -//use async_channel::Receiver; use anyhow::Result; - +use async_channel::{Receiver, Sender}; use crate::commands::{Commands, Button}; -use crate::motor_driver::Commands as MotorCommands; -use crate::motor_driver::SendQ as MotorSendQ; -use crate::dispatch::{Dispatch, RecvQ, SendQ}; +use crate::dispatch::{Dispatch, DispatchRecvQ, DispatchSendQ}; +// The main internal state of the controller, representing the current control method of the motors. #[derive(Clone, Copy, Debug, PartialEq)] enum ControllerStates { Stopped, @@ -18,9 +16,19 @@ enum ControllerStates { AutoUp, GoingDown, AutoDown, - //TODO: AutoUp and AutoDown } + +#[derive(Clone, Copy, Debug)] +pub enum MotorCommands { + StartUp, + StartDown, + Stop +} + +pub type MotorSendQ = Sender; +pub type MotorRecvQ = Receiver; + #[derive(Clone, Copy, Debug, PartialEq)] pub enum AutoMode { Disallowed = 0, @@ -37,15 +45,15 @@ pub enum LimitState { pub struct Controller { state: ControllerStates, - recv: RecvQ, - send: SendQ, + recv: DispatchRecvQ, + send: DispatchSendQ, motor_q: MotorSendQ, auto_mode: AutoMode, limit_state: LimitState, } impl Controller { - pub fn new(recv: RecvQ, send: SendQ, motor_q: MotorSendQ) -> Self { + pub fn new(recv: DispatchRecvQ, send: DispatchSendQ, motor_q: MotorSendQ) -> Self { Controller { state: ControllerStates::Stopping, recv: recv, @@ -58,7 +66,7 @@ impl Controller { /// Tell the message dispatch which messages we are interested in receiving, and get /// a callback channel that receives those messages. - pub fn prepare_controller(dp: &mut Dispatch) -> RecvQ { + pub fn prepare_controller(dp: &mut Dispatch) -> DispatchRecvQ { let cmds = vec![ Commands::PicRecvUp{data: Button::Released}, Commands::PicRecvDown{data: Button::Released},