diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 3b743c7..50b46e5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,11 @@ { "type": "process", "command": "cross", - "args": ["build", "--target","arm-unknown-linux-gnueabihf"], + "args": [ + "build", + "--target", + "arm-unknown-linux-gnueabihf" + ], "problemMatcher": [ "$rustc" ], @@ -12,4 +16,4 @@ "label": "rust: cargo build" } ] -} \ No newline at end of file +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index f0f7753..4d27990 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,21 @@ dependencies = [ "nb 1.1.0", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "async-trait" version = "0.1.80" @@ -38,6 +53,12 @@ dependencies = [ "syn", ] +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + [[package]] name = "axum" version = "0.7.5" @@ -46,6 +67,7 @@ checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", + "axum-macros", "bytes", "futures-util", "http", @@ -93,6 +115,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -114,6 +148,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytes" version = "1.6.0" @@ -138,6 +178,20 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.5", +] + [[package]] name = "clipboard-win" version = "5.3.1" @@ -147,6 +201,12 @@ dependencies = [ "error-code", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "embedded-hal" version = "0.2.7" @@ -272,6 +332,7 @@ version = "0.1.0" dependencies = [ "ads1x1x", "axum", + "chrono", "libc", "nb 1.1.0", "rppal", @@ -287,6 +348,12 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "home" version = "0.5.9" @@ -377,6 +444,29 @@ dependencies = [ "tokio", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -393,6 +483,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.153" @@ -485,6 +584,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.32.2" @@ -888,6 +996,69 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index c70e1fd..38c82ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,6 @@ nb = "1.1" rustyline = "14.0" serde = { "version" = "1.0", features = ["derive"] } toml = "0.8" -axum = "0.7" +axum = { "version" = "0.7", features = ["macros"] } tokio = { "version" = "1.37" } +chrono = "0.4.38" diff --git a/src/control.rs b/src/control.rs index 5f936ed..1143936 100644 --- a/src/control.rs +++ b/src/control.rs @@ -1,3 +1,64 @@ -use crate::state::ProgramStateShared; +use std::time::Duration; -pub fn control_thread(program_state: ProgramStateShared) {} +use chrono::{Local, Timelike}; +use tokio::join; + +use crate::{ + actuators, + error::{lock_err, GenericResult}, + sensors, + state::ProgramStateShared, +}; + +async fn temperature_control(program_state: ProgramStateShared) -> GenericResult<()> { + let mut program_state = program_state.lock().map_err(lock_err)?; + let current_temperature = sensors::get_temperature(&program_state.config)?; + if current_temperature > 28. { + actuators::switch_fan(crate::io::RelaySwitchState::On, &mut program_state)?; + } else { + actuators::switch_fan(crate::io::RelaySwitchState::Off, &mut program_state)?; + } + Ok(()) +} + +async fn temperature_control_loop(program_state: ProgramStateShared) { + loop { + let _ = temperature_control(program_state.clone()).await; + tokio::time::sleep(Duration::from_mins(1)).await; + } +} +async fn soil_moisture_control_loop(program_state: ProgramStateShared) { + loop { + tokio::time::sleep(Duration::from_days(1)).await; + } +} + +async fn light_control(program_state: ProgramStateShared) -> GenericResult<()> { + let program_state = program_state.clone(); + let mut program_state = program_state.lock().map_err(lock_err)?; + let local = Local::now(); + let hour = local.time().hour(); + const HOURS_ON: u32 = 24; + if hour <= HOURS_ON { + actuators::switch_lights(crate::io::RelaySwitchState::On, &mut program_state)?; + } else { + actuators::switch_lights(crate::io::RelaySwitchState::Off, &mut program_state)?; + } + + Ok(()) +} + +async fn light_control_loop(program_state: ProgramStateShared) { + loop { + let _ = light_control(program_state.clone()).await; + tokio::time::sleep(Duration::from_hours(1)).await; + } +} + +pub async fn control_thread(program_state: ProgramStateShared) { + join!( + light_control_loop(program_state.clone()), + temperature_control_loop(program_state.clone()), + soil_moisture_control_loop(program_state.clone()) + ); +} diff --git a/src/io.rs b/src/io.rs index 3ea7f25..7ff39b2 100644 --- a/src/io.rs +++ b/src/io.rs @@ -27,6 +27,7 @@ pub fn get_input_voltage(pin: u8) -> GenericResult { pub struct Relay { relay_pins: Vec>, } +#[derive(serde::Serialize, serde::Deserialize)] pub enum RelaySwitchState { On, Off, diff --git a/src/main.rs b/src/main.rs index e5dacfb..d53d72d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ +#![feature(duration_constructors)] #![allow(dead_code)] -use std::thread; - use cli_mode::run_cli; use config::Configuration; use server::run_server; @@ -32,16 +31,17 @@ async fn main() { let program_state = init_state(config).unwrap(); let program_state_clone = program_state.clone(); - let control_thread_handle = thread::spawn(move || control::control_thread(program_state_clone)); + let control_thread_handle = + tokio::spawn(async move { control::control_thread(program_state_clone).await }); let args = std::env::args().collect::>(); let mode = args.get(1).map(|x| x.as_str()); match mode { - Some("cli") => run_cli(program_state), - _ => run_server().await, + Some("cli") => run_cli(program_state.clone()), + _ => run_server(program_state.clone()).await, } - let _ = control_thread_handle.join(); + let _ = control_thread_handle.await; } diff --git a/src/server.rs b/src/server.rs index 6c77d09..864de83 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,13 +1,49 @@ -use axum::{response::Html, routing::get, Router}; +use axum::{ + extract::{Path, State}, + http::StatusCode, + response::{Html, IntoResponse}, + routing::get, + Router, +}; -pub async fn run_server() { - let app: Router = setup_router(); +use crate::{ + actuators, + error::{lock_err, GenericResult}, + io::RelaySwitchState, + state::ProgramStateShared, +}; + +pub async fn run_server(program_state: ProgramStateShared) { + let app: Router = setup_router(program_state); let listener = tokio::net::TcpListener::bind("0.0.0.0:2205").await.unwrap(); axum::serve(listener, app).await.unwrap(); } -fn setup_router() -> Router { - Router::new().route("/", get(handler)) +fn setup_router(program_state: ProgramStateShared) -> Router { + Router::new() + .route("/", get(handler)) + .route("/switch/:device/:state", get(switch_handler)) + .with_state(program_state) +} + +async fn switch_handler( + Path((device, state)): Path<(String, RelaySwitchState)>, + State(program_state): State, +) -> impl IntoResponse { + let exec = || -> GenericResult<()> { + let mut program_state = program_state.lock().map_err(lock_err)?; + match device.as_str() { + "lights" => actuators::switch_lights(state, &mut program_state)?, + "pump" => actuators::switch_water_pump(state, &mut program_state)?, + "fan" => actuators::switch_fan(state, &mut program_state)?, + _ => (), + } + Ok(()) + }; + match exec() { + Ok(_) => StatusCode::OK, + Err(_) => StatusCode::INTERNAL_SERVER_ERROR, + } } async fn handler() -> Html<&'static str> {