Nareshkumar Rao 7 months ago
parent
commit
a6b050cc64
  1. 22
      Cargo.lock
  2. 1
      Cargo.toml
  3. 2
      growpi.history.csv
  4. 1
      growpi.toml
  5. 10
      src/actuators.rs
  6. 2
      src/config.rs
  7. 3
      src/control/light.rs
  8. 70
      src/history.rs
  9. 1
      src/main.rs
  10. 1
      src/server.rs
  11. 10
      src/state.rs

22
Cargo.lock

@ -207,6 +207,27 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "csv"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "embedded-hal" name = "embedded-hal"
version = "0.2.7" version = "0.2.7"
@ -333,6 +354,7 @@ dependencies = [
"ads1x1x", "ads1x1x",
"axum", "axum",
"chrono", "chrono",
"csv",
"libc", "libc",
"nb 1.1.0", "nb 1.1.0",
"rppal", "rppal",

1
Cargo.toml

@ -15,3 +15,4 @@ axum = { "version" = "0.7", features = ["macros"] }
tokio = { "version" = "1.37" } tokio = { "version" = "1.37" }
chrono = "0.4" chrono = "0.4"
tower-http = { "version" = "0.5", features = ["cors"] } tower-http = { "version" = "0.5", features = ["cors"] }
csv = "1.3.0"

2
growpi.history.csv

@ -0,0 +1,2 @@
time,amount
1714687536,456
1 time amount
2 1714687536 456

1
growpi.toml

@ -33,4 +33,5 @@ grams_per_millisecond = 0.05280999839305878
temperature_set_point_upper = 35.0 temperature_set_point_upper = 35.0
temperature_set_point_lower = 28.0 temperature_set_point_lower = 28.0
temperature_loop_mins = 60 temperature_loop_mins = 60
sunlight_hours = 24
soil_loop_hours = 12 soil_loop_hours = 12

10
src/actuators.rs

@ -1,6 +1,8 @@
use std::{thread, time::Duration}; use std::{thread, time::Duration};
use crate::{error::GenericResult, io::RelaySwitchState, state::ProgramState};
use crate::{
error::GenericResult, history::WateringRecord, io::RelaySwitchState, state::ProgramState,
};
pub fn switch_lights( pub fn switch_lights(
state: RelaySwitchState, state: RelaySwitchState,
@ -50,5 +52,11 @@ pub fn pump_water(water_mass_g: u16, program_state: &mut ProgramState) -> Generi
switch_water_pump(RelaySwitchState::On, program_state)?; switch_water_pump(RelaySwitchState::On, program_state)?;
thread::sleep(duration); thread::sleep(duration);
switch_water_pump(RelaySwitchState::Off, program_state)?; switch_water_pump(RelaySwitchState::Off, program_state)?;
program_state
.history
.watering_records
.push(WateringRecord::new(water_mass_g.into()));
Ok(()) Ok(())
} }

2
src/config.rs

@ -49,6 +49,7 @@ pub struct ControllerSettings {
pub temperature_set_point_upper: f32, pub temperature_set_point_upper: f32,
pub temperature_set_point_lower: f32, pub temperature_set_point_lower: f32,
pub temperature_loop_mins: u64, pub temperature_loop_mins: u64,
pub sunlight_hours: u64,
pub soil_loop_hours: u64, pub soil_loop_hours: u64,
} }
@ -107,6 +108,7 @@ impl Default for Configuration {
temperature_set_point_lower: 28., temperature_set_point_lower: 28.,
temperature_loop_mins: 60, temperature_loop_mins: 60,
soil_loop_hours: 12, soil_loop_hours: 12,
sunlight_hours: 24,
}, },
} }
} }

3
src/control/light.rs

