feat: add fertilizer cooldown functionality with web UI, HAL integration, and configuration support
This commit is contained in:
@@ -130,6 +130,7 @@ pub struct PlantConfig {
|
||||
pub max_pump_current_ma: u16,
|
||||
pub ignore_current_error: bool,
|
||||
pub fertilizer_s: u16,
|
||||
pub fertilizer_cooldown_min: u16,
|
||||
}
|
||||
|
||||
impl Default for PlantConfig {
|
||||
@@ -152,6 +153,7 @@ impl Default for PlantConfig {
|
||||
max_pump_current_ma: 3000,
|
||||
ignore_current_error: true,
|
||||
fertilizer_s: 0,
|
||||
fertilizer_cooldown_min: 1440, // 1 day default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT];
|
||||
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
|
||||
static mut CONSECUTIVE_WATERING_PLANT: [u32; PLANT_COUNT] = [0; PLANT_COUNT];
|
||||
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
|
||||
static mut LAST_FERTILIZER_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT];
|
||||
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
|
||||
static mut LOW_VOLTAGE_DETECTED: i8 = 0;
|
||||
#[esp_hal::ram(unstable(rtc_fast), unstable(persistent))]
|
||||
static mut RESTART_TO_CONF: i8 = 0;
|
||||
@@ -342,6 +344,14 @@ impl Esp<'_> {
|
||||
LAST_WATERING_TIMESTAMP[plant] = time.timestamp_millis();
|
||||
}
|
||||
}
|
||||
pub(crate) fn last_fertilizer_time(&self, plant: usize) -> i64 {
|
||||
unsafe { LAST_FERTILIZER_TIMESTAMP[plant] }
|
||||
}
|
||||
pub(crate) fn store_last_fertilizer_time(&mut self, plant: usize, time: DateTime<Utc>) {
|
||||
unsafe {
|
||||
LAST_FERTILIZER_TIMESTAMP[plant] = time.timestamp_millis();
|
||||
}
|
||||
}
|
||||
pub(crate) fn set_low_voltage_in_cycle(&mut self) {
|
||||
unsafe {
|
||||
LOW_VOLTAGE_DETECTED = 1;
|
||||
|
||||
@@ -311,6 +311,10 @@ pub enum LogMessage {
|
||||
PumpOpenLoopCurrent,
|
||||
#[strum(serialize = "Pump Open current sensor required but did not work: ${number_a}")]
|
||||
PumpMissingSensorCurrent,
|
||||
#[strum(
|
||||
serialize = "Fertilizer applied for ${number_a}s on plant ${number_b} (last application ${txt_short} minutes ago)"
|
||||
)]
|
||||
FertilizerApplied,
|
||||
#[strum(serialize = "MPPT Current sensor could not be reached")]
|
||||
MPPTError,
|
||||
#[strum(
|
||||
|
||||
@@ -715,13 +715,33 @@ pub async fn do_secure_pump(
|
||||
let mut pump_time_ms: u32 = 0;
|
||||
|
||||
if !dry_run {
|
||||
// Run fertilizer pump first if configured
|
||||
// Run fertilizer pump first if configured and not in cooldown
|
||||
if plant_config.fertilizer_s > 0 {
|
||||
info!("Starting fertilizer pump for {} seconds", plant_config.fertilizer_s);
|
||||
board.board_hal.extra2(true).await?;
|
||||
Timer::after_millis(plant_config.fertilizer_s as u64 * 1000).await;
|
||||
board.board_hal.extra2(false).await?;
|
||||
info!("Fertilizer pump stopped");
|
||||
let current_time = board.board_hal.get_time().await;
|
||||
let last_fertilizer = board.board_hal.get_esp().last_fertilizer_time(plant_id);
|
||||
let elapsed_minutes = (current_time.timestamp() - last_fertilizer) / 60;
|
||||
|
||||
if elapsed_minutes >= plant_config.fertilizer_cooldown_min as i64 {
|
||||
info!("Starting fertilizer pump for {} seconds (last fertilizer was {} minutes ago)",
|
||||
plant_config.fertilizer_s, elapsed_minutes);
|
||||
log(
|
||||
LogMessage::FertilizerApplied,
|
||||
plant_config.fertilizer_s as u32,
|
||||
(plant_id + 1) as u32,
|
||||
&elapsed_minutes.to_string(),
|
||||
"",
|
||||
);
|
||||
board.board_hal.extra2(true).await?;
|
||||
Timer::after_millis(plant_config.fertilizer_s as u64 * 1000).await;
|
||||
board.board_hal.extra2(false).await?;
|
||||
info!("Fertilizer pump stopped");
|
||||
|
||||
// Store the current time as last fertilizer time
|
||||
board.board_hal.get_esp().store_last_fertilizer_time(plant_id, current_time);
|
||||
} else {
|
||||
let remaining_minutes = plant_config.fertilizer_cooldown_min as i64 - elapsed_minutes;
|
||||
info!("Skipping fertilizer (cooldown: {} minutes remaining)", remaining_minutes);
|
||||
}
|
||||
}
|
||||
|
||||
board.board_hal.get_tank_sensor()?.reset_flow_meter();
|
||||
|
||||
@@ -89,6 +89,8 @@ pub struct PlantState {
|
||||
pub sensor_a_firmware_build_minutes: Option<u32>,
|
||||
/// Last known firmware build timestamp for sensor B.
|
||||
pub sensor_b_firmware_build_minutes: Option<u32>,
|
||||
/// Last time fertilizer was applied (Unix timestamp in seconds).
|
||||
pub last_fertilizer_time: i64,
|
||||
}
|
||||
|
||||
fn map_range_moisture(
|
||||
@@ -162,6 +164,7 @@ impl PlantState {
|
||||
|
||||
let previous_pump = board.board_hal.get_esp().last_pump_time(plant_id);
|
||||
let consecutive_pump_count = board.board_hal.get_esp().consecutive_pump_count(plant_id);
|
||||
let last_fertilizer_time = board.board_hal.get_esp().last_fertilizer_time(plant_id);
|
||||
let (a_builds, b_builds) = board.board_hal.get_sensor_build_minutes();
|
||||
let state = Self {
|
||||
sensor_a,
|
||||
@@ -172,6 +175,7 @@ impl PlantState {
|
||||
},
|
||||
sensor_a_firmware_build_minutes: a_builds[plant_id],
|
||||
sensor_b_firmware_build_minutes: b_builds[plant_id],
|
||||
last_fertilizer_time,
|
||||
};
|
||||
if state.is_err() {
|
||||
let _ = board.board_hal.fault(plant_id, true).await;
|
||||
@@ -296,6 +300,7 @@ impl PlantState {
|
||||
},
|
||||
sensor_a_firmware_build_minutes: self.sensor_a_firmware_build_minutes,
|
||||
sensor_b_firmware_build_minutes: self.sensor_b_firmware_build_minutes,
|
||||
last_fertilizer_time: self.last_fertilizer_time
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,4 +333,6 @@ pub struct PlantInfo<'a> {
|
||||
sensor_a_firmware_build_minutes: Option<u32>,
|
||||
/// firmware build timestamp of sensor B (minutes since Unix epoch); None if unknown
|
||||
sensor_b_firmware_build_minutes: Option<u32>,
|
||||
/// last time when fertilizer was applied
|
||||
last_fertilizer_time: i64,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user