Compare commits

...

3 Commits

19 changed files with 412 additions and 160 deletions

View File

@ -1,7 +1,14 @@
<component name="ProjectDictionaryState"> <component name="ProjectDictionaryState">
<dictionary name="project"> <dictionary name="project">
<words> <words>
<w>buildtime</w>
<w>deepsleep</w>
<w>githash</w>
<w>lightstate</w>
<w>mppt</w>
<w>plantstate</w>
<w>sntp</w> <w>sntp</w>
<w>vergen</w>
</words> </words>
</dictionary> </dictionary>
</component> </component>

View File

@ -63,14 +63,14 @@ pub struct MqttClient<'a> {
mqtt_client: EspMqttClient<'a>, mqtt_client: EspMqttClient<'a>,
base_topic: heapless::String<64>, base_topic: heapless::String<64>,
} }
pub struct ESP<'a> { pub struct Esp<'a> {
pub(crate) mqtt_client: Option<MqttClient<'a>>, pub(crate) mqtt_client: Option<MqttClient<'a>>,
pub(crate) wifi_driver: EspWifi<'a>, pub(crate) wifi_driver: EspWifi<'a>,
pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
pub(crate) delay: Delay, pub(crate) delay: Delay,
} }
impl ESP<'_> { impl Esp<'_> {
const SPIFFS_PARTITION_NAME: &'static str = "storage"; const SPIFFS_PARTITION_NAME: &'static str = "storage";
const CONFIG_FILE: &'static str = "/spiffs/config.cfg"; const CONFIG_FILE: &'static str = "/spiffs/config.cfg";
const BASE_PATH: &'static str = "/spiffs"; const BASE_PATH: &'static str = "/spiffs";
@ -310,7 +310,7 @@ impl ESP<'_> {
filename: file.file_name().into_string().unwrap(), filename: file.file_name().into_string().unwrap(),
size: file size: file
.metadata() .metadata()
.and_then(|it| Ok(it.len())) .map(|it| it.len())
.unwrap_or_default() .unwrap_or_default()
as usize, as usize,
}; };

View File

@ -1,4 +1,4 @@
use crate::hal::esp::ESP; use crate::hal::esp::Esp;
use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor}; use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor};
use crate::{ use crate::{
config::PlantControllerConfig, config::PlantControllerConfig,
@ -9,10 +9,11 @@ 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>,
pub(crate) esp: ESP<'a>, pub(crate) esp: Esp<'a>,
pub(crate) config: PlantControllerConfig, pub(crate) config: PlantControllerConfig,
pub(crate) battery: Box<dyn BatteryInteraction + Send>, pub(crate) battery: Box<dyn BatteryInteraction + Send>,
} }
@ -21,7 +22,7 @@ pub(crate) fn create_initial_board(
free_pins: FreePeripherals, free_pins: FreePeripherals,
fs_mount_error: bool, fs_mount_error: bool,
config: PlantControllerConfig, config: PlantControllerConfig,
esp: ESP<'static>, esp: Esp<'static>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> { ) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?; let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?;
general_fault.set_pull(Pull::Floating)?; general_fault.set_pull(Pull::Floating)?;
@ -40,7 +41,7 @@ pub(crate) fn create_initial_board(
} }
impl<'a> BoardInteraction<'a> for Initial<'a> { impl<'a> BoardInteraction<'a> for Initial<'a> {
fn get_esp(&mut self) -> &mut ESP<'a> { fn get_esp(&mut self) -> &mut Esp<'a> {
&mut self.esp &mut self.esp
} }
@ -125,4 +126,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")
}
} }

View File