@ -13,8 +13,7 @@ fn light_control(program_state: ProgramStateShared) -> GenericResult<()> {
let mut program_state = program_state.lock().map_err(lock_err)?; let mut program_state = program_state.lock().map_err(lock_err)?;
let local = Local::now(); let local = Local::now();
let hour = local.time().hour(); let hour = local.time().hour();
const HOURS_ON: u32 = 24;
if hour <= HOURS_ON {
if hour as u64 <= program_state.config.controller_settings.sunlight_hours {
actuators::switch_lights(crate::io::RelaySwitchState::On, &mut program_state)?; actuators::switch_lights(crate::io::RelaySwitchState::On, &mut program_state)?;
} else { } else {
actuators::switch_lights(crate::io::RelaySwitchState::Off, &mut program_state)?; actuators::switch_lights(crate::io::RelaySwitchState::Off, &mut program_state)?;

70
src/history.rs

@ -0,0 +1,70 @@
use chrono::Utc;
use serde::{Deserialize, Serialize};
use crate::error::GenericResult;
#[derive(Serialize, Deserialize)]
pub struct WateringRecord {
pub time: i64,
pub amount: u64,
}
impl WateringRecord {
pub fn new(amount: u64) -> WateringRecord {
WateringRecord {
time: Utc::now().timestamp(),
amount,
}
}
}
#[derive(Default)]
pub struct History {
pub watering_records: Vec<WateringRecord>,
}
const FILE_PATH: &str = "./growpi.history.csv";
impl History {
pub fn save(&self) -> GenericResult<()> {
let mut writer = csv::WriterBuilder::new()
.has_headers(true)
.from_path(FILE_PATH)?;
for record in &self.watering_records {
writer.serialize(record)?;
}
writer.flush()?;
Ok(())
}
pub fn load() -> GenericResult<History> {
let mut history = csv::ReaderBuilder::new()
.has_headers(true)
.from_path(FILE_PATH)?;
let mut result = Vec::new();
for record in history.deserialize() {
result.push(record?);
}
Ok(History {
watering_records: result,
})
}
}
#[cfg(test)]
mod tests {
use chrono::Local;
use super::*;
#[test]
fn test_write_default() {
let mut history = History::default();
history.watering_records.push(WateringRecord {
time: Local::now().timestamp(),
amount: 456,
});
history.save().unwrap();
}
}

1
src/main.rs

@ -15,6 +15,7 @@ mod io;
mod sensors; mod sensors;
mod server; mod server;
mod state; mod state;
mod history;
fn load_config() -> config::Configuration { fn load_config() -> config::Configuration {
let config = Configuration::from_file(std::path::Path::new("./growpi.toml")); let config = Configuration::from_file(std::path::Path::new("./growpi.toml"));

1
src/server.rs

@ -58,7 +58,6 @@ async fn switch_handler(
let mut program_state = program_state.lock().map_err(lock_err)?; let mut program_state = program_state.lock().map_err(lock_err)?;
match device.as_str() { match device.as_str() {
"lights" => actuators::switch_lights(state, &mut program_state)?, "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)?, "fan" => actuators::switch_fan(state, &mut program_state)?,
_ => (), _ => (),
} }

10
src/state.rs

@ -1,14 +1,20 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::{config::Configuration, error::GenericResult, io};
use crate::{config::Configuration, error::GenericResult, history::History, io};
pub type ProgramStateShared = Arc<Mutex<ProgramState>>; pub type ProgramStateShared = Arc<Mutex<ProgramState>>;
pub struct ProgramState { pub struct ProgramState {
pub config: Configuration, pub config: Configuration,
pub relay: io::Relay, pub relay: io::Relay,
pub history: History,
} }
pub fn init_state(config: Configuration) -> GenericResult<ProgramStateShared> { pub fn init_state(config: Configuration) -> GenericResult<ProgramStateShared> {
let relay = io::Relay::new(&config)?; let relay = io::Relay::new(&config)?;
Ok(Arc::new(Mutex::new(ProgramState { config, relay })))
let history = History::load()?;
Ok(Arc::new(Mutex::new(ProgramState {
config,
relay,
history,
})))
} }

Loading…
Cancel
Save