diff --git a/charge-controller-supervisor/src/controller.rs b/charge-controller-supervisor/src/controller.rs index 6c21b66..1a612d1 100644 --- a/charge-controller-supervisor/src/controller.rs +++ b/charge-controller-supervisor/src/controller.rs @@ -47,6 +47,7 @@ impl ControllerState { #[derive(serde::Serialize, Clone)] #[serde(tag = "model")] pub enum ControllerSettings { + Pl(pl::PlSettings), Tristar(tristar::TristarSettings), } @@ -128,7 +129,7 @@ impl Controller { if self.needs_new_settings() { match self.inner.get_settings().await { Ok(s) => { - *self.data.write_settings().await = s; + *self.data.write_settings().await = Some(s); self.settings_last_read = Some(std::time::Instant::now()); } Err(e) => { @@ -211,12 +212,15 @@ impl ControllerInner { } } - pub async fn get_settings(&mut self) -> eyre::Result> { + pub async fn get_settings(&mut self) -> eyre::Result { match self { - ControllerInner::Pl(_) => Ok(None), + ControllerInner::Pl(pl) => { + let settings = pl.read_settings().await?; + Ok(ControllerSettings::Pl(settings)) + } ControllerInner::Tristar(tristar) => { let settings = tristar.read_settings().await?; - Ok(Some(ControllerSettings::Tristar(settings))) + Ok(ControllerSettings::Tristar(settings)) } } } diff --git a/charge-controller-supervisor/src/controller/pl.rs b/charge-controller-supervisor/src/controller/pl.rs index 9a61fe5..b581221 100644 --- a/charge-controller-supervisor/src/controller/pl.rs +++ b/charge-controller-supervisor/src/controller/pl.rs @@ -103,6 +103,48 @@ fn set_regulator_gauges(state: RegulatorState, label: &str) { } } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PlSettings { + load_disconnect_voltage: f64, + load_reconnect_voltage: f64, + delay_before_disconnect_minutes: u8, + days_between_boost: u8, + absorption_time: u8, + hysteresis: u8, + boost_return_voltage: f64, + charge_current_limit: u8, + battery_2_regulation_voltage: f64, + days_between_equalization: u8, + equalization_length: u8, + absorption_voltage: f64, + equalization_voltage: f64, + float_voltage: f64, + boost_voltage: f64, + program: Program, + system_voltage: SystemVoltage, + battery_capacity_ah: usize, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Program { + Prog0, + Prog1, + Prog2, + Prog3, + Prog4, + Unknown, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum SystemVoltage { + V12, + V24, + V32, + V36, + V48, + Unknown, +} + #[expect(dead_code, reason = "writing settings is not yet implemented")] #[derive(Debug, Clone, Copy)] pub enum PliRequest { @@ -186,6 +228,75 @@ impl Pli { Ok(()) } + pub async fn read_settings(&mut self) -> eyre::Result { + let load_disconnect_voltage = + f64::from(self.read_eeprom(PlEepromAddress::LOff).await?) * (4. / 10.); + let load_reconnect_voltage = + f64::from(self.read_eeprom(PlEepromAddress::LOn).await?) * (4. / 10.); + let delay_before_disconnect_minutes = self.read_eeprom(PlEepromAddress::LDel).await?; + let days_between_boost = self.read_eeprom(PlEepromAddress::BstFreq).await?; + let absorption_time = self.read_eeprom(PlEepromAddress::ATim).await?; + let hysteresis = self.read_eeprom(PlEepromAddress::Hyst).await?; + let boost_return_voltage = + f64::from(self.read_eeprom(PlEepromAddress::BRet).await?) * (4. / 10.); + let charge_current_limit = self.read_eeprom(PlEepromAddress::CurLim).await?; + let battery_2_regulation_voltage = + f64::from(self.read_eeprom(PlEepromAddress::Bat2).await?) * (4. / 10.); + let days_between_equalization = self.read_eeprom(PlEepromAddress::EqFreq).await?; + let equalization_length = self.read_eeprom(PlEepromAddress::ETim).await?; + let absorption_voltage = + f64::from(self.read_eeprom(PlEepromAddress::AbsV).await?) * (4. / 10.); + let equalization_voltage = + f64::from(self.read_eeprom(PlEepromAddress::EMax).await?) * (4. / 10.); + let float_voltage = f64::from(self.read_eeprom(PlEepromAddress::FltV).await?) * (4. / 10.); + let boost_voltage = f64::from(self.read_eeprom(PlEepromAddress::BMax).await?) * (4. / 10.); + let volt = self.read_eeprom(PlEepromAddress::Volt).await?; + let program = match volt >> 4 { + 0 => Program::Prog0, + 1 => Program::Prog1, + 2 => Program::Prog2, + 3 => Program::Prog3, + 4 => Program::Prog4, + _ => Program::Unknown, + }; + let system_voltage = match volt & 0xF { + 0 => SystemVoltage::V12, + 1 => SystemVoltage::V24, + 2 => SystemVoltage::V32, + 3 => SystemVoltage::V36, + 4 => SystemVoltage::V48, + _ => SystemVoltage::Unknown, + }; + let battery_cap = usize::from(self.read_eeprom(PlEepromAddress::BCap).await?); + + let battery_capacity_ah = if battery_cap > 50 { + (50 * 20) + ((battery_cap - 50) * 100) + } else { + battery_cap * 20 + }; + + Ok(PlSettings { + load_disconnect_voltage, + load_reconnect_voltage, + delay_before_disconnect_minutes, + days_between_boost, + absorption_time, + hysteresis, + boost_return_voltage, + charge_current_limit, + battery_2_regulation_voltage, + days_between_equalization, + equalization_length, + absorption_voltage, + equalization_voltage, + float_voltage, + boost_voltage, + program, + system_voltage, + battery_capacity_ah, + }) + } + async fn read_state(&mut self) -> eyre::Result { // let int_charge_acc_low = self.read_ram(PlRamAddress::Ciacc1).await?; // let int_charge_acc = self.read_ram(PlRamAddress::Ciacc2).await?; @@ -449,6 +560,112 @@ impl From for u8 { } } +#[allow(dead_code)] +enum PlEepromAddress { + // calibration + BCals, + BCal12, + BCal24, + BCal48, + ChargeOffset, + ChargeGain, + LoadOffset, + LoadGain, + BatTmpOffset, + BatTmpGain, + SolarOffset, + SolarGain, + BatsenOffset, + BatsenGain, + + // settings + GOn, + GOff, + GDel, + GExF, + GRun, + LOff, + LOn, + LDel, + ASet, + BstFreq, + ATim, + Hyst, + BRet, + CurLim, + Bat2, + ESet1, + ESet2, + ESet3, + EqFreq, + ETim, + AbsV, + EMax, + FltV, + BMax, + LGSet, + PwmE, + SStop, + EtMod, + GMod, + Volt, + BCap, + HistoryDataPtr, +} + +impl From for u8 { + fn from(value: PlEepromAddress) -> Self { + match value { + PlEepromAddress::BCals => 0x00, + PlEepromAddress::BCal12 => 0x01, + PlEepromAddress::BCal24 => 0x02, + PlEepromAddress::BCal48 => 0x03, + PlEepromAddress::ChargeOffset => 0x04, + PlEepromAddress::ChargeGain => 0x05, + PlEepromAddress::LoadOffset => 0x06, + PlEepromAddress::LoadGain => 0x07, + PlEepromAddress::BatTmpOffset => 0x08, + PlEepromAddress::BatTmpGain => 0x09, + PlEepromAddress::SolarOffset => 0x0A, + PlEepromAddress::SolarGain => 0x0B, + PlEepromAddress::BatsenOffset => 0x0C, + PlEepromAddress::BatsenGain => 0x0D, + PlEepromAddress::GOn => 0x0E, + PlEepromAddress::GOff => 0x0F, + PlEepromAddress::GDel => 0x10, + PlEepromAddress::GExF => 0x11, + PlEepromAddress::GRun => 0x12, + PlEepromAddress::LOff => 0x13, + PlEepromAddress::LOn => 0x14, + PlEepromAddress::LDel => 0x15, + PlEepromAddress::ASet => 0x16, + PlEepromAddress::BstFreq => 0x17, + PlEepromAddress::ATim => 0x18, + PlEepromAddress::Hyst => 0x19, + PlEepromAddress::BRet => 0x1A, + PlEepromAddress::CurLim => 0x1B, + PlEepromAddress::Bat2 => 0x1C, + PlEepromAddress::ESet1 => 0x1D, + PlEepromAddress::ESet2 => 0x1E, + PlEepromAddress::ESet3 => 0x1F, + PlEepromAddress::EqFreq => 0x20, + PlEepromAddress::ETim => 0x21, + PlEepromAddress::AbsV => 0x22, + PlEepromAddress::EMax => 0x23, + PlEepromAddress::FltV => 0x24, + PlEepromAddress::BMax => 0x25, + PlEepromAddress::LGSet => 0x26, + PlEepromAddress::PwmE => 0x27, + PlEepromAddress::SStop => 0x28, + PlEepromAddress::EtMod => 0x29, + PlEepromAddress::GMod => 0x2A, + PlEepromAddress::Volt => 0x2B, + PlEepromAddress::BCap => 0x2c, + PlEepromAddress::HistoryDataPtr => 0x2d, + } + } +} + const fn command(operation: u8, address: u8, data: u8) -> [u8; 4] { [operation, address, data, !operation] }