add pump current configuration
This commit is contained in:
parent
577c38d026
commit
5d91daf23d
@ -118,6 +118,9 @@ pub struct PlantConfig {
|
|||||||
pub max_consecutive_pump_count: u8,
|
pub max_consecutive_pump_count: u8,
|
||||||
pub moisture_sensor_min_frequency: Option<f32>, // Optional min frequency
|
pub moisture_sensor_min_frequency: Option<f32>, // Optional min frequency
|
||||||
pub moisture_sensor_max_frequency: Option<f32>, // Optional max frequency
|
pub moisture_sensor_max_frequency: Option<f32>, // Optional max frequency
|
||||||
|
pub min_pump_current_ma: u16,
|
||||||
|
pub max_pump_current_ma: u16,
|
||||||
|
pub ignore_current_error: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PlantConfig {
|
impl Default for PlantConfig {
|
||||||
@ -134,6 +137,9 @@ impl Default for PlantConfig {
|
|||||||
max_consecutive_pump_count: 10,
|
max_consecutive_pump_count: 10,
|
||||||
moisture_sensor_min_frequency: None, // No override by default
|
moisture_sensor_min_frequency: None, // No override by default
|
||||||
moisture_sensor_max_frequency: None, // No override by default
|
moisture_sensor_max_frequency: None, // No override by default
|
||||||
|
min_pump_current_ma: 10,
|
||||||
|
max_pump_current_ma: 3000,
|
||||||
|
ignore_current_error: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,10 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
|
|||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pump_current(&mut self, _plant: usize) -> Result<Current> {
|
||||||
|
bail!("Please configure board revision")
|
||||||
|
}
|
||||||
|
|
||||||
fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> {
|
fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
@ -119,10 +123,6 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
|
|||||||
let _ = self.general_fault.set_state(enable.into());
|
let _ = self.general_fault.set_state(enable.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_pump(&mut self, _plant: usize) -> Result<()> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test(&mut self) -> Result<()> {
|
fn test(&mut self) -> Result<()> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
|
@ -99,11 +99,10 @@ pub trait BoardInteraction<'a> {
|
|||||||
//should be multsampled
|
//should be multsampled
|
||||||
fn light(&mut self, enable: bool) -> Result<()>;
|
fn light(&mut self, enable: bool) -> Result<()>;
|
||||||
fn pump(&mut self, plant: usize, enable: bool) -> Result<()>;
|
fn pump(&mut self, plant: usize, enable: bool) -> Result<()>;
|
||||||
|
fn pump_current(&mut self, plant: usize) -> Result<Current>;
|
||||||
fn fault(&mut self, plant: usize, enable: bool) -> Result<()>;
|
fn fault(&mut self, plant: usize, enable: bool) -> Result<()>;
|
||||||
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>;
|
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>;
|
||||||
fn general_fault(&mut self, enable: bool);
|
fn general_fault(&mut self, enable: bool);
|
||||||
|
|
||||||
fn test_pump(&mut self, plant: usize) -> Result<()>;
|
|
||||||
fn test(&mut self) -> Result<()>;
|
fn test(&mut self) -> Result<()>;
|
||||||
fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>;
|
fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>;
|
||||||
fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage>;
|
fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage>;
|
||||||
|
@ -14,7 +14,7 @@ use esp_idf_hal::{
|
|||||||
gpio::{AnyInputPin, IOPin, InputOutput, PinDriver, Pull},
|
gpio::{AnyInputPin, IOPin, InputOutput, PinDriver, Pull},
|
||||||
pcnt::{PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex},
|
pcnt::{PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex},
|
||||||
};
|
};
|
||||||
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay};
|
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en};
|
||||||
use measurements::{Current, Voltage};
|
use measurements::{Current, Voltage};
|
||||||
use plant_ctrl2::sipo::ShiftRegister40;
|
use plant_ctrl2::sipo::ShiftRegister40;
|
||||||
use std::result::Result::Ok as OkStd;
|
use std::result::Result::Ok as OkStd;
|
||||||
@ -252,6 +252,10 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pump_current(&mut self, _plant: usize) -> Result<Current> {
|
||||||
|
bail!("Not implemented in v3")
|
||||||
|
}
|
||||||
|
|
||||||
fn fault(&mut self, plant: usize, enable: bool) -> Result<()> {
|
fn fault(&mut self, plant: usize, enable: bool) -> Result<()> {
|
||||||
let index = match plant {
|
let index = match plant {
|
||||||
0 => FAULT_1,
|
0 => FAULT_1,
|
||||||
@ -365,13 +369,6 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
|
|||||||
unsafe { gpio_hold_en(self.general_fault.pin()) };
|
unsafe { gpio_hold_en(self.general_fault.pin()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_pump(&mut self, plant: usize) -> Result<()> {
|
|
||||||
self.pump(plant, true)?;
|
|
||||||
unsafe { vTaskDelay(30000) };
|
|
||||||
self.pump(plant, false)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test(&mut self) -> Result<()> {
|
fn test(&mut self) -> Result<()> {
|
||||||
self.general_fault(true);
|
self.general_fault(true);
|
||||||
self.esp.delay.delay_ms(100);
|
self.esp.delay.delay_ms(100);
|
||||||
|
@ -13,7 +13,7 @@ use embedded_hal::digital::OutputPin;
|
|||||||
use embedded_hal_bus::i2c::MutexDevice;
|
use embedded_hal_bus::i2c::MutexDevice;
|
||||||
use esp_idf_hal::delay::Delay;
|
use esp_idf_hal::delay::Delay;
|
||||||
use esp_idf_hal::gpio::{AnyInputPin, IOPin, InputOutput, Output, PinDriver, Pull};
|
use esp_idf_hal::gpio::{AnyInputPin, IOPin, InputOutput, Output, PinDriver, Pull};
|
||||||
use esp_idf_hal::i2c::{I2cDriver, I2cError};
|
use esp_idf_hal::i2c::I2cDriver;
|
||||||
use esp_idf_hal::pcnt::{
|
use esp_idf_hal::pcnt::{
|
||||||
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
||||||
};
|
};
|
||||||
@ -21,7 +21,6 @@ use esp_idf_sys::{gpio_hold_dis, gpio_hold_en};
|
|||||||
use ina219::address::{Address, Pin};
|
use ina219::address::{Address, Pin};
|
||||||
use ina219::calibration::UnCalibrated;
|
use ina219::calibration::UnCalibrated;
|
||||||
use ina219::configuration::{Configuration, OperatingMode};
|
use ina219::configuration::{Configuration, OperatingMode};
|
||||||
use ina219::errors::InitializationError;
|
|
||||||
use ina219::SyncIna219;
|
use ina219::SyncIna219;
|
||||||
use measurements::{Current, Resistance, Voltage};
|
use measurements::{Current, Resistance, Voltage};
|
||||||
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
||||||
@ -125,6 +124,7 @@ pub struct V4<'a> {
|
|||||||
light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||||
general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||||
pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
|
pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
|
||||||
|
pump_ina: Option<SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>>,
|
||||||
sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
|
sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
|
||||||
extra1: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
|
extra1: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
|
||||||
extra2: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
|
extra2: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
|
||||||
@ -208,7 +208,7 @@ pub(crate) fn create_v4(
|
|||||||
let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
|
let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mppt_ina = SyncIna219::new(
|
let mppt_ina = SyncIna219::new(
|
||||||
MutexDevice::new(&I2C_DRIVER),
|
MutexDevice::new(&I2C_DRIVER),
|
||||||
Address::from_pins(Pin::Vcc, Pin::Gnd),
|
Address::from_pins(Pin::Vcc, Pin::Gnd),
|
||||||
);
|
);
|
||||||
@ -224,15 +224,6 @@ pub(crate) fn create_v4(
|
|||||||
operating_mode: Default::default(),
|
operating_mode: Default::default(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
//TODO this is probably already done until we are ready first time?, maybe add startup time comparison on access?
|
|
||||||
esp.delay.delay_ms(
|
|
||||||
mppt_ina
|
|
||||||
.configuration()?
|
|
||||||
.conversion_time()
|
|
||||||
.unwrap()
|
|
||||||
.as_millis() as u32,
|
|
||||||
);
|
|
||||||
|
|
||||||
Charger::SolarMpptV1 {
|
Charger::SolarMpptV1 {
|
||||||
mppt_ina,
|
mppt_ina,
|
||||||
solar_is_day,
|
solar_is_day,
|
||||||
@ -242,6 +233,17 @@ pub(crate) fn create_v4(
|
|||||||
Err(_) => Charger::ErrorInit {},
|
Err(_) => Charger::ErrorInit {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pump_ina = match SyncIna219::new(
|
||||||
|
MutexDevice::new(&I2C_DRIVER),
|
||||||
|
Address::from_pins(Pin::Gnd, Pin::Sda),
|
||||||
|
) {
|
||||||
|
Ok(pump_ina) => Some(pump_ina),
|
||||||
|
Err(err) => {
|
||||||
|
println!("Error creating pump ina: {:?}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let v = V4 {
|
let v = V4 {
|
||||||
rtc_module,
|
rtc_module,
|
||||||
esp,
|
esp,
|
||||||
@ -250,6 +252,7 @@ pub(crate) fn create_v4(
|
|||||||
signal_counter,
|
signal_counter,
|
||||||
light,
|
light,
|
||||||
general_fault,
|
general_fault,
|
||||||
|
pump_ina,
|
||||||
pump_expander,
|
pump_expander,
|
||||||
sensor_expander,
|
sensor_expander,
|
||||||
config,
|
config,
|
||||||
@ -314,6 +317,24 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pump_current(&mut self, _plant: usize) -> anyhow::Result<Current> {
|
||||||
|
//sensore is shared for all pumps, ignore plant id
|
||||||
|
match self.pump_ina.as_mut() {
|
||||||
|
None => {
|
||||||
|
bail!("pump current sensor not available");
|
||||||
|
}
|
||||||
|
Some(pump_ina) => {
|
||||||
|
let v = pump_ina.shunt_voltage().map(|v| {
|
||||||
|
let shunt_voltage = Voltage::from_microvolts(v.shunt_voltage_uv().abs() as f64);
|
||||||
|
let shut_value = Resistance::from_ohms(0.05_f64);
|
||||||
|
let current = shunt_voltage.as_volts() / shut_value.as_ohms();
|
||||||
|
Current::from_amperes(current)
|
||||||
|
})?;
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn fault(&mut self, plant: usize, enable: bool) -> anyhow::Result<()> {
|
fn fault(&mut self, plant: usize, enable: bool) -> anyhow::Result<()> {
|
||||||
if enable {
|
if enable {
|
||||||
self.pump_expander
|
self.pump_expander
|
||||||
@ -406,13 +427,6 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
unsafe { gpio_hold_en(self.general_fault.pin()) };
|
unsafe { gpio_hold_en(self.general_fault.pin()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_pump(&mut self, plant: usize) -> anyhow::Result<()> {
|
|
||||||
self.pump(plant, true)?;
|
|
||||||
self.esp.delay.delay_ms(30000);
|
|
||||||
self.pump(plant, false)?;
|
|
||||||
anyhow::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test(&mut self) -> anyhow::Result<()> {
|
fn test(&mut self) -> anyhow::Result<()> {
|
||||||
self.general_fault(true);
|
self.general_fault(true);
|
||||||
self.esp.delay.delay_ms(100);
|
self.esp.delay.delay_ms(100);
|
||||||
|
@ -93,6 +93,7 @@ pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &st
|
|||||||
let serial_entry = template.fill_in(&values);
|
let serial_entry = template.fill_in(&values);
|
||||||
|
|
||||||
println!("{serial_entry}");
|
println!("{serial_entry}");
|
||||||
|
//TODO push to mqtt?
|
||||||
|
|
||||||
let entry = LogEntry {
|
let entry = LogEntry {
|
||||||
timestamp: time,
|
timestamp: time,
|
||||||
@ -193,6 +194,16 @@ pub enum LogMessage {
|
|||||||
serialize = "Pumped multiple times, but plant is still to try attempt: ${number_a} limit :: ${number_b} plant: ${txt_short}"
|
serialize = "Pumped multiple times, but plant is still to try attempt: ${number_a} limit :: ${number_b} plant: ${txt_short}"
|
||||||
)]
|
)]
|
||||||
ConsecutivePumpCountLimit,
|
ConsecutivePumpCountLimit,
|
||||||
|
#[strum(
|
||||||
|
serialize = "Pump Overcurrent error, pump: ${number_a} tripped overcurrent ${number_b} limit was ${txt_short} @s ${txt_long}"
|
||||||
|
)]
|
||||||
|
PumpOverCurrent,
|
||||||
|
#[strum(
|
||||||
|
serialize = "Pump Open loop error, pump: ${number_a} is low, ${number_b} limit was ${txt_short} @s ${txt_long}"
|
||||||
|
)]
|
||||||
|
PumpOpenLoopCurrent,
|
||||||
|
#[strum(serialize = "Pump Open current sensor required but did not work: ${number_a}")]
|
||||||
|
PumpMissingSensorCurrent,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
125
rust/src/main.rs
125
rust/src/main.rs
@ -1,3 +1,4 @@
|
|||||||
|
use crate::config::PlantConfig;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::BoardVersion::INITIAL,
|
config::BoardVersion::INITIAL,
|
||||||
hal::{PlantHal, HAL, PLANT_COUNT},
|
hal::{PlantHal, HAL, PLANT_COUNT},
|
||||||
@ -76,6 +77,17 @@ struct LightState {
|
|||||||
struct PumpInfo {
|
struct PumpInfo {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
pump_ineffective: bool,
|
pump_ineffective: bool,
|
||||||
|
median_current_ma: u16,
|
||||||
|
max_current_ma: u16,
|
||||||
|
min_current_ma: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PumpResult {
|
||||||
|
median_current_ma: u16,
|
||||||
|
max_current_ma: u16,
|
||||||
|
min_current_ma: u16,
|
||||||
|
error: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
@ -225,6 +237,8 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
try_connect_wifi_sntp_mqtt(&mut board)
|
try_connect_wifi_sntp_mqtt(&mut board)
|
||||||
} else {
|
} else {
|
||||||
println!("No wifi configured");
|
println!("No wifi configured");
|
||||||
|
//the current sensors require this amount to stabilize, in case of wifi this is already handles for sure;
|
||||||
|
board.board_hal.get_esp().delay.delay_ms(100);
|
||||||
NetworkMode::OFFLINE
|
NetworkMode::OFFLINE
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -395,14 +409,20 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
board.board_hal.get_esp().last_pump_time(plant_id);
|
board.board_hal.get_esp().last_pump_time(plant_id);
|
||||||
//state.active = true;
|
//state.active = true;
|
||||||
|
|
||||||
pump_info(&mut board, plant_id, true, pump_ineffective);
|
pump_info(&mut board, plant_id, true, pump_ineffective, 0, 0, 0, false);
|
||||||
|
|
||||||
if !dry_run {
|
let result = do_secure_pump(&mut board, plant_id, plant_config, dry_run)?;
|
||||||
board.board_hal.pump(plant_id, true)?;
|
|
||||||
Delay::new_default().delay_ms(1000 * plant_config.pump_time_s as u32);
|
|
||||||
board.board_hal.pump(plant_id, false)?;
|
board.board_hal.pump(plant_id, false)?;
|
||||||
}
|
pump_info(
|
||||||
pump_info(&mut board, plant_id, false, pump_ineffective);
|
&mut board,
|
||||||
|
plant_id,
|
||||||
|
false,
|
||||||
|
pump_ineffective,
|
||||||
|
result.median_current_ma,
|
||||||
|
result.max_current_ma,
|
||||||
|
result.min_current_ma,
|
||||||
|
result.error,
|
||||||
|
);
|
||||||
} else if !state.pump_in_timeout(plant_config, &timezone_time) {
|
} else if !state.pump_in_timeout(plant_config, &timezone_time) {
|
||||||
// plant does not need to be watered and is not in timeout
|
// plant does not need to be watered and is not in timeout
|
||||||
// -> reset consecutive pump count
|
// -> reset consecutive pump count
|
||||||
@ -556,6 +576,92 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
.deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64);
|
.deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_secure_pump(
|
||||||
|
board: &mut MutexGuard<HAL>,
|
||||||
|
plant_id: usize,
|
||||||
|
plant_config: &PlantConfig,
|
||||||
|
dry_run: bool,
|
||||||
|
) -> anyhow::Result<PumpResult> {
|
||||||
|
let mut current_collector = vec![0_u16; plant_config.pump_time_s.into()];
|
||||||
|
let mut error = false;
|
||||||
|
let mut first_error = true;
|
||||||
|
if !dry_run {
|
||||||
|
board.board_hal.pump(plant_id, true)?;
|
||||||
|
Delay::new_default().delay_ms(2);
|
||||||
|
for step in 0..plant_config.pump_time_s as usize {
|
||||||
|
let current = board.board_hal.pump_current(plant_id);
|
||||||
|
match current {
|
||||||
|
Ok(current) => {
|
||||||
|
let current_ma = current.as_milliamperes() as u16;
|
||||||
|
current_collector[step] = current_ma;
|
||||||
|
let high_current = current_ma > plant_config.max_pump_current_ma;
|
||||||
|
if high_current {
|
||||||
|
if first_error {
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
board.board_hal.general_fault(true);
|
||||||
|
board.board_hal.fault(plant_id, true)?;
|
||||||
|
if !plant_config.ignore_current_error {
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
first_error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let low_current = current_ma < plant_config.min_pump_current_ma;
|
||||||
|
if low_current {
|
||||||
|
if first_error {
|
||||||
|
log(
|
||||||
|
LogMessage::PumpOpenLoopCurrent,
|
||||||
|
plant_id as u32 + 1,
|
||||||
|
current_ma as u32,
|
||||||
|
plant_config.min_pump_current_ma.to_string().as_str(),
|
||||||
|
step.to_string().as_str(),
|
||||||
|
);
|
||||||
|
board.board_hal.general_fault(true);
|
||||||
|
board.board_hal.fault(plant_id, true)?;
|
||||||
|
if !plant_config.ignore_current_error {
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
first_error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
if !plant_config.ignore_current_error {
|
||||||
|
println!("Error getting pump current: {}", err);
|
||||||
|
log(
|
||||||
|
LogMessage::PumpMissingSensorCurrent,
|
||||||
|
plant_id as u32,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
//eg v3 without a sensor ends here, do not spam
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Delay::new_default().delay_ms(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_collector.sort();
|
||||||
|
Ok(PumpResult {
|
||||||
|
median_current_ma: current_collector[current_collector.len() / 2],
|
||||||
|
max_current_ma: current_collector[current_collector.len() - 1],
|
||||||
|
min_current_ma: current_collector[0],
|
||||||
|
error,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn update_charge_indicator(board: &mut MutexGuard<HAL>) {
|
fn update_charge_indicator(board: &mut MutexGuard<HAL>) {
|
||||||
//we have mppt controller, ask it for charging current
|
//we have mppt controller, ask it for charging current
|
||||||
if let Ok(current) = board.board_hal.get_mptt_current() {
|
if let Ok(current) = board.board_hal.get_mptt_current() {
|
||||||
@ -711,10 +817,17 @@ fn pump_info(
|
|||||||
plant_id: usize,
|
plant_id: usize,
|
||||||
pump_active: bool,
|
pump_active: bool,
|
||||||
pump_ineffective: bool,
|
pump_ineffective: bool,
|
||||||
|
median_current_ma: u16,
|
||||||
|
max_current_ma: u16,
|
||||||
|
min_current_ma: u16,
|
||||||
|
error: bool,
|
||||||
) {
|
) {
|
||||||
let pump_info = PumpInfo {
|
let pump_info = PumpInfo {
|
||||||
enabled: pump_active,
|
enabled: pump_active,
|
||||||
pump_ineffective,
|
pump_ineffective,
|
||||||
|
median_current_ma: median_current_ma,
|
||||||
|
max_current_ma: max_current_ma,
|
||||||
|
min_current_ma: min_current_ma,
|
||||||
};
|
};
|
||||||
let pump_topic = format!("/pump{}", plant_id + 1);
|
let pump_topic = format!("/pump{}", plant_id + 1);
|
||||||
match serde_json::to_string(&pump_info) {
|
match serde_json::to_string(&pump_info) {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::PlantControllerConfig,
|
config::PlantControllerConfig,
|
||||||
determine_tank_state, get_version,
|
determine_tank_state, do_secure_pump, get_version,
|
||||||
hal::PLANT_COUNT,
|
hal::PLANT_COUNT,
|
||||||
log::LogMessage,
|
log::LogMessage,
|
||||||
plant_state::{MoistureSensorState, PlantState},
|
plant_state::{MoistureSensorState, PlantState},
|
||||||
@ -222,7 +222,7 @@ fn backup_info(
|
|||||||
fn set_config(
|
fn set_config(
|
||||||
request: &mut Request<&mut EspHttpConnection>,
|
request: &mut Request<&mut EspHttpConnection>,
|
||||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||||
let all = read_up_to_bytes_from_request(request, Some(3072))?;
|
let all = read_up_to_bytes_from_request(request, Some(4096))?;
|
||||||
let config: PlantControllerConfig = serde_json::from_slice(&all)?;
|
let config: PlantControllerConfig = serde_json::from_slice(&all)?;
|
||||||
|
|
||||||
let mut board = BOARD_ACCESS.lock().expect("board access");
|
let mut board = BOARD_ACCESS.lock().expect("board access");
|
||||||
@ -275,8 +275,11 @@ fn pump_test(
|
|||||||
let actual_data = read_up_to_bytes_from_request(request, None)?;
|
let actual_data = read_up_to_bytes_from_request(request, None)?;
|
||||||
let pump_test: TestPump = serde_json::from_slice(&actual_data)?;
|
let pump_test: TestPump = serde_json::from_slice(&actual_data)?;
|
||||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||||
board.board_hal.test_pump(pump_test.pump)?;
|
|
||||||
anyhow::Ok(None)
|
let config = &board.board_hal.get_config().plants[pump_test.pump].clone();
|
||||||
|
let pump_result = do_secure_pump(&mut board, pump_test.pump, config, false)?;
|
||||||
|
board.board_hal.pump(pump_test.pump, false)?;
|
||||||
|
anyhow::Ok(Some(serde_json::to_string(&pump_result)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tank_info(
|
fn tank_info(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export interface LogArray extends Array<LogEntry>{}
|
export interface LogArray extends Array<LogEntry> {
|
||||||
|
}
|
||||||
|
|
||||||
export interface LogEntry {
|
export interface LogEntry {
|
||||||
timestamp: string,
|
timestamp: string,
|
||||||
@ -9,7 +10,8 @@ export interface LogEntry {
|
|||||||
txt_long: string
|
txt_long: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LogLocalisation extends Array<LogLocalisationEntry>{}
|
export interface LogLocalisation extends Array<LogLocalisationEntry> {
|
||||||
|
}
|
||||||
|
|
||||||
export interface LogLocalisationEntry {
|
export interface LogLocalisationEntry {
|
||||||
msg_type: string,
|
msg_type: string,
|
||||||
@ -77,6 +79,7 @@ export enum BatteryBoardVersion {
|
|||||||
BQ34Z100G1 = "BQ34Z100G1",
|
BQ34Z100G1 = "BQ34Z100G1",
|
||||||
WchI2cSlave = "WchI2cSlave"
|
WchI2cSlave = "WchI2cSlave"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BoardVersion {
|
export enum BoardVersion {
|
||||||
INITIAL = "INITIAL",
|
INITIAL = "INITIAL",
|
||||||
V3 = "V3",
|
V3 = "V3",
|
||||||
@ -110,9 +113,17 @@ export interface PlantConfig {
|
|||||||
max_consecutive_pump_count: number,
|
max_consecutive_pump_count: number,
|
||||||
moisture_sensor_min_frequency: number | null;
|
moisture_sensor_min_frequency: number | null;
|
||||||
moisture_sensor_max_frequency: number | null;
|
moisture_sensor_max_frequency: number | null;
|
||||||
|
min_pump_current_ma: number,
|
||||||
|
max_pump_current_ma: number,
|
||||||
|
ignore_current_error: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PumpTestResult {
|
||||||
|
median_current_ma: number,
|
||||||
|
max_current_ma: number,
|
||||||
|
min_current_ma: number,
|
||||||
|
error: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
export interface SSIDList {
|
export interface SSIDList {
|
||||||
ssids: [string]
|
ssids: [string]
|
||||||
|
@ -28,7 +28,7 @@ import {
|
|||||||
SetTime, SSIDList, TankInfo,
|
SetTime, SSIDList, TankInfo,
|
||||||
TestPump,
|
TestPump,
|
||||||
VersionInfo,
|
VersionInfo,
|
||||||
FileList, SolarState
|
FileList, SolarState, PumpTestResult
|
||||||
} from "./api";
|
} from "./api";
|
||||||
import {SolarView} from "./solarview";
|
import {SolarView} from "./solarview";
|
||||||
|
|
||||||
@ -345,9 +345,10 @@ export class Controller {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: pretty
|
body: pretty
|
||||||
})
|
})
|
||||||
.then(response => response.text())
|
.then(response => response.json() as Promise<PumpTestResult>)
|
||||||
.then(
|
.then(
|
||||||
_ => {
|
response => {
|
||||||
|
controller.plantViews.setPumpTestCurrent(plantId, response);
|
||||||
clearTimeout(timerId);
|
clearTimeout(timerId);
|
||||||
controller.progressview.removeProgress("test_pump");
|
controller.progressview.removeProgress("test_pump");
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
.plantsensorkey {
|
.plantsensorkey {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plantsensorvalue {
|
.plantsensorvalue {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
@ -9,9 +10,11 @@
|
|||||||
.plantkey {
|
.plantkey {
|
||||||
min-width: 175px;
|
min-width: 175px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plantvalue {
|
.plantvalue {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plantcheckbox {
|
.plantcheckbox {
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -47,7 +50,8 @@
|
|||||||
|
|
||||||
<div class="flexcontainer">
|
<div class="flexcontainer">
|
||||||
<div class="plantkey">Pump Cooldown (m):</div>
|
<div class="plantkey">Pump Cooldown (m):</div>
|
||||||
<input class="plantvalue" id="plant_${plantId}_pump_cooldown_min" type="number" min="0" max="600" placeholder="30">
|
<input class="plantvalue" id="plant_${plantId}_pump_cooldown_min" type="number" min="0" max="600"
|
||||||
|
placeholder="30">
|
||||||
</div>
|
</div>
|
||||||
<div class="flexcontainer">
|
<div class="flexcontainer">
|
||||||
<div class="plantkey">"Pump Hour Start":</div>
|
<div class="plantkey">"Pump Hour Start":</div>
|
||||||
@ -79,6 +83,22 @@
|
|||||||
<div class="plantkey">Sensor B installed:</div>
|
<div class="plantkey">Sensor B installed:</div>
|
||||||
<input class="plantcheckbox" id="plant_${plantId}_sensor_b" type="checkbox">
|
<input class="plantcheckbox" id="plant_${plantId}_sensor_b" type="checkbox">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flexcontainer">
|
||||||
|
<h2 class="plantkey">Current config:</h2>
|
||||||
|
</div>
|
||||||
|
<div class="flexcontainer">
|
||||||
|
<div class="plantkey">Min current</div>
|
||||||
|
<input class="plantvalue" id="plant_${plantId}_min_pump_current_ma" type="number" min="0" max="4500">
|
||||||
|
</div>
|
||||||
|
<div class="flexcontainer">
|
||||||
|
<div class="plantkey">Max current</div>
|
||||||
|
<input class="plantvalue" id="plant_${plantId}_max_pump_current_ma" type="number" min="0" max="4500">
|
||||||
|
</div>
|
||||||
|
<div class="flexcontainer">
|
||||||
|
<div class="plantkey">Ignore current sensor error</div>
|
||||||
|
<input class="plantcheckbox" id="plant_${plantId}_ignore_current_error" type="checkbox">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="flexcontainer">
|
<div class="flexcontainer">
|
||||||
<button class="subtitle" id="plant_${plantId}_test">Test Pump</button>
|
<button class="subtitle" id="plant_${plantId}_test">Test Pump</button>
|
||||||
@ -89,11 +109,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flexcontainer">
|
<div class="flexcontainer">
|
||||||
<span class="plantsensorkey">Sensor A:</span>
|
<span class="plantsensorkey">Sensor A:</span>
|
||||||
<span class="plantsensorvalue" id="plant_${plantId}_moisture_a">loading</span>
|
<span class="plantsensorvalue" id="plant_${plantId}_moisture_a">not measured</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flexcontainer">
|
<div class="flexcontainer">
|
||||||
<div class="plantsensorkey">Sensor B:</div>
|
<div class="plantsensorkey">Sensor B:</div>
|
||||||
<span class="plantsensorvalue" id="plant_${plantId}_moisture_b">loading</span>
|
<span class="plantsensorvalue" id="plant_${plantId}_moisture_b">not measured</span>
|
||||||
|
</div>
|
||||||
|
<div class="flexcontainer">
|
||||||
|
<div class="plantsensorkey">Test Current</div>
|
||||||
|
<span class="plantsensorvalue" id="plant_${plantId}_pump_current_result">not_tested</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {PlantConfig} from "./api";
|
import {PlantConfig, PumpTestResult} from "./api";
|
||||||
|
|
||||||
const PLANT_COUNT = 8;
|
const PLANT_COUNT = 8;
|
||||||
|
|
||||||
@ -26,11 +26,12 @@ export class PlantViews {
|
|||||||
}
|
}
|
||||||
return rv
|
return rv
|
||||||
}
|
}
|
||||||
|
|
||||||
update(moisture_a: [string], moisture_b: [string]) {
|
update(moisture_a: [string], moisture_b: [string]) {
|
||||||
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
|
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
|
||||||
const a = moisture_a[plantId]
|
const a = moisture_a[plantId]
|
||||||
const b = moisture_b[plantId]
|
const b = moisture_b[plantId]
|
||||||
this.plants[plantId].update(a,b)
|
this.plants[plantId].setMeasurementResult(a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +42,11 @@ export class PlantViews {
|
|||||||
plantView.setConfig(plantConfig)
|
plantView.setConfig(plantConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPumpTestCurrent(plantId: number, response: PumpTestResult) {
|
||||||
|
const plantView = this.plants[plantId];
|
||||||
|
plantView.setTestResult(response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PlantView {
|
export class PlantView {
|
||||||
@ -60,7 +66,11 @@ export class PlantView {
|
|||||||
private readonly mode: HTMLSelectElement;
|
private readonly mode: HTMLSelectElement;
|
||||||
private readonly moistureA: HTMLElement;
|
private readonly moistureA: HTMLElement;
|
||||||
private readonly moistureB: HTMLElement;
|
private readonly moistureB: HTMLElement;
|
||||||
|
private readonly pump_current_result: HTMLElement
|
||||||
private readonly maxConsecutivePumpCount: HTMLInputElement;
|
private readonly maxConsecutivePumpCount: HTMLInputElement;
|
||||||
|
private readonly minPumpCurrentMa: HTMLInputElement;
|
||||||
|
private readonly maxPumpCurrentMa: HTMLInputElement;
|
||||||
|
private readonly ignoreCurrentError: HTMLInputElement;
|
||||||
|
|
||||||
|
|
||||||
constructor(plantId: number, parent: HTMLDivElement, controller: Controller) {
|
constructor(plantId: number, parent: HTMLDivElement, controller: Controller) {
|
||||||
@ -77,6 +87,8 @@ export class PlantView {
|
|||||||
|
|
||||||
this.moistureA = document.getElementById("plant_" + plantId + "_moisture_a")! as HTMLElement;
|
this.moistureA = document.getElementById("plant_" + plantId + "_moisture_a")! as HTMLElement;
|
||||||
this.moistureB = document.getElementById("plant_" + plantId + "_moisture_b")! as HTMLElement;
|
this.moistureB = document.getElementById("plant_" + plantId + "_moisture_b")! as HTMLElement;
|
||||||
|
this.pump_current_result = document.getElementById("plant_" + plantId + "_pump_current_result")! as HTMLElement;
|
||||||
|
|
||||||
|
|
||||||
this.testButton = document.getElementById("plant_" + plantId + "_test")! as HTMLButtonElement;
|
this.testButton = document.getElementById("plant_" + plantId + "_test")! as HTMLButtonElement;
|
||||||
this.testButton.onclick = function () {
|
this.testButton.onclick = function () {
|
||||||
@ -139,6 +151,22 @@ export class PlantView {
|
|||||||
controller.configChanged()
|
controller.configChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.minPumpCurrentMa = document.getElementById("plant_" + plantId + "_min_pump_current_ma") as HTMLInputElement;
|
||||||
|
this.minPumpCurrentMa.onchange = function () {
|
||||||
|
controller.configChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.maxPumpCurrentMa = document.getElementById("plant_" + plantId + "_max_pump_current_ma") as HTMLInputElement;
|
||||||
|
this.maxPumpCurrentMa.onchange = function () {
|
||||||
|
controller.configChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ignoreCurrentError = document.getElementById("plant_" + plantId + "_ignore_current_error") as HTMLInputElement;
|
||||||
|
this.ignoreCurrentError.onchange = function () {
|
||||||
|
controller.configChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.maxConsecutivePumpCount = document.getElementById("plant_" + plantId + "_max_consecutive_pump_count") as HTMLInputElement;
|
this.maxConsecutivePumpCount = document.getElementById("plant_" + plantId + "_max_consecutive_pump_count") as HTMLInputElement;
|
||||||
this.maxConsecutivePumpCount.onchange = function () {
|
this.maxConsecutivePumpCount.onchange = function () {
|
||||||
controller.configChanged()
|
controller.configChanged()
|
||||||
@ -158,7 +186,11 @@ export class PlantView {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
update(a: string, b: string) {
|
setTestResult(result: PumpTestResult) {
|
||||||
|
this.pump_current_result.innerText = "Did abort " + result.error + " median current " + result.median_current_ma + " max current " + result.max_current_ma + " min current " + result.min_current_ma
|
||||||
|
}
|
||||||
|
|
||||||
|
setMeasurementResult(a: string, b: string) {
|
||||||
this.moistureA.innerText = a
|
this.moistureA.innerText = a
|
||||||
this.moistureB.innerText = b
|
this.moistureB.innerText = b
|
||||||
}
|
}
|
||||||
@ -173,6 +205,9 @@ export class PlantView {
|
|||||||
this.sensorBInstalled.checked = plantConfig.sensor_b;
|
this.sensorBInstalled.checked = plantConfig.sensor_b;
|
||||||
this.sensorAInstalled.checked = plantConfig.sensor_a;
|
this.sensorAInstalled.checked = plantConfig.sensor_a;
|
||||||
this.maxConsecutivePumpCount.value = plantConfig.max_consecutive_pump_count.toString();
|
this.maxConsecutivePumpCount.value = plantConfig.max_consecutive_pump_count.toString();
|
||||||
|
this.minPumpCurrentMa.value = plantConfig.min_pump_current_ma.toString();
|
||||||
|
this.maxPumpCurrentMa.value = plantConfig.max_pump_current_ma.toString();
|
||||||
|
this.ignoreCurrentError.checked = plantConfig.ignore_current_error;
|
||||||
|
|
||||||
// Set new fields
|
// Set new fields
|
||||||
this.moistureSensorMinFrequency.value =
|
this.moistureSensorMinFrequency.value =
|
||||||
@ -193,7 +228,10 @@ export class PlantView {
|
|||||||
sensor_a: this.sensorAInstalled.checked,
|
sensor_a: this.sensorAInstalled.checked,
|
||||||
max_consecutive_pump_count: this.maxConsecutivePumpCount.valueAsNumber,
|
max_consecutive_pump_count: this.maxConsecutivePumpCount.valueAsNumber,
|
||||||
moisture_sensor_min_frequency: this.moistureSensorMinFrequency.valueAsNumber || null,
|
moisture_sensor_min_frequency: this.moistureSensorMinFrequency.valueAsNumber || null,
|
||||||
moisture_sensor_max_frequency: this.moistureSensorMaxFrequency.valueAsNumber || null
|
moisture_sensor_max_frequency: this.moistureSensorMaxFrequency.valueAsNumber || null,
|
||||||
|
min_pump_current_ma: this.minPumpCurrentMa.valueAsNumber,
|
||||||
|
max_pump_current_ma: this.maxPumpCurrentMa.valueAsNumber,
|
||||||
|
ignore_current_error: this.ignoreCurrentError.checked,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user