5 Commits

Author SHA1 Message Date
9e3886bd8c move all mqtt publishing functions to mqtt module 2026-05-10 17:37:21 +02:00
efb72ca2f7 refctor: TankInfo structure (consistent layout)
- fix: use tagged enum serialization for TankError
- fix: rename TankInfo fields for consistent naming (volume_ml, pct, water_temp_c)
- renamed some fields for better clarity on contained value
2026-05-10 17:37:20 +02:00
296c12735d refactor: PlantInfo structure (consistent layout)
- fix: use tagged enum serialization for MoistureSensorError and PumpError
- fix: flatten PlantInfo sensors to SensorTelemetry with top-level moisture_pct
2026-05-10 17:37:18 +02:00
dc0bfcbbfd refactor: BatteryInfo structure (consistent layout)
- use tagged enum serialization for BatteryError
- flatten BatteryInfo telemetry with consistent field names and typed error
2026-05-10 17:37:17 +02:00
12d929d853 fix: serialize firmware/state as JSON instead of Debug format 2026-05-10 17:37:14 +02:00
2 changed files with 81 additions and 85 deletions

View File

@@ -26,7 +26,7 @@ use crate::{
config::BoardVersion::INITIAL, config::BoardVersion::INITIAL,
hal::{PlantHal, HAL, PLANT_COUNT}, hal::{PlantHal, HAL, PLANT_COUNT},
}; };
use ::log::{error, info, warn}; use ::log::{info, warn, error};
use alloc::borrow::ToOwned; use alloc::borrow::ToOwned;
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use alloc::sync::Arc; use alloc::sync::Arc;
@@ -122,6 +122,8 @@ pub struct PumpResult {
pump_time_s: u16, pump_time_s: u16,
} }
async fn safe_main(spawner: Spawner) -> FatResult<()> { async fn safe_main(spawner: Spawner) -> FatResult<()> {
info!("Startup Rust"); info!("Startup Rust");
@@ -195,15 +197,10 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
info!("No wifi configured, starting initial config mode"); info!("No wifi configured, starting initial config mode");
let esp = board.board_hal.get_esp(); let esp = board.board_hal.get_esp();
let ssid = esp let ssid = esp.load_config().await
.load_config()
.await
.map(|config| config.network.ap_ssid.to_string()) .map(|config| config.network.ap_ssid.to_string())
.unwrap_or_else(|_| String::from("PlantCtrl Emergency Mode")); .unwrap_or_else(|_| String::from("PlantCtrl Emergency Mode"));
let device = esp let device = esp.interface_ap.take().context("AP interface already taken")?;
.interface_ap
.take()
.context("AP interface already taken")?;
let stack = network::wifi_ap(ssid, device, &esp.controller, &mut esp.rng, spawner).await?; let stack = network::wifi_ap(ssid, device, &esp.controller, &mut esp.rng, spawner).await?;
let reboot_now = Arc::new(AtomicBool::new(false)); let reboot_now = Arc::new(AtomicBool::new(false));
@@ -220,7 +217,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
info!("No wifi configured"); info!("No wifi configured");
//the current sensors require this amount to stabilize, in the case of Wi-Fi this is already handled due to connect timings; //the current sensors require this amount to stabilize, in the case of Wi-Fi this is already handled due to connect timings;
Timer::after_millis(100).await; Timer::after_millis(100).await;
network::NetworkMode::OFFLINE network::NetworkMode::OFFLINE
}; };
if matches!(network_mode, network::NetworkMode::OFFLINE) && to_config { if matches!(network_mode, network::NetworkMode::OFFLINE) && to_config {
@@ -228,18 +225,14 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
let res = { let res = {
let esp = board.board_hal.get_esp(); let esp = board.board_hal.get_esp();
let ssid = esp let ssid = esp.load_config().await
.load_config()
.await
.map(|config| config.network.ap_ssid.to_string()) .map(|config| config.network.ap_ssid.to_string())
.unwrap_or_else(|_| String::from("PlantCtrl Emergency Mode")); .unwrap_or_else(|_| String::from("PlantCtrl Emergency Mode"));
let device = match esp.interface_ap.take() { let device = match esp.interface_ap.take() {
Some(d) => d, Some(d) => d,
None => { None => {
use crate::fat_error::FatError; use crate::fat_error::FatError;
return Err(FatError::String { return Err(FatError::String { error: "AP interface already taken".to_string() });
error: "AP interface already taken".to_string(),
});
} }
}; };
network::wifi_ap(ssid, device, &esp.controller, &mut esp.rng, spawner).await network::wifi_ap(ssid, device, &esp.controller, &mut esp.rng, spawner).await
@@ -309,7 +302,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
log(LogMessage::NormalRun, 0, 0, "", ""); log(LogMessage::NormalRun, 0, 0, "", "");
} }
let dry_run = false; let _dry_run = false;
let tank_state = determine_tank_state(&mut board).await; let tank_state = determine_tank_state(&mut board).await;
@@ -346,7 +339,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
} }
} }
let mut water_frozen = false; let mut _water_frozen = false;
let water_temp: FatResult<f32> = match board.board_hal.get_tank_sensor() { let water_temp: FatResult<f32> = match board.board_hal.get_tank_sensor() {
Ok(sensor) => sensor.water_temperature_c().await, Ok(sensor) => sensor.water_temperature_c().await,
Err(e) => Err(e), Err(e) => Err(e),
@@ -354,7 +347,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
if let Ok(res) = water_temp { if let Ok(res) = water_temp {
if res < WATER_FROZEN_THRESH { if res < WATER_FROZEN_THRESH {
water_frozen = true; _water_frozen = true;
} }
} }
info!("Water temp is {}", water_temp.as_ref().unwrap_or(&0.)); info!("Water temp is {}", water_temp.as_ref().unwrap_or(&0.));
@@ -374,70 +367,74 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
mqtt::publish_plant_states(&mut board, &timezone_time.clone(), &plantstate).await; mqtt::publish_plant_states(&mut board, &timezone_time.clone(), &plantstate).await;
let pump_required = plantstate // let pump_required = plantstate
.iter() // .iter()
.zip(&board.board_hal.get_config().plants) // .zip(&board.board_hal.get_config().plants)
.any(|(it, conf)| it.needs_to_be_watered(conf, &timezone_time)) // .any(|(it, conf)| it.needs_to_be_watered(conf, &timezone_time))
&& !water_frozen; // && !water_frozen;
if pump_required { // if pump_required {
log(LogMessage::EnableMain, dry_run as u32, 0, "", ""); // log(LogMessage::EnableMain, dry_run as u32, 0, "", "");
for (plant_id, (state, plant_config)) in plantstate // for (plant_id, (state, plant_config)) in plantstate
.iter() // .iter()
.zip(&board.board_hal.get_config().plants.clone()) // .zip(&board.board_hal.get_config().plants.clone())
.enumerate() // .enumerate()
{ // {
if state.needs_to_be_watered(plant_config, &timezone_time) { // if state.needs_to_be_watered(plant_config, &timezone_time) {
let pump_count = board.board_hal.get_esp().consecutive_pump_count(plant_id) + 1; // let pump_count = board.board_hal.get_esp().consecutive_pump_count(plant_id) + 1;
board // board
.board_hal // .board_hal
.get_esp() // .get_esp()
.store_consecutive_pump_count(plant_id, pump_count); // .store_consecutive_pump_count(plant_id, pump_count);
let pump_ineffective = pump_count > plant_config.max_consecutive_pump_count as u32; //
if pump_ineffective { // let pump_ineffective = pump_count > plant_config.max_consecutive_pump_count as u32;
log( // if pump_ineffective {
LogMessage::ConsecutivePumpCountLimit, // log(
pump_count, // LogMessage::ConsecutivePumpCountLimit,
plant_config.max_consecutive_pump_count as u32, // pump_count,
&(plant_id + 1).to_string(), // plant_config.max_consecutive_pump_count as u32,
"", // &(plant_id + 1).to_string(),
); // "",
board.board_hal.fault(plant_id, true).await?; // );
} // board.board_hal.fault(plant_id, true).await?;
log( // }
LogMessage::PumpPlant, // log(
(plant_id + 1) as u32, // LogMessage::PumpPlant,
plant_config.pump_time_s as u32, // (plant_id + 1) as u32,
&dry_run.to_string(), // plant_config.pump_time_s as u32,
"", // &dry_run.to_string(),
); // "",
board // );
.board_hal // board
.get_esp() // .board_hal
.store_last_pump_time(plant_id, cur); // .get_esp()
board.board_hal.get_esp().last_pump_time(plant_id); // .store_last_pump_time(plant_id, cur);
mqtt::pump_info(plant_id, true, pump_ineffective, 0, 0, 0, false).await; // board.board_hal.get_esp().last_pump_time(plant_id);
let result = do_secure_pump(&mut board, plant_id, plant_config, dry_run).await?; // //state.active = true;
board.board_hal.pump(plant_id, false).await?; //
mqtt::pump_info( // pump_info(plant_id, true, pump_ineffective, 0, 0, 0, false).await;
plant_id, //
false, // let result = do_secure_pump(plant_id, plant_config, dry_run).await?;
pump_ineffective, // board.board_hal.pump(plant_id, false).await?;
result.median_current_ma, // pump_info(
result.max_current_ma, // plant_id,
result.min_current_ma, // false,
result.error, // pump_ineffective,
) // result.median_current_ma,
.await; // result.max_current_ma,
} else if !state.pump_in_timeout(plant_config, &timezone_time) { // result.min_current_ma,
// plant does not need to be watered and is not in timeout // result.error,
// -> reset consecutive pump count // )
board // .await;
.board_hal // } else if !state.pump_in_timeout(plant_config, &timezone_time) {
.get_esp() // // plant does not need to be watered and is not in timeout
.store_consecutive_pump_count(plant_id, 0); // // -> reset consecutive pump count
} // board
} // .board_hal
} // .get_esp()
// .store_consecutive_pump_count(plant_id, 0);
// }
// }
// }
info!("state of charg"); info!("state of charg");
let is_day = board.board_hal.is_day(); let is_day = board.board_hal.is_day();
@@ -561,8 +558,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
if stay_alive { if stay_alive {
let reboot_now = Arc::new(AtomicBool::new(false)); let reboot_now = Arc::new(AtomicBool::new(false));
let _webserver = http_server(reboot_now.clone(), stack.take().unwrap());
spawner.spawn(http_server(reboot_now.clone(), stack.take().unwrap())?);
wait_infinity(board, WaitType::MqttConfig, reboot_now.clone(), UTC).await; wait_infinity(board, WaitType::MqttConfig, reboot_now.clone(), UTC).await;
} else { } else {
//TODO wait for all mqtt publishes? //TODO wait for all mqtt publishes?

View File

@@ -300,7 +300,7 @@ impl PlantState {
false false
}, },
cooldown: self.pump_in_timeout(plant_conf, current_time), cooldown: self.pump_in_timeout(plant_conf, current_time),
out_of_work_hour: !in_time_range( out_of_work_hour: in_time_range(
current_time, current_time,
plant_conf.pump_hour_start, plant_conf.pump_hour_start,
plant_conf.pump_hour_end, plant_conf.pump_hour_end,