added solar ina handling, adjusted website
This commit is contained in:
parent
34b20b1f8f
commit
04849162cd
@ -7,6 +7,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use embedded_hal::digital::OutputPin;
|
use embedded_hal::digital::OutputPin;
|
||||||
use esp_idf_hal::gpio::{IOPin, Pull};
|
use esp_idf_hal::gpio::{IOPin, Pull};
|
||||||
use esp_idf_hal::gpio::{InputOutput, PinDriver};
|
use esp_idf_hal::gpio::{InputOutput, PinDriver};
|
||||||
|
use measurements::{Current, Voltage};
|
||||||
|
|
||||||
pub struct Initial<'a> {
|
pub struct Initial<'a> {
|
||||||
pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||||
@ -123,4 +124,12 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
|
|||||||
self.esp.save_config(&self.config)?;
|
self.esp.save_config(&self.config)?;
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_mptt_voltage(&mut self) -> Result<Voltage> {
|
||||||
|
bail!("Please configure board revision")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mptt_current(&mut self) -> Result<Current> {
|
||||||
|
bail!("Please configure board revision")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ use esp_idf_hal::gpio::{
|
|||||||
use esp_idf_hal::pcnt::PCNT0;
|
use esp_idf_hal::pcnt::PCNT0;
|
||||||
use esp_idf_hal::prelude::Peripherals;
|
use esp_idf_hal::prelude::Peripherals;
|
||||||
use esp_idf_hal::reset::ResetReason;
|
use esp_idf_hal::reset::ResetReason;
|
||||||
|
use measurements::{Current, Voltage};
|
||||||
use pca9535::StandardExpanderInterface;
|
use pca9535::StandardExpanderInterface;
|
||||||
|
|
||||||
//Only support for 8 right now!
|
//Only support for 8 right now!
|
||||||
@ -115,15 +116,18 @@ impl Default for BackupHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait BoardInteraction<'a> {
|
pub trait BoardInteraction<'a> {
|
||||||
|
fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
|
||||||
|
fn is_day(&self) -> bool;
|
||||||
|
fn get_mptt_voltage(&mut self) -> Result<Voltage>;
|
||||||
|
fn get_mptt_current(&mut self) -> Result<Current>;
|
||||||
|
|
||||||
fn get_esp(&mut self) -> &mut ESP<'a>;
|
fn get_esp(&mut self) -> &mut ESP<'a>;
|
||||||
fn get_config(&mut self) -> &PlantControllerConfig;
|
fn get_config(&mut self) -> &PlantControllerConfig;
|
||||||
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
|
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
|
||||||
fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
|
|
||||||
fn deep_sleep(&mut self, duration_in_ms: u64) -> !;
|
fn deep_sleep(&mut self, duration_in_ms: u64) -> !;
|
||||||
fn get_backup_info(&mut self) -> Result<BackupHeader>;
|
fn get_backup_info(&mut self) -> Result<BackupHeader>;
|
||||||
fn get_backup_config(&mut self) -> Result<Vec<u8>>;
|
fn get_backup_config(&mut self) -> Result<Vec<u8>>;
|
||||||
fn backup_config(&mut self, bytes: &[u8]) -> Result<()>;
|
fn backup_config(&mut self, bytes: &[u8]) -> Result<()>;
|
||||||
fn is_day(&self) -> bool;
|
|
||||||
//should be multsampled
|
//should be multsampled
|
||||||
fn water_temperature_c(&mut self) -> Result<f32>;
|
fn water_temperature_c(&mut self) -> Result<f32>;
|
||||||
/// return median tank sensor value in milli volt
|
/// return median tank sensor value in milli volt
|
||||||
|
@ -23,6 +23,7 @@ use esp_idf_hal::pcnt::{
|
|||||||
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
||||||
};
|
};
|
||||||
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
|
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
|
||||||
|
use measurements::{Current, Voltage};
|
||||||
use one_wire_bus::OneWire;
|
use one_wire_bus::OneWire;
|
||||||
use plant_ctrl2::sipo::ShiftRegister40;
|
use plant_ctrl2::sipo::ShiftRegister40;
|
||||||
use std::result::Result::Ok as OkStd;
|
use std::result::Result::Ok as OkStd;
|
||||||
@ -247,6 +248,26 @@ pub(crate) fn create_v3(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BoardInteraction<'a> for V3<'a> {
|
impl<'a> BoardInteraction<'a> for V3<'a> {
|
||||||
|
fn set_charge_indicator(&mut self, charging: bool) -> Result<()> {
|
||||||
|
Ok(self.shift_register.decompose()[CHARGING].set_state(charging.into())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_day(&self) -> bool {
|
||||||
|
self.solar_is_day.get_level().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mptt_voltage(&mut self) -> Result<Voltage> {
|
||||||
|
//if working this is the hardware set mppt voltage
|
||||||
|
if self.is_day() {
|
||||||
|
Ok(Voltage::from_volts(15_f64))
|
||||||
|
} else {
|
||||||
|
Ok(Voltage::from_volts(0_f64))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mptt_current(&mut self) -> Result<Current> {
|
||||||
|
bail!("Board does not have current sensor")
|
||||||
|
}
|
||||||
fn get_esp(&mut self) -> &mut ESP<'a> {
|
fn get_esp(&mut self) -> &mut ESP<'a> {
|
||||||
&mut self.esp
|
&mut self.esp
|
||||||
}
|
}
|
||||||
@ -259,10 +280,6 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
|
|||||||
&mut self.battery_monitor
|
&mut self.battery_monitor
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_charge_indicator(&mut self, charging: bool) -> Result<()> {
|
|
||||||
Ok(self.shift_register.decompose()[CHARGING].set_state(charging.into())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
|
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
|
||||||
let _ = self.shift_register.decompose()[AWAKE].set_low();
|
let _ = self.shift_register.decompose()[AWAKE].set_low();
|
||||||
deep_sleep(duration_in_ms)
|
deep_sleep(duration_in_ms)
|
||||||
@ -364,10 +381,6 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_day(&self) -> bool {
|
|
||||||
self.solar_is_day.get_level().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn water_temperature_c(&mut self) -> Result<f32> {
|
fn water_temperature_c(&mut self) -> Result<f32> {
|
||||||
self.one_wire_bus
|
self.one_wire_bus
|
||||||
.reset(&mut self.esp.delay)
|
.reset(&mut self.esp.delay)
|
||||||
|
@ -23,12 +23,14 @@ use esp_idf_hal::pcnt::{
|
|||||||
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
||||||
};
|
};
|
||||||
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
|
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
|
||||||
use ina219::address::Address;
|
use ina219::address::{Address, Pin};
|
||||||
use ina219::calibration::{Calibration, UnCalibrated};
|
use ina219::calibration::{Calibration, UnCalibrated};
|
||||||
use ina219::SyncIna219;
|
use ina219::SyncIna219;
|
||||||
|
use measurements::{Current, Resistance, Voltage};
|
||||||
use one_wire_bus::OneWire;
|
use one_wire_bus::OneWire;
|
||||||
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
||||||
use std::result::Result::Ok as OkStd;
|
use std::result::Result::Ok as OkStd;
|
||||||
|
use ina219::configuration::{Configuration, OperatingMode};
|
||||||
|
|
||||||
const MS0: u8 = 1_u8;
|
const MS0: u8 = 1_u8;
|
||||||
const MS1: u8 = 0_u8;
|
const MS1: u8 = 0_u8;
|
||||||
@ -37,15 +39,78 @@ const MS3: u8 = 4_u8;
|
|||||||
const MS4: u8 = 2_u8;
|
const MS4: u8 = 2_u8;
|
||||||
const SENSOR_ON: u8 = 5_u8;
|
const SENSOR_ON: u8 = 5_u8;
|
||||||
|
|
||||||
pub struct V4<'a> {
|
pub enum Charger<'a> {
|
||||||
|
SolarMpptV1 {
|
||||||
mppt_ina: SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>,
|
mppt_ina: SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>,
|
||||||
|
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
|
||||||
|
charge_indicator: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Charger<'a> {
|
||||||
|
pub(crate) fn powersave(&mut self) {
|
||||||
|
match self { Charger::SolarMpptV1 { mppt_ina, .. } => {
|
||||||
|
let _ = mppt_ina.set_configuration(Configuration {
|
||||||
|
reset: Default::default(),
|
||||||
|
bus_voltage_range: Default::default(),
|
||||||
|
shunt_voltage_range: Default::default(),
|
||||||
|
bus_resolution: Default::default(),
|
||||||
|
shunt_resolution: Default::default(),
|
||||||
|
operating_mode: OperatingMode::PowerDown,
|
||||||
|
}).map_err(|e| {
|
||||||
|
println!(
|
||||||
|
"Error setting ina mppt configuration during deepsleep preparation{:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::SolarMpptV1 {
|
||||||
|
charge_indicator, ..
|
||||||
|
} => {
|
||||||
|
charge_indicator.set_state(charging.into())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_day(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Charger::SolarMpptV1 { solar_is_day, .. } => solar_is_day.get_level().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage> {
|
||||||
|
let voltage = match self {
|
||||||
|
Charger::SolarMpptV1 { mppt_ina, .. } => mppt_ina
|
||||||
|
.bus_voltage()
|
||||||
|
.map(|v| Voltage::from_millivolts(v.voltage_mv() as f64))?,
|
||||||
|
};
|
||||||
|
Ok(voltage)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mptt_current(&mut self) -> anyhow::Result<Current> {
|
||||||
|
let current = match self {
|
||||||
|
Charger::SolarMpptV1 { mppt_ina, .. } => mppt_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(current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct V4<'a> {
|
||||||
esp: ESP<'a>,
|
esp: ESP<'a>,
|
||||||
|
charger: Charger<'a>,
|
||||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
||||||
config: PlantControllerConfig,
|
config: PlantControllerConfig,
|
||||||
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>,
|
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>,
|
||||||
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
|
|
||||||
signal_counter: PcntDriver<'a>,
|
signal_counter: PcntDriver<'a>,
|
||||||
charge_indicator: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
|
||||||
awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
|
awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
|
||||||
light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||||
tank_power: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
tank_power: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||||
@ -173,7 +238,21 @@ 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(MutexDevice::new(&I2C_DRIVER), Address::from_byte(68)?)?;
|
//TODO error handling is not done nicely here, should not break if ina is not responsive
|
||||||
|
let mut mppt_ina = SyncIna219::new(
|
||||||
|
MutexDevice::new(&I2C_DRIVER),
|
||||||
|
Address::from_pins(Pin::Vcc, Pin::Gnd),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
mppt_ina.set_configuration(Configuration{
|
||||||
|
reset: Default::default(),
|
||||||
|
bus_voltage_range: Default::default(),
|
||||||
|
shunt_voltage_range: Default::default(),
|
||||||
|
bus_resolution: Default::default(),
|
||||||
|
shunt_resolution: ina219::configuration::Resolution::Avg128,
|
||||||
|
operating_mode: Default::default(),
|
||||||
|
})?;
|
||||||
|
//TODO this is probably laready done untill we are ready first time?, maybee add startup time comparison on access?
|
||||||
esp.delay.delay_ms(
|
esp.delay.delay_ms(
|
||||||
mppt_ina
|
mppt_ina
|
||||||
.configuration()?
|
.configuration()?
|
||||||
@ -181,18 +260,17 @@ pub(crate) fn create_v4(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.as_millis() as u32,
|
.as_millis() as u32,
|
||||||
);
|
);
|
||||||
println!("Bus Voltage: {}", mppt_ina.bus_voltage()?);
|
|
||||||
println!("Shunt Voltage: {}", mppt_ina.shunt_voltage()?);
|
let charger = Charger::SolarMpptV1 {
|
||||||
let volt = (mppt_ina.shunt_voltage()?.shunt_voltage_mv()) as f32 / 1000_f32;
|
mppt_ina,
|
||||||
let current = volt / 0.05;
|
solar_is_day,
|
||||||
println!("Shunt Current: {}", current);
|
charge_indicator,
|
||||||
|
};
|
||||||
|
|
||||||
let v = V4 {
|
let v = V4 {
|
||||||
mppt_ina,
|
|
||||||
esp,
|
esp,
|
||||||
awake,
|
awake,
|
||||||
tank_channel,
|
tank_channel,
|
||||||
solar_is_day,
|
|
||||||
signal_counter,
|
signal_counter,
|
||||||
light,
|
light,
|
||||||
tank_power,
|
tank_power,
|
||||||
@ -202,9 +280,9 @@ pub(crate) fn create_v4(
|
|||||||
general_fault,
|
general_fault,
|
||||||
pump_expander,
|
pump_expander,
|
||||||
sensor_expander,
|
sensor_expander,
|
||||||
charge_indicator,
|
|
||||||
config,
|
config,
|
||||||
battery_monitor,
|
battery_monitor,
|
||||||
|
charger,
|
||||||
};
|
};
|
||||||
Ok(Box::new(v))
|
Ok(Box::new(v))
|
||||||
}
|
}
|
||||||
@ -223,14 +301,12 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
|
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
|
||||||
self.charge_indicator
|
self.charger.set_charge_indicator(charging)
|
||||||
.set_state(charging.into())
|
|
||||||
.expect("cannot fail");
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
|
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
|
||||||
self.awake.set_low().unwrap();
|
self.awake.set_low().unwrap();
|
||||||
|
self.charger.powersave();
|
||||||
deep_sleep(duration_in_ms);
|
deep_sleep(duration_in_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +407,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_day(&self) -> bool {
|
fn is_day(&self) -> bool {
|
||||||
self.solar_is_day.get_level().into()
|
self.charger.is_day()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn water_temperature_c(&mut self) -> anyhow::Result<f32> {
|
fn water_temperature_c(&mut self) -> anyhow::Result<f32> {
|
||||||
@ -568,4 +644,12 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
self.esp.save_config(&self.config)?;
|
self.esp.save_config(&self.config)?;
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage> {
|
||||||
|
self.charger.get_mptt_voltage()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mptt_current(&mut self) -> anyhow::Result<Current> {
|
||||||
|
self.charger.get_mptt_current()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -756,13 +756,24 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
|||||||
loop {
|
loop {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||||
if let Ok(charging) = board
|
|
||||||
|
//we have mppt controller, ask it for charging current
|
||||||
|
if let Ok(current) = board.board_hal.get_mptt_current() {
|
||||||
|
let _ = board.board_hal.set_charge_indicator(current.as_milliamperes() > 20_f64);
|
||||||
|
}
|
||||||
|
//fallback to battery controller and ask it instead
|
||||||
|
else if let Ok(charging) = board
|
||||||
.board_hal
|
.board_hal
|
||||||
.get_battery_monitor()
|
.get_battery_monitor()
|
||||||
.average_current_milli_ampere()
|
.average_current_milli_ampere()
|
||||||
{
|
{
|
||||||
let _ = board.board_hal.set_charge_indicator(charging > 20);
|
let _ = board.board_hal.set_charge_indicator(charging > 20);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
//who knows
|
||||||
|
let _ = board.board_hal.set_charge_indicator(false);
|
||||||
|
}
|
||||||
|
|
||||||
match wait_type {
|
match wait_type {
|
||||||
WaitType::MissingConfig => {
|
WaitType::MissingConfig => {
|
||||||
// Keep existing behavior: circular filling pattern
|
// Keep existing behavior: circular filling pattern
|
||||||
|
@ -40,6 +40,13 @@ struct Moistures {
|
|||||||
moisture_b: Vec<std::string::String>,
|
moisture_b: Vec<std::string::String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
struct SolarState {
|
||||||
|
mppt_voltage: f32,
|
||||||
|
mppt_current: f32,
|
||||||
|
is_day: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct SetTime<'a> {
|
struct SetTime<'a> {
|
||||||
time: &'a str,
|
time: &'a str,
|
||||||
@ -218,6 +225,18 @@ fn set_config(
|
|||||||
anyhow::Ok(Some("saved".to_owned()))
|
anyhow::Ok(Some("saved".to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_solar_state(
|
||||||
|
_request: &mut Request<&mut EspHttpConnection>,
|
||||||
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||||
|
let mut board = BOARD_ACCESS.lock().expect("board access");
|
||||||
|
let state = SolarState {
|
||||||
|
mppt_voltage: board.board_hal.get_mptt_voltage()?.as_volts() as f32,
|
||||||
|
mppt_current: board.board_hal.get_mptt_current()?.as_amperes() as f32,
|
||||||
|
is_day: board.board_hal.is_day(),
|
||||||
|
};
|
||||||
|
anyhow::Ok(Some(serde_json::to_string(&state)?))
|
||||||
|
}
|
||||||
|
|
||||||
fn get_battery_state(
|
fn get_battery_state(
|
||||||
_request: &mut Request<&mut EspHttpConnection>,
|
_request: &mut Request<&mut EspHttpConnection>,
|
||||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||||
@ -383,6 +402,11 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
handle_error_to500(request, get_battery_state)
|
handle_error_to500(request, get_battery_state)
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
server
|
||||||
|
.fn_handler("/solar", Method::Get, |request| {
|
||||||
|
handle_error_to500(request, get_solar_state)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
server
|
server
|
||||||
.fn_handler("/time", Method::Get, |request| {
|
.fn_handler("/time", Method::Get, |request| {
|
||||||
handle_error_to500(request, get_time)
|
handle_error_to500(request, get_time)
|
||||||
|
@ -38,6 +38,12 @@ export interface FileList {
|
|||||||
iter_error: string,
|
iter_error: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SolarState{
|
||||||
|
mppt_voltage: number,
|
||||||
|
mppt_current: number,
|
||||||
|
is_day: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface FileInfo{
|
export interface FileInfo{
|
||||||
filename: string,
|
filename: string,
|
||||||
size: number,
|
size: number,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<select class="boardvalue" id="hardware_board_value">
|
<select class="boardvalue" id="hardware_board_value">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="flexcontainer" style="text-decoration-line: line-through;">
|
<div class="flexcontainer">
|
||||||
<div class="boardkey">BatteryMonitor</div>
|
<div class="boardkey">BatteryMonitor</div>
|
||||||
<select class="boardvalue" id="hardware_battery_value">
|
<select class="boardvalue" id="hardware_battery_value">
|
||||||
</select>
|
</select>
|
||||||
|
@ -149,6 +149,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="batteryview" class="subcontainer">
|
<div id="batteryview" class="subcontainer">
|
||||||
</div>
|
</div>
|
||||||
|
<div id="solarview" class="subcontainer">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flexcontainer">
|
<div class="flexcontainer">
|
||||||
|
@ -28,8 +28,9 @@ import {
|
|||||||
SetTime, SSIDList, TankInfo,
|
SetTime, SSIDList, TankInfo,
|
||||||
TestPump,
|
TestPump,
|
||||||
VersionInfo,
|
VersionInfo,
|
||||||
FileList
|
FileList, SolarState
|
||||||
} from "./api";
|
} from "./api";
|
||||||
|
import {SolarView} from "./solarview";
|
||||||
|
|
||||||
export class Controller {
|
export class Controller {
|
||||||
loadTankInfo() : Promise<void> {
|
loadTankInfo() : Promise<void> {
|
||||||
@ -160,7 +161,7 @@ export class Controller {
|
|||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateBatteryData(): Promise<void> {
|
updateBatteryData() {
|
||||||
return fetch(PUBLIC_URL + "/battery")
|
return fetch(PUBLIC_URL + "/battery")
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(json => json as BatteryState)
|
.then(json => json as BatteryState)
|
||||||
@ -172,6 +173,18 @@ export class Controller {
|
|||||||
console.log(error);
|
console.log(error);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
updateSolarData() {
|
||||||
|
return fetch(PUBLIC_URL + "/solar")
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(json => json as SolarState)
|
||||||
|
.then(solar => {
|
||||||
|
controller.solarView.update(solar)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
controller.solarView.update(null)
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
}
|
||||||
uploadNewFirmware(file: File) {
|
uploadNewFirmware(file: File) {
|
||||||
var current = 0;
|
var current = 0;
|
||||||
var max = 100;
|
var max = 100;
|
||||||
@ -244,6 +257,7 @@ export class Controller {
|
|||||||
//load from remote to be clean
|
//load from remote to be clean
|
||||||
controller.downloadConfig()
|
controller.downloadConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
backupConfig(json: string, statusCallback: (status: string) => void) {
|
backupConfig(json: string, statusCallback: (status: string) => void) {
|
||||||
controller.progressview.addIndeterminate("backup_config", "Backingup Config")
|
controller.progressview.addIndeterminate("backup_config", "Backingup Config")
|
||||||
fetch(PUBLIC_URL + "/backup_config", {
|
fetch(PUBLIC_URL + "/backup_config", {
|
||||||
@ -465,6 +479,7 @@ export class Controller {
|
|||||||
readonly firmWareView: OTAView;
|
readonly firmWareView: OTAView;
|
||||||
readonly progressview: ProgressView;
|
readonly progressview: ProgressView;
|
||||||
readonly batteryView: BatteryView;
|
readonly batteryView: BatteryView;
|
||||||
|
readonly solarView: SolarView;
|
||||||
readonly fileview: FileView;
|
readonly fileview: FileView;
|
||||||
readonly logView: LogView
|
readonly logView: LogView
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -473,6 +488,7 @@ export class Controller {
|
|||||||
this.networkView = new NetworkConfigView(this, PUBLIC_URL)
|
this.networkView = new NetworkConfigView(this, PUBLIC_URL)
|
||||||
this.tankView = new TankConfigView(this)
|
this.tankView = new TankConfigView(this)
|
||||||
this.batteryView = new BatteryView(this)
|
this.batteryView = new BatteryView(this)
|
||||||
|
this.solarView = new SolarView(this)
|
||||||
this.nightLampView = new NightLampView(this)
|
this.nightLampView = new NightLampView(this)
|
||||||
this.submitView = new SubmitView(this)
|
this.submitView = new SubmitView(this)
|
||||||
this.firmWareView = new OTAView(this)
|
this.firmWareView = new OTAView(this)
|
||||||
@ -489,21 +505,16 @@ export class Controller {
|
|||||||
controller.exit();
|
controller.exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selftest() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const controller = new Controller();
|
const controller = new Controller();
|
||||||
controller.progressview.removeProgress("rebooting");
|
controller.progressview.removeProgress("rebooting");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const tasks = [
|
const tasks = [
|
||||||
{ task: controller.populateTimezones, displayString: "Populating Timezones" },
|
{ task: controller.populateTimezones, displayString: "Populating Timezones" },
|
||||||
{ task: controller.updateRTCData, displayString: "Updating RTC Data" },
|
{ task: controller.updateRTCData, displayString: "Updating RTC Data" },
|
||||||
{ task: controller.updateBatteryData, displayString: "Updating Battery Data" },
|
{ task: controller.updateBatteryData, displayString: "Updating Battery Data" },
|
||||||
|
{ task: controller.updateSolarData, displayString: "Updating Solar Data" },
|
||||||
{ task: controller.downloadConfig, displayString: "Downloading Configuration" },
|
{ task: controller.downloadConfig, displayString: "Downloading Configuration" },
|
||||||
{ task: controller.version, displayString: "Fetching Version Information" },
|
{ task: controller.version, displayString: "Fetching Version Information" },
|
||||||
{ task: controller.updateFileList, displayString: "Updating File List" },
|
{ task: controller.updateFileList, displayString: "Updating File List" },
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<div class="lightkey">Test Nightlight</div>
|
<div class="lightkey">Test Nightlight</div>
|
||||||
<input class="lightcheckbox" type="checkbox" id="night_lamp_test">
|
<input class="lightcheckbox" type="checkbox" id="night_lamp_test">
|
||||||
</div>
|
</div>
|
||||||
<div class="flexcontainer" style="text-decoration-line: line-through;">
|
<div class="flexcontainer">
|
||||||
<div class="lightkey">Enable Nightlight</div>
|
<div class="lightkey">Enable Nightlight</div>
|
||||||
<input class="lightcheckbox" type="checkbox" id="night_lamp_enabled">
|
<input class="lightcheckbox" type="checkbox" id="night_lamp_enabled">
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +29,7 @@ export class OTAView {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test.onclick = () => {
|
test.onclick = () => {
|
||||||
controller.selftest();
|
controller.selfTest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
29
rust/src_webpack/src/solarview.html
Normal file
29
rust/src_webpack/src/solarview.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<style>
|
||||||
|
.solarflexkey {
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
.solarflexvalue {
|
||||||
|
text-wrap: nowrap;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="flexcontainer">
|
||||||
|
<div class="subtitle">
|
||||||
|
Mppt:
|
||||||
|
</div>
|
||||||
|
<input id="solar_auto_refresh" type="checkbox">⟳
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flexcontainer">
|
||||||
|
<span class="solarflexkey">Mppt mV:</span>
|
||||||
|
<span class="solarflexvalue" id="solar_voltage_milli_volt"></span>
|
||||||
|
</div>
|
||||||
|
<div class="flexcontainer">
|
||||||
|
<span class="solarflexkey">Mppt mA:</span>
|
||||||
|
<span class="solarflexvalue" id="solar_current_milli_ampere" ></span>
|
||||||
|
</div>
|
||||||
|
<div class="flexcontainer">
|
||||||
|
<span class="solarflexkey">is Day:</span>
|
||||||
|
<span class="solarflexvalue" id="solar_is_day" ></span>
|
||||||
|
</div>
|
49
rust/src_webpack/src/solarview.ts
Normal file
49
rust/src_webpack/src/solarview.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { Controller } from "./main";
|
||||||
|
import {BatteryState, SolarState} from "./api";
|
||||||
|
|
||||||
|
export class SolarView{
|
||||||
|
solar_voltage_milli_volt: HTMLSpanElement;
|
||||||
|
solar_current_milli_ampere: HTMLSpanElement;
|
||||||
|
solar_is_day: HTMLSpanElement;
|
||||||
|
solar_auto_refresh: HTMLInputElement;
|
||||||
|
timer: NodeJS.Timeout | undefined;
|
||||||
|
controller: Controller;
|
||||||
|
|
||||||
|
constructor (controller:Controller) {
|
||||||
|
(document.getElementById("solarview") as HTMLElement).innerHTML = require("./solarview.html")
|
||||||
|
this.solar_voltage_milli_volt = document.getElementById("solar_voltage_milli_volt") as HTMLSpanElement;
|
||||||
|
this.solar_current_milli_ampere = document.getElementById("solar_current_milli_ampere") as HTMLSpanElement;
|
||||||
|
this.solar_is_day = document.getElementById("solar_is_day") as HTMLSpanElement;
|
||||||
|
this.solar_auto_refresh = document.getElementById("solar_auto_refresh") as HTMLInputElement;
|
||||||
|
|
||||||
|
this.controller = controller
|
||||||
|
this.solar_auto_refresh.onchange = () => {
|
||||||
|
if(this.timer){
|
||||||
|
clearTimeout(this.timer)
|
||||||
|
}
|
||||||
|
if(this.solar_auto_refresh.checked){
|
||||||
|
controller.updateSolarData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update(solarState: SolarState|null){
|
||||||
|
if (solarState == null) {
|
||||||
|
this.solar_voltage_milli_volt.innerText = "N/A"
|
||||||
|
this.solar_current_milli_ampere.innerText = "N/A"
|
||||||
|
this.solar_is_day.innerText = "N/A"
|
||||||
|
} else {
|
||||||
|
this.solar_voltage_milli_volt.innerText = solarState.mppt_voltage.toFixed(2)
|
||||||
|
this.solar_current_milli_ampere.innerText = String(+solarState.mppt_current)
|
||||||
|
this.solar_is_day.innerText = solarState.is_day?"🌞":"🌙"
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.solar_auto_refresh.checked){
|
||||||
|
this.timer = setTimeout(this.controller.updateSolarData, 1000);
|
||||||
|
} else {
|
||||||
|
if(this.timer){
|
||||||
|
clearTimeout(this.timer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,14 +7,31 @@
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
.submitbutton{
|
||||||
|
padding: 1em 1em;
|
||||||
|
background: #667eea;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submitbutton:hover {
|
||||||
|
background: #1c4e63;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<button class="submitbutton" id="submit">Submit</button>
|
||||||
|
<br>
|
||||||
<button id="showJson">Show Json</button>
|
<button id="showJson">Show Json</button>
|
||||||
<div id="rawdata" class="flexcontainer" style="display: none;">
|
<div id="rawdata" class="flexcontainer" style="display: none;">
|
||||||
<div class="submitarea" id="json" contenteditable="true"></div>
|
<div class="submitarea" id="json" contenteditable="true"></div>
|
||||||
<div class="submitarea" id="backupjson">backup will be here</div>
|
<div class="submitarea" id="backupjson">backup will be here</div>
|
||||||
</div>
|
</div>
|
||||||
<button id="submit">Submit</button>
|
|
||||||
<div>BackupStatus:</div>
|
<div>BackupStatus:</div>
|
||||||
<div id="backuptimestamp"></div>
|
<div id="backuptimestamp"></div>
|
||||||
<div id="backupsize"></div>
|
<div id="backupsize"></div>
|
||||||
|
@ -9,7 +9,7 @@ console.log("Dev server is " + isDevServer);
|
|||||||
var host;
|
var host;
|
||||||
if (isDevServer){
|
if (isDevServer){
|
||||||
//ensure no trailing /
|
//ensure no trailing /
|
||||||
host = 'http://192.168.71.1';
|
host = 'http://10.23.44.186';
|
||||||
} else {
|
} else {
|
||||||
host = '';
|
host = '';
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user