Switch println! to log::info in water.rs; add dynamic deep-sleep adjustment based on plant state; update heap metrics and OTA UI.
This commit is contained in:
@@ -11,6 +11,7 @@ use esp_hal::pcnt::unit::Unit;
|
||||
use esp_hal::peripherals::GPIO5;
|
||||
use esp_hal::Async;
|
||||
use esp_println::println;
|
||||
use log::info;
|
||||
use onewire::{ds18b20, Device, DeviceSearch, OneWire, DS18B20};
|
||||
|
||||
unsafe impl Send for TankSensor<'_> {}
|
||||
@@ -90,9 +91,9 @@ impl<'a> TankSensor<'a> {
|
||||
let mut delay = Delay::new();
|
||||
|
||||
let presence = self.one_wire_bus.reset(&mut delay)?;
|
||||
println!("OneWire: reset presence pulse = {}", presence);
|
||||
info!("OneWire: reset presence pulse = {}", presence);
|
||||
if !presence {
|
||||
println!("OneWire: no device responded to reset — check pull-up resistor and wiring");
|
||||
info!("OneWire: no device responded to reset — check pull-up resistor and wiring");
|
||||
}
|
||||
|
||||
let mut search = DeviceSearch::new();
|
||||
@@ -100,7 +101,7 @@ impl<'a> TankSensor<'a> {
|
||||
let mut devices_found = 0u8;
|
||||
while let Some(device) = self.one_wire_bus.search_next(&mut search, &mut delay)? {
|
||||
devices_found += 1;
|
||||
println!(
|
||||
info!(
|
||||
"OneWire: found device #{} family=0x{:02X} addr={:02X?}",
|
||||
devices_found, device.address[0], device.address
|
||||
);
|
||||
@@ -108,16 +109,16 @@ impl<'a> TankSensor<'a> {
|
||||
water_temp_sensor = Some(device);
|
||||
break;
|
||||
} else {
|
||||
println!("OneWire: skipping device — not a DS18B20 (family 0x{:02X} != 0x{:02X})", device.address[0], ds18b20::FAMILY_CODE);
|
||||
info!("OneWire: skipping device — not a DS18B20 (family 0x{:02X} != 0x{:02X})", device.address[0], ds18b20::FAMILY_CODE);
|
||||
}
|
||||
}
|
||||
if devices_found == 0 {
|
||||
println!("OneWire: search found zero devices on the bus");
|
||||
info!("OneWire: search found zero devices on the bus");
|
||||
}
|
||||
|
||||
match water_temp_sensor {
|
||||
Some(device) => {
|
||||
println!("Found one wire device: {:?}", device);
|
||||
info!("Found one wire device: {:?}", device);
|
||||
let mut water_temp_sensor = DS18B20::new(device)?;
|
||||
|
||||
let water_temp: Result<f32, FatError> = loop {
|
||||
|
||||
@@ -641,18 +641,44 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
.get_esp()
|
||||
.mqtt_publish("/deepsleep", "low Volt 12h").await;
|
||||
12 * 60
|
||||
} else if is_day {
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.mqtt_publish("/deepsleep", "normal 20m").await;
|
||||
20
|
||||
} else {
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.mqtt_publish("/deepsleep", "night 1h").await;
|
||||
60
|
||||
let base_duration: u32 = if is_day { 20 } else { 60 };
|
||||
|
||||
// Shorten sleep if any plant has a pending action sooner than the base duration
|
||||
let min_plant_wakeup: Option<u32> = plantstate
|
||||
.iter()
|
||||
.zip(&board.board_hal.get_config().plants)
|
||||
.filter_map(|(state, conf)| {
|
||||
state.minutes_until_actionable(conf, &timezone_time)
|
||||
})
|
||||
.min();
|
||||
|
||||
let duration = min_plant_wakeup
|
||||
.map(|m| base_duration.min(m.max(1)))
|
||||
.unwrap_or(base_duration);
|
||||
|
||||
if duration < base_duration {
|
||||
let msg = format!("reduced {}m", duration);
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.mqtt_publish("/deepsleep", &msg)
|
||||
.await;
|
||||
} else if is_day {
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.mqtt_publish("/deepsleep", "normal 20m")
|
||||
.await;
|
||||
} else {
|
||||
let _ = board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.mqtt_publish("/deepsleep", "night 1h")
|
||||
.await;
|
||||
}
|
||||
|
||||
duration
|
||||
};
|
||||
|
||||
let _ = board
|
||||
@@ -1336,7 +1362,6 @@ async fn get_version(
|
||||
slot1_state: format!("{:?}", board.slot1_state),
|
||||
heap_total: heap.size,
|
||||
heap_used: heap.current_usage,
|
||||
heap_free: heap.size.saturating_sub(heap.current_usage),
|
||||
heap_max_used: heap.max_usage,
|
||||
}
|
||||
}
|
||||
@@ -1350,6 +1375,5 @@ struct VersionInfo {
|
||||
slot1_state: String,
|
||||
heap_total: usize,
|
||||
heap_used: usize,
|
||||
heap_free: usize,
|
||||
heap_max_used: usize,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::hal::Moistures;
|
||||
use crate::{config::PlantConfig, hal::HAL, in_time_range};
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use chrono::{DateTime, TimeDelta, Timelike, Utc};
|
||||
use chrono_tz::Tz;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -192,6 +192,67 @@ impl PlantState {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns how many minutes until this plant becomes actionable (could be watered),
|
||||
/// or `None` if no action is pending within any reasonable horizon.
|
||||
///
|
||||
/// Used to dynamically shorten deep-sleep so cooldowns are not prolonged.
|
||||
pub fn minutes_until_actionable(
|
||||
&self,
|
||||
plant_conf: &PlantConfig,
|
||||
current_time: &DateTime<Tz>,
|
||||
) -> Option<u32> {
|
||||
if matches!(plant_conf.mode, PlantWateringMode::Off) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut earliest: Option<u32> = None;
|
||||
|
||||
// When does the current cooldown expire?
|
||||
if self.pump_in_timeout(plant_conf, current_time) {
|
||||
if let Some(last_pump) = self.pump.previous_pump {
|
||||
if let Some(expiry) = last_pump
|
||||
.checked_add_signed(TimeDelta::minutes(plant_conf.pump_cooldown_min.into()))
|
||||
{
|
||||
let diff = expiry.signed_duration_since(current_time.with_timezone(&Utc));
|
||||
let mins = diff.num_minutes().max(0) as u32;
|
||||
earliest = Some(earliest.map_or(mins, |e| e.min(mins)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For moisture-based modes: when does the watering window open, if the plant is dry
|
||||
// but currently outside its time window (and not in cooldown)?
|
||||
if matches!(
|
||||
plant_conf.mode,
|
||||
PlantWateringMode::TargetMoisture | PlantWateringMode::MinMoisture
|
||||
) {
|
||||
let (moisture, _) = self.plant_moisture();
|
||||
if let Some(moisture) = moisture {
|
||||
if moisture < plant_conf.target_moisture
|
||||
&& !self.pump_in_timeout(plant_conf, current_time)
|
||||
&& !in_time_range(
|
||||
current_time,
|
||||
plant_conf.pump_hour_start,
|
||||
plant_conf.pump_hour_end,
|
||||
)
|
||||
{
|
||||
// in_time_range uses `hour > start`, so the window opens at start+1:00
|
||||
let cur_hour = current_time.hour() as u32;
|
||||
let cur_minute = current_time.minute() as u32;
|
||||
let open_at_hour = (plant_conf.pump_hour_start as u32 + 1) % 24;
|
||||
let mins = if open_at_hour > cur_hour {
|
||||
(open_at_hour - cur_hour) * 60 - cur_minute
|
||||
} else {
|
||||
(24 - cur_hour + open_at_hour) * 60 - cur_minute
|
||||
};
|
||||
earliest = Some(earliest.map_or(mins, |e| e.min(mins)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
earliest
|
||||
}
|
||||
|
||||
pub fn is_err(&self) -> bool {
|
||||
self.sensor_a.is_err().is_some() || self.sensor_b.is_err().is_some()
|
||||
}
|
||||
|
||||
@@ -185,7 +185,6 @@ export interface VersionInfo {
|
||||
slot1_state: string,
|
||||
heap_total: number,
|
||||
heap_used: number,
|
||||
heap_free: number,
|
||||
heap_max_used: number,
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="flexcontainer">
|
||||
<span class="otakey">Free:</span>
|
||||
<span class="otavalue" id="heap_free"></span>
|
||||
<span class="otakey">Minimum Free:</span>
|
||||
<span class="otavalue" id="heap_min_free"></span>
|
||||
</div>
|
||||
<div class="flexcontainer">
|
||||
<span class="otakey">Used:</span>
|
||||
|
||||
@@ -12,7 +12,7 @@ export class OTAView {
|
||||
readonly firmware_partition: HTMLDivElement;
|
||||
readonly firmware_state0: HTMLDivElement;
|
||||
readonly firmware_state1: HTMLDivElement;
|
||||
readonly heap_free: HTMLDivElement;
|
||||
readonly heap_min_free: HTMLDivElement;
|
||||
readonly heap_used: HTMLDivElement;
|
||||
readonly heap_total: HTMLDivElement;
|
||||
readonly heap_max_used: HTMLDivElement;
|
||||
@@ -28,7 +28,7 @@ export class OTAView {
|
||||
this.firmware_partition = document.getElementById("firmware_partition") as HTMLDivElement;
|
||||
this.firmware_state0 = document.getElementById("firmware_state0") as HTMLDivElement;
|
||||
this.firmware_state1 = document.getElementById("firmware_state1") as HTMLDivElement;
|
||||
this.heap_free = document.getElementById("heap_free") as HTMLDivElement;
|
||||
this.heap_min_free = document.getElementById("heap_min_free") as HTMLDivElement;
|
||||
this.heap_used = document.getElementById("heap_used") as HTMLDivElement;
|
||||
this.heap_total = document.getElementById("heap_total") as HTMLDivElement;
|
||||
this.heap_max_used = document.getElementById("heap_max_used") as HTMLDivElement;
|
||||
@@ -59,7 +59,7 @@ export class OTAView {
|
||||
this.firmware_partition.innerText = versionInfo.current;
|
||||
this.firmware_state0.innerText = versionInfo.slot0_state;
|
||||
this.firmware_state1.innerText = versionInfo.slot1_state;
|
||||
this.heap_free.innerText = fmtBytes(versionInfo.heap_free);
|
||||
this.heap_min_free.innerText = fmtBytes(versionInfo.heap_total - versionInfo.heap_max_used);
|
||||
this.heap_used.innerText = fmtBytes(versionInfo.heap_used);
|
||||
this.heap_total.innerText = fmtBytes(versionInfo.heap_total);
|
||||
this.heap_max_used.innerText = fmtBytes(versionInfo.heap_max_used);
|
||||
|
||||
Reference in New Issue
Block a user