@ -8,7 +8,7 @@ use crate::{
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig}, config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
hal::{ hal::{
battery::{print_battery_bq34z100, BatteryInteraction, NoBatteryMonitor}, battery::{print_battery_bq34z100, BatteryInteraction, NoBatteryMonitor},
esp::ESP, esp::Esp,
}, },
log::{log, LogMessage}, log::{log, LogMessage},
}; };
@ -37,6 +37,7 @@ use esp_idf_sys::{
esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW,
}; };
use esp_ota::mark_app_valid; use esp_ota::mark_app_valid;
use measurements::{Current, Voltage};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::result::Result::Ok as OkStd; use std::result::Result::Ok as OkStd;
@ -83,25 +84,15 @@ pub struct HAL<'a> {
pub board_hal: Box<dyn BoardInteraction<'a> + Send>, pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug, Default)]
pub struct BackupHeader { pub struct BackupHeader {
pub timestamp: i64, pub timestamp: i64,
crc16: u16, crc16: u16,
pub size: usize, pub size: usize,
} }
impl Default for BackupHeader {
fn default() -> Self {
Self {
timestamp: Default::default(),
crc16: Default::default(),
size: Default::default(),
}
}
}
pub trait BoardInteraction<'a> { pub trait BoardInteraction<'a> {
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 set_charge_indicator(&mut self, charging: bool) -> Result<()>;
@ -125,6 +116,8 @@ pub trait BoardInteraction<'a> {
fn test_pump(&mut self, plant: usize) -> Result<()>; 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_current(&mut self) -> anyhow::Result<Current>;
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -222,7 +215,7 @@ impl PlantHal {
gpio30: peripherals.pins.gpio30, gpio30: peripherals.pins.gpio30,
}; };
let mut esp = ESP { let mut esp = Esp {
mqtt_client: None, mqtt_client: None,
wifi_driver, wifi_driver,
boot_button, boot_button,

View File

@ -5,7 +5,7 @@ use crate::hal::{
use crate::log::{log, LogMessage}; use crate::log::{log, LogMessage};
use crate::{ use crate::{
config::PlantControllerConfig, config::PlantControllerConfig,
hal::{battery::BatteryInteraction, esp::ESP}, hal::{battery::BatteryInteraction, esp::Esp},
}; };
use anyhow::{anyhow, bail, Ok, Result}; use anyhow::{anyhow, bail, Ok, Result};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -26,6 +26,7 @@ use esp_idf_hal::{
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, 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;
@ -78,7 +79,7 @@ const FAULT_2: usize = 23;
pub struct V3<'a> { pub struct V3<'a> {
config: PlantControllerConfig, config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
esp: ESP<'a>, esp: Esp<'a>,
shift_register: ShiftRegister40< shift_register: ShiftRegister40<
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
@ -106,7 +107,7 @@ pub struct V3<'a> {
pub(crate) fn create_v3( pub(crate) fn create_v3(
peripherals: FreePeripherals, peripherals: FreePeripherals,
esp: ESP<'static>, esp: Esp<'static>,
config: PlantControllerConfig, config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> { ) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
@ -249,7 +250,27 @@ pub(crate) fn create_v3(
} }
impl<'a> BoardInteraction<'a> for V3<'a> { impl<'a> BoardInteraction<'a> for V3<'a> {
fn get_esp(&mut self) -> &mut ESP<'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> {
&mut self.esp &mut self.esp
} }
@ -261,10 +282,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)
@ -366,10 +383,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)

View File

@ -1,6 +1,6 @@
pub use crate::config::PlantControllerConfig; use crate::config::PlantControllerConfig;
use crate::hal::battery::BatteryInteraction; use crate::hal::battery::BatteryInteraction;
use crate::hal::esp::ESP; use crate::hal::esp::Esp;
use crate::hal::{ use crate::hal::{
deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT, deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT,
REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25, REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25,
@ -13,19 +13,21 @@ use ds323x::{DateTimeAccess, Ds323x};
use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr}; use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr};
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
use embedded_hal_bus::i2c::MutexDevice; use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::{ use esp_idf_hal::adc::oneshot::config::AdcChannelConfig;
adc::{ use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver};
attenuation, use esp_idf_hal::adc::{attenuation, Resolution};
oneshot::{config::AdcChannelConfig, AdcChannelDriver, AdcDriver}, use esp_idf_hal::delay::Delay;
Resolution, use esp_idf_hal::gpio::{AnyInputPin, Gpio5, IOPin, InputOutput, Output, PinDriver, Pull};
}, use esp_idf_hal::i2c::I2cDriver;
delay::Delay, use esp_idf_hal::pcnt::{
gpio::{AnyInputPin, Gpio5, IOPin, InputOutput, Output, PinDriver, Pull}, PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
i2c::I2cDriver,
pcnt::{PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex},
}; };
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, EspError}; use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, EspError};
use ina219::{address::Address, calibration::UnCalibrated, SyncIna219}; use ina219::address::{Address, Pin};
use ina219::calibration::UnCalibrated;
use ina219::configuration::{Configuration, OperatingMode};
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;
@ -37,15 +39,82 @@ 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 enum Charger<'a> {
SolarMpptV1 {
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 Charger<'_> {
pub(crate) fn power_save(&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 deep sleep 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> { pub struct V4<'a> {
_mppt_ina: SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>, 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>,
@ -63,12 +132,12 @@ pub struct V4<'a> {
sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
} }
pub(crate) fn create_v4<'a>( pub(crate) fn create_v4(
peripherals: FreePeripherals, peripherals: FreePeripherals,
esp: ESP<'static>, esp: Esp<'static>,
config: PlantControllerConfig, config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
) -> anyhow::Result<Box<dyn BoardInteraction<'static> + Send>> { ) -> anyhow::Result<Box<dyn BoardInteraction<'static> + Send + 'static>> {
let mut awake = PinDriver::output(peripherals.gpio15.downgrade())?; let mut awake = PinDriver::output(peripherals.gpio15.downgrade())?;
awake.set_high()?; awake.set_high()?;
@ -173,7 +242,21 @@ pub(crate) fn create_v4<'a>(
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 already done until we are ready first time?, maybe add startup time comparison on access?
esp.delay.delay_ms( esp.delay.delay_ms(
mppt_ina mppt_ina
.configuration()? .configuration()?
@ -181,18 +264,17 @@ pub(crate) fn create_v4<'a>(
.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: mppt_ina,
esp, esp,
awake, awake,
tank_channel, tank_channel,
solar_is_day,
signal_counter, signal_counter,
light, light,
tank_power, tank_power,
@ -202,15 +284,15 @@ pub(crate) fn create_v4<'a>(
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))
} }
impl<'a> BoardInteraction<'a> for V4<'a> { impl<'a> BoardInteraction<'a> for V4<'a> {
fn get_esp(&mut self) -> &mut ESP<'a> { fn get_esp(&mut self) -> &mut Esp<'a> {
&mut self.esp &mut self.esp
} }
@ -223,14 +305,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.power_save();
deep_sleep(duration_in_ms); deep_sleep(duration_in_ms);
} }
@ -331,7 +411,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 +648,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()
}
} }

