Merge branch 'develop' of ssh://git.mannheim.ccc.de:1337/C3MA/PlantCtrl into develop
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
use crate::hal::Box;
|
|
||||||
use crate::fat_error::{FatError, FatResult};
|
use crate::fat_error::{FatError, FatResult};
|
||||||
|
use crate::hal::Box;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use esp_hal::i2c::master::I2c;
|
use esp_hal::i2c::master::I2c;
|
||||||
use esp_hal::Blocking;
|
use esp_hal::Blocking;
|
||||||
use lib_bms_protocol::{BatteryState as bstate, BmsReadable, Config, FirmwareVersion, ProtocolVersion};
|
use lib_bms_protocol::{
|
||||||
|
BatteryState as bstate, BmsReadable, Config, FirmwareVersion, ProtocolVersion,
|
||||||
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
@@ -59,18 +61,17 @@ impl BatteryInteraction for NoBatteryMonitor {
|
|||||||
pub struct WchI2cSlave {}
|
pub struct WchI2cSlave {}
|
||||||
|
|
||||||
pub struct WCHI2CSlave<'a> {
|
pub struct WCHI2CSlave<'a> {
|
||||||
pub(crate) i2c: I2cDevice<'a, CriticalSectionRawMutex, I2c<'a, Blocking>>
|
pub(crate) i2c: I2cDevice<'a, CriticalSectionRawMutex, I2c<'a, Blocking>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl BatteryInteraction for WCHI2CSlave<'_> {
|
impl BatteryInteraction for WCHI2CSlave<'_> {
|
||||||
|
|
||||||
|
|
||||||
async fn get_state(&mut self) -> FatResult<BatteryState> {
|
async fn get_state(&mut self) -> FatResult<BatteryState> {
|
||||||
let state = bstate::read_from_i2c(&mut self.i2c)?;
|
let state = bstate::read_from_i2c(&mut self.i2c)?;
|
||||||
let config = Config::read_from_i2c(&mut self.i2c)?;
|
let config = Config::read_from_i2c(&mut self.i2c)?;
|
||||||
|
|
||||||
let state_of_charge = (state.remaining_capacity_mah * 100 / state.lifetime_capacity_mah) as u8;
|
let state_of_charge =
|
||||||
|
(state.remaining_capacity_mah * 100 / state.lifetime_capacity_mah) as u8;
|
||||||
let state_of_health = state.lifetime_capacity_mah / config.capacity_mah * 100;
|
let state_of_health = state.lifetime_capacity_mah / config.capacity_mah * 100;
|
||||||
|
|
||||||
Ok(BatteryState::Info(BatteryInfo {
|
Ok(BatteryState::Info(BatteryInfo {
|
||||||
@@ -95,4 +96,4 @@ impl BatteryInteraction for WCHI2CSlave<'_> {
|
|||||||
async fn reset(&mut self) -> FatResult<()> {
|
async fn reset(&mut self) -> FatResult<()> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,6 +186,8 @@ pub(crate) async fn create_v4(
|
|||||||
let pump_device = I2cDevice::new(I2C_DRIVER.get().await);
|
let pump_device = I2cDevice::new(I2C_DRIVER.get().await);
|
||||||
let mut pump_expander = Pca9535Immediate::new(pump_device, 32);
|
let mut pump_expander = Pca9535Immediate::new(pump_device, 32);
|
||||||
for pin in 0..8 {
|
for pin in 0..8 {
|
||||||
|
let _ = pump_expander.pin_set_low(GPIOBank::Bank0, pin);
|
||||||
|
let _ = pump_expander.pin_set_low(GPIOBank::Bank1, pin);
|
||||||
let _ = pump_expander.pin_into_output(GPIOBank::Bank0, pin);
|
let _ = pump_expander.pin_into_output(GPIOBank::Bank0, pin);
|
||||||
let _ = pump_expander.pin_into_output(GPIOBank::Bank1, pin);
|
let _ = pump_expander.pin_into_output(GPIOBank::Bank1, pin);
|
||||||
let _ = pump_expander.pin_set_low(GPIOBank::Bank0, pin);
|
let _ = pump_expander.pin_set_low(GPIOBank::Bank0, pin);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ use embassy_net::Stack;
|
|||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::mutex::{Mutex, MutexGuard};
|
use embassy_sync::mutex::{Mutex, MutexGuard};
|
||||||
use embassy_sync::once_lock::OnceLock;
|
use embassy_sync::once_lock::OnceLock;
|
||||||
use embassy_time::{Duration, Timer, WithTimeout};
|
use embassy_time::{Duration, Instant, Timer, WithTimeout};
|
||||||
use esp_hal::rom::ets_delay_us;
|
use esp_hal::rom::ets_delay_us;
|
||||||
use esp_hal::system::software_reset;
|
use esp_hal::system::software_reset;
|
||||||
use esp_hal_ota::OtaImgState;
|
use esp_hal_ota::OtaImgState;
|
||||||
@@ -642,17 +642,24 @@ pub async fn do_secure_pump(
|
|||||||
plant_config: &PlantConfig,
|
plant_config: &PlantConfig,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
) -> FatResult<PumpResult> {
|
) -> FatResult<PumpResult> {
|
||||||
let mut current_collector = vec![0_u16; plant_config.pump_time_s.into()];
|
const STARTUP_IGNORE_MS: u32 = 500;
|
||||||
let mut flow_collector = vec![0_i16; plant_config.pump_time_s.into()];
|
const STARTUP_ABORT_CURRENT_MA: u16 = 1500;
|
||||||
|
|
||||||
|
let steps_in_50ms = plant_config.pump_time_s as usize * 20;
|
||||||
|
|
||||||
|
let mut current_collector = vec![0_u16; steps_in_50ms];
|
||||||
|
let mut flow_collector = vec![0_i16; steps_in_50ms];
|
||||||
let mut error = false;
|
let mut error = false;
|
||||||
let mut first_error = true;
|
let mut first_error = true;
|
||||||
let mut pump_time_s = 0;
|
let mut pump_time_ms: u32 = 0;
|
||||||
|
|
||||||
if !dry_run {
|
if !dry_run {
|
||||||
board.board_hal.get_tank_sensor()?.reset_flow_meter();
|
board.board_hal.get_tank_sensor()?.reset_flow_meter();
|
||||||
board.board_hal.get_tank_sensor()?.start_flow_meter();
|
board.board_hal.get_tank_sensor()?.start_flow_meter();
|
||||||
board.board_hal.pump(plant_id, true).await?;
|
board.board_hal.pump(plant_id, true).await?;
|
||||||
Timer::after_millis(10).await;
|
|
||||||
for step in 0..plant_config.pump_time_s as usize {
|
for step in 0..steps_in_50ms {
|
||||||
|
let step_start = Instant::now();
|
||||||
let flow_value = board.board_hal.get_tank_sensor()?.get_flow_meter_value();
|
let flow_value = board.board_hal.get_tank_sensor()?.get_flow_meter_value();
|
||||||
flow_collector[step] = flow_value;
|
flow_collector[step] = flow_value;
|
||||||
let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
||||||
@@ -672,7 +679,20 @@ pub async fn do_secure_pump(
|
|||||||
let current_ma = current.as_milliamperes() as u16;
|
let current_ma = current.as_milliamperes() as u16;
|
||||||
current_collector[step] = current_ma;
|
current_collector[step] = current_ma;
|
||||||
let high_current = current_ma > plant_config.max_pump_current_ma;
|
let high_current = current_ma > plant_config.max_pump_current_ma;
|
||||||
if high_current && first_error {
|
if pump_time_ms < STARTUP_IGNORE_MS
|
||||||
|
&& high_current
|
||||||
|
&& current_ma > STARTUP_ABORT_CURRENT_MA
|
||||||
|
{
|
||||||
|
log(
|
||||||
|
LogMessage::PumpOverCurrent,
|
||||||
|
plant_id as u32 + 1,
|
||||||
|
current_ma as u32,
|
||||||
|
plant_config.max_pump_current_ma.to_string().as_str(),
|
||||||
|
step.to_string().as_str(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
error = true;
|
||||||
|
} else if high_current && first_error {
|
||||||
log(
|
log(
|
||||||
LogMessage::PumpOverCurrent,
|
LogMessage::PumpOverCurrent,
|
||||||
plant_id as u32 + 1,
|
plant_id as u32 + 1,
|
||||||
@@ -722,12 +742,27 @@ pub async fn do_secure_pump(
|
|||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
//e.g., v3 without a sensor ends here, do not spam
|
error!("Error getting pump current: {err}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timer::after_millis(1000).await;
|
|
||||||
pump_time_s += 1;
|
// Keep the loop period as close to 50ms (including sensor read + processing time) as possible.
|
||||||
|
// Always sleep at least 1ms to avoid a fully busy loop when we overrun.
|
||||||
|
let elapsed = step_start.elapsed();
|
||||||
|
let target = Duration::from_millis(50);
|
||||||
|
let sleep_time = match target.checked_sub(elapsed) {
|
||||||
|
Some(remaining) => {
|
||||||
|
if remaining < Duration::from_millis(1) {
|
||||||
|
Duration::from_millis(1)
|
||||||
|
} else {
|
||||||
|
remaining
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Duration::from_millis(1),
|
||||||
|
};
|
||||||
|
Timer::after(sleep_time).await;
|
||||||
|
pump_time_ms += 50;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
board.board_hal.get_tank_sensor()?.stop_flow_meter();
|
board.board_hal.get_tank_sensor()?.stop_flow_meter();
|
||||||
@@ -741,7 +776,7 @@ pub async fn do_secure_pump(
|
|||||||
min_current_ma: current_collector[0],
|
min_current_ma: current_collector[0],
|
||||||
flow_value_ml,
|
flow_value_ml,
|
||||||
flow_value_count: final_flow_value,
|
flow_value_count: final_flow_value,
|
||||||
pump_time_s,
|
pump_time_s: (pump_time_ms / 1000) as u16,
|
||||||
error,
|
error,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ use crate::webserver::get_json::{
|
|||||||
use crate::webserver::get_log::get_log;
|
use crate::webserver::get_log::get_log;
|
||||||
use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index};
|
use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index};
|
||||||
use crate::webserver::ota::ota_operations;
|
use crate::webserver::ota::ota_operations;
|
||||||
use crate::webserver::post_json::{board_test, can_power, detect_sensors, night_lamp_test, pump_test, set_config, wifi_scan, write_time};
|
use crate::webserver::post_json::{
|
||||||
|
board_test, can_power, detect_sensors, night_lamp_test, pump_test, set_config, wifi_scan,
|
||||||
|
write_time,
|
||||||
|
};
|
||||||
use crate::{bail, BOARD_ACCESS};
|
use crate::{bail, BOARD_ACCESS};
|
||||||
use alloc::borrow::ToOwned;
|
use alloc::borrow::ToOwned;
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
|
|||||||
Reference in New Issue
Block a user