View File

@ -65,7 +65,7 @@ pub fn get_log() -> Vec<LogEntry> {
read_copy.push(copy); read_copy.push(copy);
} }
drop(buffer); drop(buffer);
return read_copy; read_copy
} }
pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) { pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) {

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
config::BoardVersion::INITIAL, config::BoardVersion::INITIAL,
hal::{PlantHal, HAL, PLANT_COUNT}, hal::{PlantHal, HAL, PLANT_COUNT},
webserver::webserver::httpd, webserver::httpd,
}; };
use anyhow::bail; use anyhow::bail;
use chrono::{DateTime, Datelike, Timelike, Utc}; use chrono::{DateTime, Datelike, Timelike, Utc};
@ -12,7 +12,7 @@ use esp_idf_sys::{
esp_ota_img_states_t, esp_ota_img_states_t_ESP_OTA_IMG_ABORTED, esp_ota_img_states_t, esp_ota_img_states_t_ESP_OTA_IMG_ABORTED,
esp_ota_img_states_t_ESP_OTA_IMG_INVALID, esp_ota_img_states_t_ESP_OTA_IMG_NEW, esp_ota_img_states_t_ESP_OTA_IMG_INVALID, esp_ota_img_states_t_ESP_OTA_IMG_NEW,
esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY, esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED, esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY, esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED,
esp_ota_img_states_t_ESP_OTA_IMG_VALID, vTaskDelay, esp_ota_img_states_t_ESP_OTA_IMG_VALID,
}; };
use esp_ota::{mark_app_valid, rollback_and_reboot}; use esp_ota::{mark_app_valid, rollback_and_reboot};
use hal::battery::BatteryState; use hal::battery::BatteryState;
@ -28,13 +28,12 @@ mod hal;
mod log; mod log;
mod plant_state; mod plant_state;
mod tank; mod tank;
mod webserver;
pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap()); pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap());
pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false)); pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
mod webserver {
pub mod webserver;
}
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
enum WaitType { enum WaitType {
@ -69,7 +68,7 @@ struct LightState {
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
///mqtt stuct to track pump activities ///mqtt struct to track pump activities
struct PumpInfo { struct PumpInfo {
enabled: bool, enabled: bool,
pump_ineffective: bool, pump_ineffective: bool,
@ -175,16 +174,7 @@ fn safe_main() -> anyhow::Result<()> {
} }
println!("cur is {}", cur); println!("cur is {}", cur);
match board update_charge_indicator(&mut board);
.board_hal
.get_battery_monitor()
.average_current_milli_ampere()
{
Ok(charging) => {
let _ = board.board_hal.set_charge_indicator(charging > 20);
}
Err(_) => {}
}
if board.board_hal.get_esp().get_restart_to_conf() { if board.board_hal.get_esp().get_restart_to_conf() {
log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", ""); log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "");
@ -265,7 +255,7 @@ fn safe_main() -> anyhow::Result<()> {
address, address,
ota_state_string, ota_state_string,
&mut board, &mut board,
&ip_address, ip_address,
timezone_time, timezone_time,
); );
publish_battery_state(&mut board); publish_battery_state(&mut board);
@ -426,7 +416,7 @@ fn safe_main() -> anyhow::Result<()> {
.board_hal .board_hal
.get_battery_monitor() .get_battery_monitor()
.get_battery_state() .get_battery_state()
.unwrap_or(hal::battery::BatteryState::Unknown); .unwrap_or(BatteryState::Unknown);
let mut light_state = LightState { let mut light_state = LightState {
enabled: board.board_hal.get_config().night_lamp.enabled, enabled: board.board_hal.get_config().night_lamp.enabled,
@ -556,6 +546,26 @@ 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);
} }
fn update_charge_indicator(board: &mut MutexGuard<HAL>) {
//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
.get_battery_monitor()
.average_current_milli_ampere()
{
let _ = board.board_hal.set_charge_indicator(charging > 20);
} else {
//who knows
let _ = board.board_hal.set_charge_indicator(false);
}
}
fn obtain_tank_temperature(board: &mut MutexGuard<HAL>) -> anyhow::Result<f32> { fn obtain_tank_temperature(board: &mut MutexGuard<HAL>) -> anyhow::Result<f32> {
//multisample should be moved to water_temperature_c //multisample should be moved to water_temperature_c
let mut attempt = 1; let mut attempt = 1;
@ -608,7 +618,7 @@ fn publish_plant_states(
.zip(&board.board_hal.get_config().plants.clone()) .zip(&board.board_hal.get_config().plants.clone())
.enumerate() .enumerate()
{ {
match serde_json::to_string(&plant_state.to_mqtt_info(plant_conf, &timezone_time)) { match serde_json::to_string(&plant_state.to_mqtt_info(plant_conf, timezone_time)) {
Ok(state) => { Ok(state) => {
let plant_topic = format!("/plant{}", plant_id + 1); let plant_topic = format!("/plant{}", plant_id + 1);
let _ = board let _ = board
@ -679,7 +689,7 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode {
SntpMode::OFFLINE SntpMode::OFFLINE
} }
}; };
let mqtt_connected = if let Some(_) = board.board_hal.get_config().network.mqtt_url { let mqtt_connected = if board.board_hal.get_config().network.mqtt_url.is_some() {
let nw_config = &board.board_hal.get_config().network.clone(); let nw_config = &board.board_hal.get_config().network.clone();
match board.board_hal.get_esp().mqtt(nw_config) { match board.board_hal.get_esp().mqtt(nw_config) {
Ok(_) => { Ok(_) => {
@ -750,65 +760,59 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
let delay = wait_type.blink_pattern(); let delay = wait_type.blink_pattern();
let mut led_count = 8; let mut led_count = 8;
let mut pattern_step = 0; let mut pattern_step = 0;
let delay_handle = Delay::new_default();
loop { loop {
unsafe { let mut board = BOARD_ACCESS.lock().unwrap();
let mut board = BOARD_ACCESS.lock().unwrap(); update_charge_indicator(&mut board);
if let Ok(charging) = board
.board_hal match wait_type {
.get_battery_monitor() WaitType::MissingConfig => {
.average_current_milli_ampere() // Keep existing behavior: circular filling pattern
{ led_count %= 8;
let _ = board.board_hal.set_charge_indicator(charging > 20); led_count += 1;
} for i in 0..8 {
match wait_type { let _ = board.board_hal.fault(i, i < led_count);
WaitType::MissingConfig => {
// Keep existing behavior: circular filling pattern
led_count %= 8;
led_count += 1;
for i in 0..8 {
let _ = board.board_hal.fault(i, i < led_count);
}
}
WaitType::ConfigButton => {
// Alternating pattern: 1010 1010 -> 0101 0101
pattern_step = (pattern_step + 1) % 2;
for i in 0..8 {
let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0);
}
}
WaitType::MqttConfig => {
// Moving dot pattern
pattern_step = (pattern_step + 1) % 8;
for i in 0..8 {
let _ = board.board_hal.fault(i, i == pattern_step);
}
} }
} }
WaitType::ConfigButton => {
board.board_hal.general_fault(true); // Alternating pattern: 1010 1010 -> 0101 0101
drop(board); pattern_step = (pattern_step + 1) % 2;
vTaskDelay(delay); for i in 0..8 {
let mut board = BOARD_ACCESS.lock().unwrap(); let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0);
board.board_hal.general_fault(false); }
// Clear all LEDs
for i in 0..8 {
let _ = board.board_hal.fault(i, false);
} }
drop(board); WaitType::MqttConfig => {
vTaskDelay(delay); // Moving dot pattern
pattern_step = (pattern_step + 1) % 8;
for i in 0..8 {
let _ = board.board_hal.fault(i, i == pattern_step);
}
}
}
if wait_type == WaitType::MqttConfig board.board_hal.general_fault(true);
&& !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed) drop(board);
{ //cannot use shared delay as that is inside the mutex here
reboot_now.store(true, std::sync::atomic::Ordering::Relaxed); delay_handle.delay_ms(delay);
} let mut board = BOARD_ACCESS.lock().unwrap();
if reboot_now.load(std::sync::atomic::Ordering::Relaxed) { board.board_hal.general_fault(false);
//ensure clean http answer
Delay::new_default().delay_ms(500); // Clear all LEDs
BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1); for i in 0..8 {
} let _ = board.board_hal.fault(i, false);
}
drop(board);
delay_handle.delay_ms(delay);
if wait_type == WaitType::MqttConfig
&& !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed)
{
reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
}
if reboot_now.load(std::sync::atomic::Ordering::Relaxed) {
//ensure clean http answer
Delay::new_default().delay_ms(500);
BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1);
} }
} }
} }
@ -816,7 +820,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
fn main() { fn main() {
let result = safe_main(); let result = safe_main();
match result { match result {
// this should not get triggered, safe_main should not return but go into deep sleep with sensbile // this should not get triggered, safe_main should not return but go into deep sleep with sensible
// timeout, this is just a fallback // timeout, this is just a fallback
Ok(_) => { Ok(_) => {
println!("Main app finished, restarting"); println!("Main app finished, restarting");
@ -838,13 +842,13 @@ fn main() {
} }
pub fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool { pub fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
let curhour = cur.hour() as u8; let current_hour = cur.hour() as u8;
//eg 10-14 //eg 10-14
if start < end { if start < end {
curhour > start && curhour < end current_hour > start && current_hour < end
} else { } else {
//eg 20-05 //eg 20-05
curhour > start || curhour < end current_hour > start || current_hour < end
} }
} }

View File

@ -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)

View File

@ -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,

View File

@ -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>

View File

@ -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">

View File

@ -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" },

View File

@ -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>

View File

@ -29,7 +29,7 @@ export class OTAView {
}; };
test.onclick = () => { test.onclick = () => {
controller.selftest(); controller.selfTest();
} }
} }

View 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>

View 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)
}
}
}
}

View File

@ -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>

View File

@ -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 = '';
} }