Part 1 of V3 extraction in preperation of v4 and v3 software merge #16
| @@ -88,6 +88,7 @@ text-template = "0.1.0" | |||||||
| strum_macros = "0.27.0" | strum_macros = "0.27.0" | ||||||
| esp-ota = { version = "0.2.2", features = ["log"] } | esp-ota = { version = "0.2.2", features = ["log"] } | ||||||
| unit-enum = "1.4.1" | unit-enum = "1.4.1" | ||||||
|  | pca9535 = { version = "2.0.0", features = ["std"] } | ||||||
|  |  | ||||||
|  |  | ||||||
| [patch.crates-io] | [patch.crates-io] | ||||||
|   | |||||||
| @@ -88,11 +88,16 @@ pub enum BoardVersion{ | |||||||
|     V4 |     V4 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | ||||||
|  | pub struct BoardHardware { | ||||||
|  |     pub board: BoardVersion, | ||||||
|  |     pub battery: BatteryBoardVersion, | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | ||||||
| #[serde(default)] | #[serde(default)] | ||||||
| pub struct PlantControllerConfig { | pub struct PlantControllerConfig { | ||||||
|     pub board_hardware: BoardVersion, |     pub hardware: BoardHardware, | ||||||
|     pub battery_hardware: BatteryBoardVersion, |  | ||||||
|     pub network: NetworkConfig, |     pub network: NetworkConfig, | ||||||
|     pub tank: TankConfig, |     pub tank: TankConfig, | ||||||
|     pub night_lamp: NightLampConfig, |     pub night_lamp: NightLampConfig, | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ use esp_idf_svc::sntp::{self, SyncStatus}; | |||||||
| use esp_idf_svc::systime::EspSystemTime; | use esp_idf_svc::systime::EspSystemTime; | ||||||
| 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 one_wire_bus::OneWire; | use one_wire_bus::OneWire; | ||||||
|  | use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; | ||||||
| use crate::config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig}; | use crate::config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig}; | ||||||
| use crate::log::log; | use crate::log::log; | ||||||
| use crate::plant_hal::BoardHal::{Initial, V3, V4}; | use crate::plant_hal::BoardHal::{Initial, V3, V4}; | ||||||
| @@ -78,12 +78,39 @@ const PUMP5_BIT: usize = 5; | |||||||
| const PUMP6_BIT: usize = 6; | const PUMP6_BIT: usize = 6; | ||||||
| const PUMP7_BIT: usize = 7; | const PUMP7_BIT: usize = 7; | ||||||
|  |  | ||||||
| const MS_0: usize = 8; | #[non_exhaustive] | ||||||
| const MS_4: usize = 9; | struct V3Constants; | ||||||
| const MS_2: usize = 10; |  | ||||||
| const MS_3: usize = 11; | impl V3Constants { | ||||||
| const SENSOR_ON: usize = 12; |     const MS_0: usize = 8; | ||||||
| const MS_1: usize = 13; |     const MS_4: usize = 9; | ||||||
|  |     const MS_2: usize = 10; | ||||||
|  |     const MS_3: usize = 11; | ||||||
|  |     const MS_1: usize = 13; | ||||||
|  |     const SENSOR_ON: usize = 12; | ||||||
|  |  | ||||||
|  |     const SENSOR_A_1: u8 = 7; | ||||||
|  |     const SENSOR_A_2: u8 = 6; | ||||||
|  |     const SENSOR_A_3: u8 = 5; | ||||||
|  |     const SENSOR_A_4: u8 = 4; | ||||||
|  |     const SENSOR_A_5: u8 = 3; | ||||||
|  |     const SENSOR_A_6: u8 = 2; | ||||||
|  |     const SENSOR_A_7: u8 = 1; | ||||||
|  |     const SENSOR_A_8: u8 = 0; | ||||||
|  |  | ||||||
|  |     const SENSOR_B_1: u8 = 8; | ||||||
|  |     const SENSOR_B_2: u8 = 9; | ||||||
|  |     const SENSOR_B_3: u8 = 10; | ||||||
|  |     const SENSOR_B_4: u8 = 11; | ||||||
|  |     const SENSOR_B_5: u8 = 12; | ||||||
|  |     const SENSOR_B_6: u8 = 13; | ||||||
|  |     const SENSOR_B_7: u8 = 14; | ||||||
|  |     const SENSOR_B_8: u8 = 15; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| const CHARGING: usize = 14; | const CHARGING: usize = 14; | ||||||
| const AWAKE: usize = 15; | const AWAKE: usize = 15; | ||||||
|  |  | ||||||
| @@ -96,23 +123,7 @@ const FAULT_4: usize = 21; | |||||||
| const FAULT_1: usize = 22; | const FAULT_1: usize = 22; | ||||||
| const FAULT_2: usize = 23; | const FAULT_2: usize = 23; | ||||||
|  |  | ||||||
| const SENSOR_A_1: u8 = 7; |  | ||||||
| const SENSOR_A_2: u8 = 6; |  | ||||||
| const SENSOR_A_3: u8 = 5; |  | ||||||
| const SENSOR_A_4: u8 = 4; |  | ||||||
| const SENSOR_A_5: u8 = 3; |  | ||||||
| const SENSOR_A_6: u8 = 2; |  | ||||||
| const SENSOR_A_7: u8 = 1; |  | ||||||
| const SENSOR_A_8: u8 = 0; |  | ||||||
|  |  | ||||||
| const SENSOR_B_1: u8 = 8; |  | ||||||
| const SENSOR_B_2: u8 = 9; |  | ||||||
| const SENSOR_B_3: u8 = 10; |  | ||||||
| const SENSOR_B_4: u8 = 11; |  | ||||||
| const SENSOR_B_5: u8 = 12; |  | ||||||
| const SENSOR_B_6: u8 = 13; |  | ||||||
| const SENSOR_B_7: u8 = 14; |  | ||||||
| const SENSOR_B_8: u8 = 15; |  | ||||||
|  |  | ||||||
| const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC); | const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC); | ||||||
|  |  | ||||||
| @@ -382,13 +393,9 @@ pub enum BoardHal<'a>{ | |||||||
|     V4 { |     V4 { | ||||||
|         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>, |         solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, | ||||||
|         boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, |  | ||||||
|         signal_counter: PcntDriver<'a>, |         signal_counter: PcntDriver<'a>, | ||||||
|         light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, |         light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, | ||||||
|         main_pump: 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>, | ||||||
|         general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, |  | ||||||
|         wifi_driver: EspWifi<'a>, |  | ||||||
|         one_wire_bus: OneWire<PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>>, |         one_wire_bus: OneWire<PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>>, | ||||||
|         rtc: |         rtc: | ||||||
|             Ds323x<ds323x::interface::I2cInterface<MutexDevice<'a, I2cDriver<'a>>>, ds323x::ic::DS3231>, |             Ds323x<ds323x::interface::I2cInterface<MutexDevice<'a, I2cDriver<'a>>>, ds323x::ic::DS3231>, | ||||||
| @@ -398,6 +405,9 @@ pub enum BoardHal<'a>{ | |||||||
|             eeprom24x::addr_size::TwoBytes, |             eeprom24x::addr_size::TwoBytes, | ||||||
|             eeprom24x::unique_serial::No, |             eeprom24x::unique_serial::No, | ||||||
|         >, |         >, | ||||||
|  |         general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, | ||||||
|  |         pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, | ||||||
|  |         sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -882,9 +892,9 @@ impl BoardInteraction for HAL<'_> { | |||||||
|         unsafe { gpio_hold_en(light.pin()) }; |         unsafe { gpio_hold_en(light.pin()) }; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|     fn pump(& self, plant: usize, enable: bool) -> Result<()> { |     fn pump(&mut self, plant: usize, enable: bool) -> Result<()> { | ||||||
|         match & self.board_hal { |         match &mut self.board_hal { | ||||||
|             BoardHal::V3 { shift_register, .. } => { |             V3 { shift_register, .. } => { | ||||||
|                 let index = match plant { |                 let index = match plant { | ||||||
|                     0 => PUMP1_BIT, |                     0 => PUMP1_BIT, | ||||||
|                     1 => PUMP2_BIT, |                     1 => PUMP2_BIT, | ||||||
| @@ -899,10 +909,14 @@ impl BoardInteraction for HAL<'_> { | |||||||
|                 //currently infallible error, keep for future as result anyway |                 //currently infallible error, keep for future as result anyway | ||||||
|                 shift_register.decompose()[index].set_state(enable.into())?; |                 shift_register.decompose()[index].set_state(enable.into())?; | ||||||
|             } |             } | ||||||
|             BoardHal::V4 { .. } => { |             V4 { pump_expander,  .. } => { | ||||||
|                 bail!("Not yet implemented") |                 if enable { | ||||||
|  |                     pump_expander.pin_set_high(GPIOBank::Bank0, plant.try_into()?)?; | ||||||
|  |                 } else { | ||||||
|  |                     pump_expander.pin_set_low(GPIOBank::Bank0, plant.try_into()?)?; | ||||||
|  |                 } | ||||||
|             }, |             }, | ||||||
|             &plant_hal::BoardHal::Initial { .. } => { |             &mut Initial { .. } => { | ||||||
|                 bail!("Board not configured yet") |                 bail!("Board not configured yet") | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -926,8 +940,8 @@ impl BoardInteraction for HAL<'_> { | |||||||
|     fn consecutive_pump_count(&mut self, plant: usize) -> u32 { |     fn consecutive_pump_count(&mut self, plant: usize) -> u32 { | ||||||
|         unsafe { CONSECUTIVE_WATERING_PLANT[plant] } |         unsafe { CONSECUTIVE_WATERING_PLANT[plant] } | ||||||
|     } |     } | ||||||
|     fn fault(& self, plant: usize, enable: bool) -> Result<()>{ |     fn fault(&mut self, plant: usize, enable: bool) -> Result<()>{ | ||||||
|         match & self.board_hal { |         match &mut self.board_hal { | ||||||
|             V3 { shift_register, .. } => { |             V3 { shift_register, .. } => { | ||||||
|                 let index = match plant { |                 let index = match plant { | ||||||
|                     0 => FAULT_1, |                     0 => FAULT_1, | ||||||
| @@ -942,29 +956,31 @@ impl BoardInteraction for HAL<'_> { | |||||||
|                 }; |                 }; | ||||||
|                 shift_register.decompose()[index] |                 shift_register.decompose()[index] | ||||||
|                     .set_state(enable.into())?; |                     .set_state(enable.into())?; | ||||||
|                 Ok(()) |             } | ||||||
|  |             V4 { pump_expander, .. } => { | ||||||
|  |                 if enable { | ||||||
|  |                     pump_expander.pin_set_high(GPIOBank::Bank1, plant.try_into()?)?; | ||||||
|  |                 } else { | ||||||
|  |                     pump_expander.pin_set_low(GPIOBank::Bank1, plant.try_into()?)?; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|             } |             } | ||||||
|             BoardHal::V4 { .. } => { |             &mut Initial { .. } => { | ||||||
|                 bail!("Not yet implemented") |  | ||||||
|             } |  | ||||||
|             &plant_hal::BoardHal::Initial { .. } => { |  | ||||||
|                 bail!("Board not configured yet") |                 bail!("Board not configured yet") | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         Ok(()) | ||||||
|     } |     } | ||||||
|     fn low_voltage_in_cycle(&mut self) -> bool { |     fn low_voltage_in_cycle(&mut self) -> bool { | ||||||
|         unsafe { LOW_VOLTAGE_DETECTED } |         unsafe { LOW_VOLTAGE_DETECTED } | ||||||
|     } |     } | ||||||
|     fn any_pump(&mut self, enable: bool) -> Result<()> { |     fn any_pump(&mut self, enable: bool) -> Result<()> { | ||||||
|  |  | ||||||
|         match &mut self.board_hal { |         match &mut self.board_hal { | ||||||
|             BoardHal::V3 { main_pump, .. } => { |             V3 { main_pump, .. } => { | ||||||
|                 main_pump.set_state(enable.into())?; |                 main_pump.set_state(enable.into())?; | ||||||
|             } |             } | ||||||
|             BoardHal::V4 { main_pump, .. } => { |             V4 { .. } => { | ||||||
|                 main_pump.set_state(enable.into())?; |                 //does not exist in v4, ignore it | ||||||
|             } |             } | ||||||
|             &mut plant_hal::BoardHal::Initial { .. } => { |             &mut plant_hal::BoardHal::Initial { .. } => { | ||||||
|                 bail!("Board not configured yet") |                 bail!("Board not configured yet") | ||||||
| @@ -1002,39 +1018,39 @@ impl BoardInteraction for HAL<'_> { | |||||||
|                     signal_counter.counter_pause()?; |                     signal_counter.counter_pause()?; | ||||||
|                     signal_counter.counter_clear()?; |                     signal_counter.counter_clear()?; | ||||||
|                     //Disable all |                     //Disable all | ||||||
|                     shift_register.decompose()[MS_4].set_high()?; |                     shift_register.decompose()[V3Constants::MS_4].set_high()?; | ||||||
|  |  | ||||||
|                     let sensor_channel = match sensor { |                     let sensor_channel = match sensor { | ||||||
|                         Sensor::A => match plant { |                         Sensor::A => match plant { | ||||||
|                             0 => SENSOR_A_1, |                             0 => V3Constants::SENSOR_A_1, | ||||||
|                             1 => SENSOR_A_2, |                             1 => V3Constants::SENSOR_A_2, | ||||||
|                             2 => SENSOR_A_3, |                             2 => V3Constants::SENSOR_A_3, | ||||||
|                             3 => SENSOR_A_4, |                             3 => V3Constants::SENSOR_A_4, | ||||||
|                             4 => SENSOR_A_5, |                             4 => V3Constants::SENSOR_A_5, | ||||||
|                             5 => SENSOR_A_6, |                             5 => V3Constants::SENSOR_A_6, | ||||||
|                             6 => SENSOR_A_7, |                             6 => V3Constants::SENSOR_A_7, | ||||||
|                             7 => SENSOR_A_8, |                             7 => V3Constants::SENSOR_A_8, | ||||||
|                             _ => bail!("Invalid plant id {}", plant), |                             _ => bail!("Invalid plant id {}", plant), | ||||||
|                         }, |                         }, | ||||||
|                         Sensor::B => match plant { |                         Sensor::B => match plant { | ||||||
|                             0 => SENSOR_B_1, |                             0 => V3Constants::SENSOR_B_1, | ||||||
|                             1 => SENSOR_B_2, |                             1 => V3Constants::SENSOR_B_2, | ||||||
|                             2 => SENSOR_B_3, |                             2 => V3Constants::SENSOR_B_3, | ||||||
|                             3 => SENSOR_B_4, |                             3 => V3Constants::SENSOR_B_4, | ||||||
|                             4 => SENSOR_B_5, |                             4 => V3Constants::SENSOR_B_5, | ||||||
|                             5 => SENSOR_B_6, |                             5 => V3Constants::SENSOR_B_6, | ||||||
|                             6 => SENSOR_B_7, |                             6 => V3Constants::SENSOR_B_7, | ||||||
|                             7 => SENSOR_B_8, |                             7 => V3Constants::SENSOR_B_8, | ||||||
|                             _ => bail!("Invalid plant id {}", plant), |                             _ => bail!("Invalid plant id {}", plant), | ||||||
|                         }, |                         }, | ||||||
|                     }; |                     }; | ||||||
|  |  | ||||||
|  |  | ||||||
|                     let is_bit_set = |b: u8| -> bool { sensor_channel & (1 << b) != 0 }; |                     let is_bit_set = |b: u8| -> bool { sensor_channel & (1 << b) != 0 }; | ||||||
|                     let pin_0 = &mut shift_register.decompose()[MS_0]; |                     let pin_0 = &mut shift_register.decompose()[V3Constants::MS_0]; | ||||||
|                     let pin_1 = &mut shift_register.decompose()[MS_1]; |                     let pin_1 = &mut shift_register.decompose()[V3Constants::MS_1]; | ||||||
|                     let pin_2 = &mut shift_register.decompose()[MS_2]; |                     let pin_2 = &mut shift_register.decompose()[V3Constants::MS_2]; | ||||||
|                     let pin_3 = &mut shift_register.decompose()[MS_3]; |                     let pin_3 = &mut shift_register.decompose()[V3Constants::MS_3]; | ||||||
|                     if is_bit_set(0) { |                     if is_bit_set(0) { | ||||||
|                         pin_0.set_high()?; |                         pin_0.set_high()?; | ||||||
|                     } else { |                     } else { | ||||||
| @@ -1056,8 +1072,8 @@ impl BoardInteraction for HAL<'_> { | |||||||
|                         pin_3.set_low()?; |                         pin_3.set_low()?; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     shift_register.decompose()[MS_4].set_low()?; |                     shift_register.decompose()[V3Constants::MS_4].set_low()?; | ||||||
|                     shift_register.decompose()[SENSOR_ON].set_high()?; |                     shift_register.decompose()[V3Constants::SENSOR_ON].set_high()?; | ||||||
|  |  | ||||||
|                     let delay = Delay::new_default(); |                     let delay = Delay::new_default(); | ||||||
|                     let measurement = 100; // TODO what is this scaling factor? what is its purpose? |                     let measurement = 100; // TODO what is this scaling factor? what is its purpose? | ||||||
| @@ -1068,8 +1084,8 @@ impl BoardInteraction for HAL<'_> { | |||||||
|                     signal_counter.counter_resume()?; |                     signal_counter.counter_resume()?; | ||||||
|                     delay.delay_ms(measurement); |                     delay.delay_ms(measurement); | ||||||
|                     signal_counter.counter_pause()?; |                     signal_counter.counter_pause()?; | ||||||
|                     shift_register.decompose()[MS_4].set_high()?; |                     shift_register.decompose()[V3Constants::MS_4].set_high()?; | ||||||
|                     shift_register.decompose()[SENSOR_ON].set_low()?; |                     shift_register.decompose()[V3Constants::SENSOR_ON].set_low()?; | ||||||
|                     delay.delay_ms(10); |                     delay.delay_ms(10); | ||||||
|                     let unscaled = signal_counter.get_counter_value()? as i32; |                     let unscaled = signal_counter.get_counter_value()? as i32; | ||||||
|                     let hz = unscaled as f32 * factor; |                     let hz = unscaled as f32 * factor; | ||||||
| @@ -1088,6 +1104,91 @@ impl BoardInteraction for HAL<'_> { | |||||||
|                 let median = results[mid]; |                 let median = results[mid]; | ||||||
|                 Ok(median) |                 Ok(median) | ||||||
|             }, |             }, | ||||||
|  |             V4 {sensor_expander, signal_counter, ..} => { | ||||||
|  |                 let mut results = [0_f32; REPEAT_MOIST_MEASURE]; | ||||||
|  |                 for repeat in 0..REPEAT_MOIST_MEASURE { | ||||||
|  |                     signal_counter.counter_pause()?; | ||||||
|  |                     signal_counter.counter_clear()?; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                     const MS0: u8 = 1_u8; | ||||||
|  |                     const MS1: u8 = 0_u8; | ||||||
|  |                     const MS2: u8 = 3_u8; | ||||||
|  |                     const MS3: u8 = 4_u8; | ||||||
|  |                     const MS4: u8 = 2_u8; | ||||||
|  |                     const SENSOR_ON: u8 = 5_u8; | ||||||
|  |                     //Disable all | ||||||
|  |                     sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?; | ||||||
|  |  | ||||||
|  |                     let sensor_channel = match sensor { | ||||||
|  |                         Sensor::A =>{ | ||||||
|  |                             plant as u32 | ||||||
|  |                         }, | ||||||
|  |                         Sensor::B => { | ||||||
|  |                             (15 - plant) as u32 | ||||||
|  |                         }, | ||||||
|  |                     }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                     let is_bit_set = |b: u8| -> bool { sensor_channel & (1 << b) != 0 }; | ||||||
|  |                     if is_bit_set(0) { | ||||||
|  |                         sensor_expander.pin_set_high(GPIOBank::Bank0, MS0)?; | ||||||
|  |                     } else { | ||||||
|  |                         sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?; | ||||||
|  |                     } | ||||||
|  |                     if is_bit_set(1) { | ||||||
|  |                         sensor_expander.pin_set_high(GPIOBank::Bank0, MS1)?; | ||||||
|  |                     } else { | ||||||
|  |                         sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?; | ||||||
|  |                     } | ||||||
|  |                     if is_bit_set(2) { | ||||||
|  |                         sensor_expander.pin_set_high(GPIOBank::Bank0, MS2)?; | ||||||
|  |                     } else { | ||||||
|  |                         sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?; | ||||||
|  |                     } | ||||||
|  |                     if is_bit_set(3) { | ||||||
|  |                         sensor_expander.pin_set_high(GPIOBank::Bank0, MS3)?; | ||||||
|  |                     } else { | ||||||
|  |                         sensor_expander.pin_set_low(GPIOBank::Bank0, MS3)?; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS4)?; | ||||||
|  |                     sensor_expander.pin_set_high(GPIOBank::Bank0, SENSOR_ON)?; | ||||||
|  |  | ||||||
|  |                     let delay = Delay::new_default(); | ||||||
|  |                     let measurement = 100; // TODO what is this scaling factor? what is its purpose? | ||||||
|  |                     let factor = 1000f32 / measurement as f32; | ||||||
|  |  | ||||||
|  |                     //give some time to stabilize | ||||||
|  |                     delay.delay_ms(10); | ||||||
|  |                     signal_counter.counter_resume()?; | ||||||
|  |                     delay.delay_ms(measurement); | ||||||
|  |                     signal_counter.counter_pause()?; | ||||||
|  |                     sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?; | ||||||
|  |                     sensor_expander.pin_set_low(GPIOBank::Bank0, SENSOR_ON)?; | ||||||
|  |                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS0); | ||||||
|  |                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS1); | ||||||
|  |                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS2); | ||||||
|  |                     sensor_expander.pin_set_low(GPIOBank::Bank0, MS3); | ||||||
|  |                     delay.delay_ms(10); | ||||||
|  |                     let unscaled = signal_counter.get_counter_value()? as i32; | ||||||
|  |                     let hz = unscaled as f32 * factor; | ||||||
|  |                     log( | ||||||
|  |                         LogMessage::RawMeasure, | ||||||
|  |                         unscaled as u32, | ||||||
|  |                         hz as u32, | ||||||
|  |                         &plant.to_string(), | ||||||
|  |                         &format!("{sensor:?}"), | ||||||
|  |                     ); | ||||||
|  |                     results[repeat] = hz; | ||||||
|  |                 } | ||||||
|  |                 results.sort_by(|a, b| a.partial_cmp(b).unwrap()); // floats don't seem to implement total_ord | ||||||
|  |  | ||||||
|  |                 let mid = results.len() / 2; | ||||||
|  |                 let median = results[mid]; | ||||||
|  |                 Ok(median) | ||||||
|  |             } | ||||||
|             _ => { |             _ => { | ||||||
|                 bail!("Not implemented for this board") |                 bail!("Not implemented for this board") | ||||||
|             } |             } | ||||||
| @@ -1534,12 +1635,12 @@ pub trait BoardInteraction { | |||||||
|     fn set_low_voltage_in_cycle(&mut self); |     fn set_low_voltage_in_cycle(&mut self); | ||||||
|     fn clear_low_voltage_in_cycle(&mut self); |     fn clear_low_voltage_in_cycle(&mut self); | ||||||
|     fn light(&mut self, enable: bool) -> Result<()>; |     fn light(&mut self, enable: bool) -> Result<()>; | ||||||
|     fn pump(&self, plant: usize, enable: bool) -> Result<()>; |     fn pump(&mut self, plant: usize, enable: bool) -> Result<()>; | ||||||
|     fn last_pump_time(&self, plant: usize) -> Option<DateTime<Utc>>; |     fn last_pump_time(&self, plant: usize) -> Option<DateTime<Utc>>; | ||||||
|     fn store_last_pump_time(&mut self, plant: usize, time: DateTime<Utc>); |     fn store_last_pump_time(&mut self, plant: usize, time: DateTime<Utc>); | ||||||
|     fn store_consecutive_pump_count(&mut self, plant: usize, count: u32); |     fn store_consecutive_pump_count(&mut self, plant: usize, count: u32); | ||||||
|     fn consecutive_pump_count(&mut self, plant: usize) -> u32; |     fn consecutive_pump_count(&mut self, plant: usize) -> u32; | ||||||
|     fn fault(&self, plant: usize, enable: bool) -> Result<()>; |     fn fault(&mut self, plant: usize, enable: bool) -> Result<()>; | ||||||
|     fn low_voltage_in_cycle(&mut self) -> bool; |     fn low_voltage_in_cycle(&mut self) -> bool; | ||||||
|     fn any_pump(&mut self, enable: bool) -> Result<()>; |     fn any_pump(&mut self, enable: bool) -> Result<()>; | ||||||
|     fn time(&mut self) -> Result<DateTime<Utc>>; |     fn time(&mut self) -> Result<DateTime<Utc>>; | ||||||
| @@ -1771,8 +1872,7 @@ impl PlantHal { | |||||||
|         let config = esp.get_config(); |         let config = esp.get_config(); | ||||||
|         let hal = match config { |         let hal = match config { | ||||||
|             Result::Ok(config) => { |             Result::Ok(config) => { | ||||||
|                 let board_hal : BoardHal = match config.board_hardware { |                 let board_hal : BoardHal = match config.hardware.board { | ||||||
|  |  | ||||||
|                     BoardVersion::INITIAL => { |                     BoardVersion::INITIAL => { | ||||||
|                         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)?; | ||||||
| @@ -1787,7 +1887,7 @@ impl PlantHal { | |||||||
|                     BoardVersion::V4 => {PlantHal::create_v4(free_pins)?} |                     BoardVersion::V4 => {PlantHal::create_v4(free_pins)?} | ||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 let battery_monitor : BatteryMonitor = match config.battery_hardware { |                 let battery_monitor : BatteryMonitor = match config.hardware.battery { | ||||||
|                     BatteryBoardVersion::Disabled => { BatteryMonitor::Disabled {}} |                     BatteryBoardVersion::Disabled => { BatteryMonitor::Disabled {}} | ||||||
|                     BatteryBoardVersion::BQ34Z100G1 => { |                     BatteryBoardVersion::BQ34Z100G1 => { | ||||||
|                         let mut battery_driver = Bq34z100g1Driver { |                         let mut battery_driver = Bq34z100g1Driver { | ||||||
| @@ -1849,15 +1949,124 @@ impl PlantHal { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn create_v4(peripherals: FreePeripherals) -> Result<BoardHal<'static>> { |     fn create_v4(peripherals: FreePeripherals) -> Result<BoardHal<'static>> { | ||||||
|  |         let mut awake = PinDriver::output(peripherals.gpio15.downgrade())?; | ||||||
|  |         awake.set_high()?; | ||||||
|  |  | ||||||
|         let mut general_fault = PinDriver::input_output(peripherals.gpio6.downgrade())?; |         let mut general_fault = PinDriver::input_output(peripherals.gpio6.downgrade())?; | ||||||
|         general_fault.set_pull(Pull::Floating)?; |         general_fault.set_pull(Pull::Floating)?; | ||||||
|         general_fault.set_low()?; |         general_fault.set_low()?; | ||||||
|  |  | ||||||
|  |  | ||||||
|         //temp remove me |  | ||||||
|         general_fault.set_high()?; |  | ||||||
|  |  | ||||||
|         bail!("not implemented"); |         println!("Init rtc driver"); | ||||||
|  |         let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER)); | ||||||
|  |  | ||||||
|  |         println!("Init rtc eeprom driver"); | ||||||
|  |         let mut eeprom = { | ||||||
|  |             Eeprom24x::new_24x32( | ||||||
|  |                 MutexDevice::new(&I2C_DRIVER), | ||||||
|  |                 SlaveAddr::Alternative(true, true, true), | ||||||
|  |             ) | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         let mut one_wire_pin = PinDriver::input_output_od(peripherals.gpio18.downgrade())?; | ||||||
|  |         one_wire_pin.set_pull(Pull::Floating)?; | ||||||
|  |  | ||||||
|  |         let one_wire_bus = OneWire::new(one_wire_pin) | ||||||
|  |             .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         let rtc_time = rtc.datetime(); | ||||||
|  |         match rtc_time { | ||||||
|  |             OkStd(tt) => { | ||||||
|  |                 println!("Rtc Module reports time at UTC {}", tt); | ||||||
|  |             } | ||||||
|  |             Err(err) => { | ||||||
|  |                 println!("Rtc Module could not be read {:?}", err); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         match eeprom.read_byte(0) { | ||||||
|  |             OkStd(byte) => { | ||||||
|  |                 println!("Read first byte with status {}", byte); | ||||||
|  |             } | ||||||
|  |             Err(err) => { | ||||||
|  |                 println!("Eeprom could not read first byte {:?}", err); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         let mut signal_counter = PcntDriver::new( | ||||||
|  |             peripherals.pcnt0, | ||||||
|  |             Some(peripherals.gpio22), | ||||||
|  |             Option::<AnyInputPin>::None, | ||||||
|  |             Option::<AnyInputPin>::None, | ||||||
|  |             Option::<AnyInputPin>::None, | ||||||
|  |         )?; | ||||||
|  |  | ||||||
|  |         signal_counter.channel_config( | ||||||
|  |             PcntChannel::Channel0, | ||||||
|  |             PinIndex::Pin0, | ||||||
|  |             PinIndex::Pin1, | ||||||
|  |             &PcntChannelConfig { | ||||||
|  |                 lctrl_mode: PcntControlMode::Keep, | ||||||
|  |                 hctrl_mode: PcntControlMode::Keep, | ||||||
|  |                 pos_mode: PcntCountMode::Increment, | ||||||
|  |                 neg_mode: PcntCountMode::Hold, | ||||||
|  |                 counter_h_lim: i16::MAX, | ||||||
|  |                 counter_l_lim: 0, | ||||||
|  |             }, | ||||||
|  |         )?; | ||||||
|  |  | ||||||
|  |         let adc_config = AdcChannelConfig { | ||||||
|  |             attenuation: attenuation::DB_11, | ||||||
|  |             resolution: Resolution::Resolution12Bit, | ||||||
|  |             calibration: esp_idf_hal::adc::oneshot::config::Calibration::Curve, | ||||||
|  |         }; | ||||||
|  |         let tank_driver = AdcDriver::new(peripherals.adc1)?; | ||||||
|  |         let tank_channel: AdcChannelDriver<Gpio5, AdcDriver<esp_idf_hal::adc::ADC1>> = | ||||||
|  |             AdcChannelDriver::new(tank_driver, peripherals.gpio5, &adc_config)?; | ||||||
|  |  | ||||||
|  |         let mut solar_is_day = PinDriver::input(peripherals.gpio7.downgrade())?; | ||||||
|  |         solar_is_day.set_pull(Pull::Floating)?; | ||||||
|  |  | ||||||
|  |         let mut light = PinDriver::input_output(peripherals.gpio10.downgrade())?; | ||||||
|  |         light.set_pull(Pull::Floating)?; | ||||||
|  |  | ||||||
|  |         let mut tank_power = PinDriver::input_output(peripherals.gpio11.downgrade())?; | ||||||
|  |         tank_power.set_pull(Pull::Floating)?; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         let mut pump_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 32); | ||||||
|  |  | ||||||
|  |         //todo error handing if init error | ||||||
|  |         for pin in 0..8{ | ||||||
|  |             let _ = pump_expander.pin_into_output(GPIOBank::Bank0, pin); | ||||||
|  |             let _ = pump_expander.pin_into_output(GPIOBank::Bank1, pin); | ||||||
|  |             let _ = pump_expander.pin_set_low(GPIOBank::Bank0, pin); | ||||||
|  |             let _ = pump_expander.pin_set_low(GPIOBank::Bank1, pin); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let mut sensor_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 34); | ||||||
|  |         for pin in 0..8{ | ||||||
|  |             let _ = sensor_expander.pin_into_output(GPIOBank::Bank0, pin); | ||||||
|  |             let _ = sensor_expander.pin_into_output(GPIOBank::Bank1, pin); | ||||||
|  |             let _ = sensor_expander.pin_set_low(GPIOBank::Bank0, pin); | ||||||
|  |             let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Ok(V4 { | ||||||
|  |             tank_channel, | ||||||
|  |             solar_is_day, | ||||||
|  |             signal_counter, | ||||||
|  |             light, | ||||||
|  |             tank_power, | ||||||
|  |             one_wire_bus, | ||||||
|  |             rtc, | ||||||
|  |             eeprom, | ||||||
|  |             general_fault, | ||||||
|  |             pump_expander, | ||||||
|  |             sensor_expander | ||||||
|  |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn create_v3(peripherals: FreePeripherals) -> Result<BoardHal<'static>> { |     fn create_v3(peripherals: FreePeripherals) -> Result<BoardHal<'static>> { | ||||||
| @@ -1879,16 +2088,16 @@ impl PlantHal { | |||||||
|         let charging = &mut shift_register.decompose()[CHARGING]; |         let charging = &mut shift_register.decompose()[CHARGING]; | ||||||
|         charging.set_high()?; |         charging.set_high()?; | ||||||
|  |  | ||||||
|         let ms0 = &mut shift_register.decompose()[MS_0]; |         let ms0 = &mut shift_register.decompose()[V3Constants::MS_0]; | ||||||
|         ms0.set_low()?; |         ms0.set_low()?; | ||||||
|         let ms1 = &mut shift_register.decompose()[MS_1]; |         let ms1 = &mut shift_register.decompose()[V3Constants::MS_1]; | ||||||
|         ms1.set_low()?; |         ms1.set_low()?; | ||||||
|         let ms2 = &mut shift_register.decompose()[MS_2]; |         let ms2 = &mut shift_register.decompose()[V3Constants::MS_2]; | ||||||
|         ms2.set_low()?; |         ms2.set_low()?; | ||||||
|         let ms3 = &mut shift_register.decompose()[MS_3]; |         let ms3 = &mut shift_register.decompose()[V3Constants::MS_3]; | ||||||
|         ms3.set_low()?; |         ms3.set_low()?; | ||||||
|  |  | ||||||
|         let ms4 = &mut shift_register.decompose()[MS_4]; |         let ms4 = &mut shift_register.decompose()[V3Constants::MS_4]; | ||||||
|         ms4.set_high()?; |         ms4.set_high()?; | ||||||
|  |  | ||||||
|         println!("Init battery driver"); |         println!("Init battery driver"); | ||||||
|   | |||||||
| @@ -216,6 +216,7 @@ fn set_config( | |||||||
| ) -> 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(3072))?; | ||||||
|     let config: PlantControllerConfig = serde_json::from_slice(&all)?; |     let config: PlantControllerConfig = serde_json::from_slice(&all)?; | ||||||
|  |  | ||||||
|     let mut board = BOARD_ACCESS.lock().unwrap(); |     let mut board = BOARD_ACCESS.lock().unwrap(); | ||||||
|     board.esp.set_config(&config)?; |     board.esp.set_config(&config)?; | ||||||
|  |  | ||||||
| @@ -525,7 +526,7 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> { | |||||||
|     server |     server | ||||||
|         .fn_handler("/file", Method::Post, move |mut request| { |         .fn_handler("/file", Method::Post, move |mut request| { | ||||||
|             let filename = query_param(request.uri(), "filename").unwrap(); |             let filename = query_param(request.uri(), "filename").unwrap(); | ||||||
|             let lock = BOARD_ACCESS.lock().unwrap(); |             let mut lock = BOARD_ACCESS.lock().unwrap(); | ||||||
|             let file_handle = lock.esp.get_file_handle(&filename, true); |             let file_handle = lock.esp.get_file_handle(&filename, true); | ||||||
|             match file_handle { |             match file_handle { | ||||||
|                 //TODO get free filesystem size, check against during write if not to large |                 //TODO get free filesystem size, check against during write if not to large | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| interface LogArray  extends Array<LogEntry>{} | export interface LogArray  extends Array<LogEntry>{} | ||||||
|  |  | ||||||
| interface LogEntry { | export interface LogEntry { | ||||||
|   timestamp: string, |   timestamp: string, | ||||||
|   message_id: number, |   message_id: number, | ||||||
|   a: number, |   a: number, | ||||||
| @@ -9,27 +9,28 @@ interface LogEntry { | |||||||
|   txt_long: string |   txt_long: string | ||||||
| } | } | ||||||
|  |  | ||||||
| interface LogLocalisation extends Array<LogLocalisationEntry>{} | export interface LogLocalisation extends Array<LogLocalisationEntry>{} | ||||||
|  |  | ||||||
| interface LogLocalisationEntry { | export interface LogLocalisationEntry { | ||||||
|   msg_type: string, |   msg_type: string, | ||||||
|   message: string |   message: string | ||||||
| } | } | ||||||
|  |  | ||||||
| interface BackupHeader { | export interface BackupHeader { | ||||||
|   timestamp: string, |   timestamp: string, | ||||||
|   size: number |   size: number | ||||||
| } | } | ||||||
|  |  | ||||||
| interface NetworkConfig { | export interface NetworkConfig { | ||||||
|   ap_ssid: string, |   ap_ssid: string, | ||||||
|   ssid: string, |   ssid: string, | ||||||
|   password: string, |   password: string, | ||||||
|   mqtt_url: string, |   mqtt_url: string, | ||||||
|   base_topic: string |   base_topic: string, | ||||||
|  |   max_wait: number | ||||||
| } | } | ||||||
|  |  | ||||||
| interface FileList { | export interface FileList { | ||||||
|   total: number, |   total: number, | ||||||
|   used: number, |   used: number, | ||||||
|   files: FileInfo[], |   files: FileInfo[], | ||||||
| @@ -37,12 +38,12 @@ interface FileList { | |||||||
|   iter_error: string, |   iter_error: string, | ||||||
| } | } | ||||||
|  |  | ||||||
| interface FileInfo{ | export interface FileInfo{ | ||||||
|   filename: string, |   filename: string, | ||||||
|   size: number, |   size: number, | ||||||
| } | } | ||||||
|  |  | ||||||
| interface NightLampConfig { | export interface NightLampConfig { | ||||||
|   enabled: boolean, |   enabled: boolean, | ||||||
|   night_lamp_hour_start: number, |   night_lamp_hour_start: number, | ||||||
|   night_lamp_hour_end: number, |   night_lamp_hour_end: number, | ||||||
| @@ -51,11 +52,11 @@ interface NightLampConfig { | |||||||
|   low_soc_restore: number |   low_soc_restore: number | ||||||
| } | } | ||||||
|  |  | ||||||
| interface NightLampCommand { | export interface NightLampCommand { | ||||||
|   active: boolean |   active: boolean | ||||||
| } | } | ||||||
|  |  | ||||||
| interface TankConfig { | export interface TankConfig { | ||||||
|   tank_sensor_enabled: boolean, |   tank_sensor_enabled: boolean, | ||||||
|   tank_allow_pumping_if_sensor_error: boolean, |   tank_allow_pumping_if_sensor_error: boolean, | ||||||
|   tank_useable_ml: number, |   tank_useable_ml: number, | ||||||
| @@ -64,7 +65,26 @@ interface TankConfig { | |||||||
|   tank_full_percent: number, |   tank_full_percent: number, | ||||||
| } | } | ||||||
|  |  | ||||||
| interface PlantControllerConfig { |  | ||||||
|  | export enum BatteryBoardVersion { | ||||||
|  |   Disabled = "Disabled", | ||||||
|  |   BQ34Z100G1 = "BQ34Z100G1", | ||||||
|  |   WchI2cSlave = "WchI2cSlave" | ||||||
|  | } | ||||||
|  | export enum BoardVersion{ | ||||||
|  |     INITIAL = "INITIAL", | ||||||
|  |     V3 = "V3", | ||||||
|  |     V4 = "V4" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface BoardHardware { | ||||||
|  |   board: BoardVersion, | ||||||
|  |   battery: BatteryBoardVersion, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface PlantControllerConfig { | ||||||
|  |   hardware: BoardHardware, | ||||||
|  |  | ||||||
|   network: NetworkConfig, |   network: NetworkConfig, | ||||||
|   tank: TankConfig, |   tank: TankConfig, | ||||||
|   night_lamp: NightLampConfig, |   night_lamp: NightLampConfig, | ||||||
| @@ -72,7 +92,7 @@ interface PlantControllerConfig { | |||||||
|   timezone?: string, |   timezone?: string, | ||||||
| } | } | ||||||
|  |  | ||||||
| interface PlantConfig { | export interface PlantConfig { | ||||||
|   mode: string, |   mode: string, | ||||||
|   target_moisture: number, |   target_moisture: number, | ||||||
|   pump_time_s: number, |   pump_time_s: number, | ||||||
| @@ -88,35 +108,35 @@ interface PlantConfig { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| interface SSIDList { | export interface SSIDList { | ||||||
|   ssids: [string] |   ssids: [string] | ||||||
| } | } | ||||||
|  |  | ||||||
| interface TestPump { | export interface TestPump { | ||||||
|   pump: number |   pump: number | ||||||
| } | } | ||||||
|  |  | ||||||
| interface SetTime { | export interface SetTime { | ||||||
|   time: string |   time: string | ||||||
| } | } | ||||||
|  |  | ||||||
| interface GetTime { | export interface GetTime { | ||||||
|   rtc: string, |   rtc: string, | ||||||
|   native: string |   native: string | ||||||
| } | } | ||||||
|  |  | ||||||
| interface Moistures { | export interface Moistures { | ||||||
|   moisture_a: [string], |   moisture_a: [string], | ||||||
|   moisture_b: [string], |   moisture_b: [string], | ||||||
| } | } | ||||||
|  |  | ||||||
| interface VersionInfo { | export interface VersionInfo { | ||||||
|   git_hash: string, |   git_hash: string, | ||||||
|   build_time: string, |   build_time: string, | ||||||
|   partition: string |   partition: string | ||||||
| } | } | ||||||
|  |  | ||||||
| interface BatteryState { | export interface BatteryState { | ||||||
|   temperature: string |   temperature: string | ||||||
|   voltage_milli_volt: string, |   voltage_milli_volt: string, | ||||||
|   current_milli_ampere: string, |   current_milli_ampere: string, | ||||||
| @@ -127,7 +147,7 @@ interface BatteryState { | |||||||
|   state_of_health: string |   state_of_health: string | ||||||
| } | } | ||||||
|  |  | ||||||
| interface TankInfo { | export interface TankInfo { | ||||||
|   /// is there enough water in the tank |   /// is there enough water in the tank | ||||||
|   enough_water: boolean, |   enough_water: boolean, | ||||||
|   /// warning that water needs to be refilled soon |   /// warning that water needs to be refilled soon | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { Controller } from "./main"; | import { Controller } from "./main"; | ||||||
|  | import {BatteryState} from "./api"; | ||||||
|  |  | ||||||
| export class BatteryView{ | export class BatteryView{ | ||||||
|     voltage_milli_volt: HTMLSpanElement; |     voltage_milli_volt: HTMLSpanElement; | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import {Controller} from "./main"; | import {Controller} from "./main"; | ||||||
|  | import {FileInfo, FileList} from "./api"; | ||||||
| const regex = /[^a-zA-Z0-9_.]/g; | const regex = /[^a-zA-Z0-9_.]/g; | ||||||
|  |  | ||||||
| function sanitize(str:string){ | function sanitize(str:string){ | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								rust/src_webpack/src/hardware.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								rust/src_webpack/src/hardware.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | <style> | ||||||
|  |   .boardkey{ | ||||||
|  |     min-width: 200px; | ||||||
|  |   } | ||||||
|  |   .boardvalue{ | ||||||
|  |     flex-grow: 1; | ||||||
|  |   } | ||||||
|  | </style> | ||||||
|  |  | ||||||
|  | <div class="subtitle">Hardware:</div> | ||||||
|  | <div class="flexcontainer">  | ||||||
|  |   <div class="boardkey">BoardRevision</div> | ||||||
|  |     <select class="boardvalue" id="hardware_board_value"> | ||||||
|  |     </select> | ||||||
|  | </div> | ||||||
|  | <div class="flexcontainer" style="text-decoration-line: line-through;"> | ||||||
|  |     <div class="boardkey">BatteryMonitor</div> | ||||||
|  |     <select class="boardvalue" id="hardware_battery_value"> | ||||||
|  |     </select> | ||||||
|  | </div> | ||||||
							
								
								
									
										45
									
								
								rust/src_webpack/src/hardware.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								rust/src_webpack/src/hardware.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | import { Controller } from "./main"; | ||||||
|  | import {BatteryBoardVersion, BoardHardware, BoardVersion} from "./api"; | ||||||
|  |  | ||||||
|  | export class HardwareConfigView { | ||||||
|  |     private readonly hardware_board_value: HTMLSelectElement; | ||||||
|  |     private readonly hardware_battery_value: HTMLSelectElement; | ||||||
|  |     constructor(controller:Controller){ | ||||||
|  |       (document.getElementById("hardwareview") as HTMLElement).innerHTML = require('./hardware.html') as string; | ||||||
|  |  | ||||||
|  |       this.hardware_board_value = document.getElementById("hardware_board_value") as HTMLSelectElement; | ||||||
|  |       this.hardware_board_value.onchange = controller.configChanged | ||||||
|  |  | ||||||
|  |       Object.keys(BoardVersion).forEach(version => { | ||||||
|  |         let option = document.createElement("option"); | ||||||
|  |         if (version == BoardVersion.INITIAL.toString()){ | ||||||
|  |           option.selected = true | ||||||
|  |         } | ||||||
|  |         option.innerText = version.toString(); | ||||||
|  |         this.hardware_board_value.appendChild(option); | ||||||
|  |       }) | ||||||
|  |  | ||||||
|  |       this.hardware_battery_value = document.getElementById("hardware_battery_value") as HTMLSelectElement; | ||||||
|  |       this.hardware_battery_value.onchange = controller.configChanged | ||||||
|  |       Object.keys(BatteryBoardVersion).forEach(version => { | ||||||
|  |         let option = document.createElement("option"); | ||||||
|  |         if (version == BatteryBoardVersion.Disabled.toString()){ | ||||||
|  |           option.selected = true | ||||||
|  |         } | ||||||
|  |         option.innerText = version.toString(); | ||||||
|  |         this.hardware_battery_value.appendChild(option); | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  |    | ||||||
|  |     setConfig(hardware: BoardHardware) { | ||||||
|  |       this.hardware_board_value.value = hardware.board.toString() | ||||||
|  |       this.hardware_battery_value.value = hardware.battery.toString() | ||||||
|  |     } | ||||||
|  |    | ||||||
|  |     getConfig(): BoardHardware { | ||||||
|  |       return { | ||||||
|  |         board :  BoardVersion[this.hardware_board_value.value as keyof typeof BoardVersion], | ||||||
|  |         battery :  BatteryBoardVersion[this.hardware_battery_value.value as keyof typeof BatteryBoardVersion], | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { Controller } from "./main"; | import { Controller } from "./main"; | ||||||
|  | import {LogArray, LogLocalisation} from "./api"; | ||||||
|  |  | ||||||
| export class LogView { | export class LogView { | ||||||
|   private readonly logpanel: HTMLElement; |   private readonly logpanel: HTMLElement; | ||||||
|   | |||||||
| @@ -138,6 +138,10 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| <div class="container-xl"> | <div class="container-xl"> | ||||||
|  |   <div style="display:flex; flex-wrap: wrap;"> | ||||||
|  |     <div id="hardwareview" class="subcontainer"></div> | ||||||
|  |   </div> | ||||||
|  |  | ||||||
|   <div style="display:flex; flex-wrap: wrap;"> |   <div style="display:flex; flex-wrap: wrap;"> | ||||||
|     <div id="firmwareview" class="subcontainer"> |     <div id="firmwareview" class="subcontainer"> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -17,6 +17,19 @@ import { OTAView } from "./ota"; | |||||||
| import { BatteryView } from "./batteryview"; | import { BatteryView } from "./batteryview"; | ||||||
| import { FileView } from './fileview'; | import { FileView } from './fileview'; | ||||||
| import { LogView } from './log'; | import { LogView } from './log'; | ||||||
|  | import {HardwareConfigView} from "./hardware"; | ||||||
|  | import { | ||||||
|  |   BackupHeader, | ||||||
|  |   BatteryState, | ||||||
|  |   GetTime, LogArray, LogLocalisation, | ||||||
|  |   Moistures, | ||||||
|  |   NightLampCommand, | ||||||
|  |   PlantControllerConfig, | ||||||
|  |   SetTime, SSIDList, TankInfo, | ||||||
|  |   TestPump, | ||||||
|  |   VersionInfo, | ||||||
|  |   FileList | ||||||
|  | } from "./api"; | ||||||
|  |  | ||||||
| export class Controller { | export class Controller { | ||||||
|   loadTankInfo() : Promise<void> { |   loadTankInfo() : Promise<void> { | ||||||
| @@ -66,7 +79,7 @@ export class Controller { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   populateTimezones(): Promise<void> { |   populateTimezones(): Promise<void> { | ||||||
|     return fetch('/timezones') |     return fetch(PUBLIC_URL+'/timezones') | ||||||
|         .then(response => response.json()) |         .then(response => response.json()) | ||||||
|         .then(json => json as string[]) |         .then(json => json as string[]) | ||||||
|         .then(timezones => { |         .then(timezones => { | ||||||
| @@ -268,6 +281,12 @@ export class Controller { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   selfTest(){ | ||||||
|  |     fetch(PUBLIC_URL + "/boardtest", { | ||||||
|  |       method: "POST" | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  |  | ||||||
|   testNightLamp(active: boolean){ |   testNightLamp(active: boolean){ | ||||||
|     var body: NightLampCommand = { |     var body: NightLampCommand = { | ||||||
|       active: active |       active: active | ||||||
| @@ -313,6 +332,7 @@ export class Controller { | |||||||
|  |  | ||||||
|   getConfig(): PlantControllerConfig { |   getConfig(): PlantControllerConfig { | ||||||
|     return { |     return { | ||||||
|  |       hardware: controller.hardwareView.getConfig(), | ||||||
|       network: controller.networkView.getConfig(), |       network: controller.networkView.getConfig(), | ||||||
|       tank: controller.tankView.getConfig(), |       tank: controller.tankView.getConfig(), | ||||||
|       night_lamp: controller.nightLampView.getConfig(), |       night_lamp: controller.nightLampView.getConfig(), | ||||||
| @@ -360,6 +380,7 @@ export class Controller { | |||||||
|     this.nightLampView.setConfig(current.night_lamp); |     this.nightLampView.setConfig(current.night_lamp); | ||||||
|     this.plantViews.setConfig(current.plants); |     this.plantViews.setConfig(current.plants); | ||||||
|     this.timeView.setTimeZone(current.timezone); |     this.timeView.setTimeZone(current.timezone); | ||||||
|  |     this.hardwareView.setConfig(current.hardware); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   measure_moisture() { |   measure_moisture() { | ||||||
| @@ -437,6 +458,7 @@ export class Controller { | |||||||
|   readonly timeView: TimeView; |   readonly timeView: TimeView; | ||||||
|   readonly plantViews: PlantViews; |   readonly plantViews: PlantViews; | ||||||
|   readonly networkView: NetworkConfigView; |   readonly networkView: NetworkConfigView; | ||||||
|  |   readonly hardwareView: HardwareConfigView; | ||||||
|   readonly tankView: TankConfigView; |   readonly tankView: TankConfigView; | ||||||
|   readonly nightLampView: NightLampView; |   readonly nightLampView: NightLampView; | ||||||
|   readonly submitView: SubmitView; |   readonly submitView: SubmitView; | ||||||
| @@ -457,6 +479,7 @@ export class Controller { | |||||||
|     this.progressview = new ProgressView(this) |     this.progressview = new ProgressView(this) | ||||||
|     this.fileview = new FileView(this) |     this.fileview = new FileView(this) | ||||||
|     this.logView = new LogView(this) |     this.logView = new LogView(this) | ||||||
|  |     this.hardwareView = new HardwareConfigView(this) | ||||||
|     this.rebootBtn = document.getElementById("reboot") as HTMLButtonElement |     this.rebootBtn = document.getElementById("reboot") as HTMLButtonElement | ||||||
|     this.rebootBtn.onclick = () => { |     this.rebootBtn.onclick = () => { | ||||||
|       controller.reboot(); |       controller.reboot(); | ||||||
| @@ -466,6 +489,10 @@ export class Controller { | |||||||
|       controller.exit(); |       controller.exit(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   selftest() { | ||||||
|  |  | ||||||
|  |   } | ||||||
| } | } | ||||||
| const controller = new Controller(); | const controller = new Controller(); | ||||||
| controller.progressview.removeProgress("rebooting"); | controller.progressview.removeProgress("rebooting"); | ||||||
| @@ -505,9 +532,6 @@ executeTasksSequentially().then(r => { | |||||||
|   controller.progressview.removeProgress("initial") |   controller.progressview.removeProgress("initial") | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| controller.progressview.removeProgress("rebooting"); | controller.progressview.removeProgress("rebooting"); | ||||||
|  |  | ||||||
| window.addEventListener("beforeunload", (event) => { | window.addEventListener("beforeunload", (event) => { | ||||||
|   | |||||||
| @@ -43,6 +43,11 @@ | |||||||
|                 <input class="basicnetworkkeyssid2" type="button" id="scan" value="Scan"> |                 <input class="basicnetworkkeyssid2" type="button" id="scan" value="Scan"> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|  |             <div class="flexcontainer"> | ||||||
|  |                 <label class="basicnetworkkey" for="max_wait">Max wait:</label> | ||||||
|  |                 <input class="basicnetworkvalue" type="number" id="max_wait"> | ||||||
|  |             </div> | ||||||
|  |          | ||||||
|             <div class="flexcontainer"> |             <div class="flexcontainer"> | ||||||
|                 <label class="basicnetworkkey" for="ssid">Password:</label> |                 <label class="basicnetworkkey" for="ssid">Password:</label> | ||||||
|                 <input class="basicnetworkvalue" type="text" id="password">     |                 <input class="basicnetworkvalue" type="text" id="password">     | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { Controller } from "./main"; | import { Controller } from "./main"; | ||||||
|  | import {NetworkConfig, SSIDList} from "./api"; | ||||||
|  |  | ||||||
| export class NetworkConfigView { | export class NetworkConfigView { | ||||||
|     setScanResult(ssidList: SSIDList) { |     setScanResult(ssidList: SSIDList) { | ||||||
| @@ -14,6 +15,7 @@ export class NetworkConfigView { | |||||||
|     private readonly password: HTMLInputElement; |     private readonly password: HTMLInputElement; | ||||||
|     private readonly mqtt_url: HTMLInputElement; |     private readonly mqtt_url: HTMLInputElement; | ||||||
|     private readonly base_topic: HTMLInputElement; |     private readonly base_topic: HTMLInputElement; | ||||||
|  |     private readonly max_wait: HTMLInputElement; | ||||||
|     private readonly ssidlist: HTMLElement; |     private readonly ssidlist: HTMLElement; | ||||||
|      |      | ||||||
|     constructor(controller: Controller, publicIp: string) { |     constructor(controller: Controller, publicIp: string) { | ||||||
| @@ -28,6 +30,9 @@ export class NetworkConfigView { | |||||||
|       this.ssid.onchange = controller.configChanged |       this.ssid.onchange = controller.configChanged | ||||||
|       this.password = (document.getElementById("password") as HTMLInputElement); |       this.password = (document.getElementById("password") as HTMLInputElement); | ||||||
|       this.password.onchange = controller.configChanged |       this.password.onchange = controller.configChanged | ||||||
|  |       this.max_wait = (document.getElementById("max_wait") as HTMLInputElement); | ||||||
|  |       this.max_wait.onchange = controller.configChanged | ||||||
|  |  | ||||||
|       this.mqtt_url = document.getElementById("mqtt_url") as HTMLInputElement; |       this.mqtt_url = document.getElementById("mqtt_url") as HTMLInputElement; | ||||||
|       this.mqtt_url.onchange = controller.configChanged |       this.mqtt_url.onchange = controller.configChanged | ||||||
|       this.base_topic = document.getElementById("base_topic") as HTMLInputElement; |       this.base_topic = document.getElementById("base_topic") as HTMLInputElement; | ||||||
| @@ -47,10 +52,12 @@ export class NetworkConfigView { | |||||||
|       this.password.value = network.password; |       this.password.value = network.password; | ||||||
|       this.mqtt_url.value = network.mqtt_url; |       this.mqtt_url.value = network.mqtt_url; | ||||||
|       this.base_topic.value = network.base_topic; |       this.base_topic.value = network.base_topic; | ||||||
|  |       this.max_wait.value = network.max_wait.toString(); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     getConfig(): NetworkConfig { |     getConfig(): NetworkConfig { | ||||||
|       return { |       return { | ||||||
|  |         max_wait: +this.max_wait.value, | ||||||
|         ap_ssid: this.ap_ssid.value, |         ap_ssid: this.ap_ssid.value, | ||||||
|         ssid: this.ssid.value ?? null, |         ssid: this.ssid.value ?? null, | ||||||
|         password: this.password.value ?? null, |         password: this.password.value ?? null, | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { Controller } from "./main"; | import { Controller } from "./main"; | ||||||
|  | import {NightLampConfig} from "./api"; | ||||||
|  |  | ||||||
| export class NightLampView { | export class NightLampView { | ||||||
|     private readonly night_lamp_only_when_dark: HTMLInputElement; |     private readonly night_lamp_only_when_dark: HTMLInputElement; | ||||||
|   | |||||||
| @@ -37,5 +37,5 @@ | |||||||
|     </form> |     </form> | ||||||
| </div> | </div> | ||||||
| <div class="display:flex"> | <div class="display:flex"> | ||||||
|     <input style="margin-left: 16px; margin-top: 8px;" class="col-6" type="button" id="test" value="Self-Test"> |     <button style="margin-left: 16px; margin-top: 8px;" class="col-6" type="button" id="test">Self-Test</button> | ||||||
| </div> | </div> | ||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { Controller } from "./main"; | import { Controller } from "./main"; | ||||||
|  | import {VersionInfo} from "./api"; | ||||||
|  |  | ||||||
| export class OTAView { | export class OTAView { | ||||||
|     readonly file1Upload: HTMLInputElement; |     readonly file1Upload: HTMLInputElement; | ||||||
| @@ -9,6 +10,8 @@ export class OTAView { | |||||||
|     constructor(controller: Controller) { |     constructor(controller: Controller) { | ||||||
|         (document.getElementById("firmwareview") as HTMLElement).innerHTML = require("./ota.html") |         (document.getElementById("firmwareview") as HTMLElement).innerHTML = require("./ota.html") | ||||||
|  |  | ||||||
|  |         let test  = document.getElementById("test") as HTMLButtonElement; | ||||||
|  |  | ||||||
|         this.firmware_buildtime = document.getElementById("firmware_buildtime") as HTMLDivElement; |         this.firmware_buildtime = document.getElementById("firmware_buildtime") as HTMLDivElement; | ||||||
|         this.firmware_githash = document.getElementById("firmware_githash") as HTMLDivElement; |         this.firmware_githash = document.getElementById("firmware_githash") as HTMLDivElement; | ||||||
|         this.firmware_partition = document.getElementById("firmware_partition") as HTMLDivElement; |         this.firmware_partition = document.getElementById("firmware_partition") as HTMLDivElement; | ||||||
| @@ -24,6 +27,10 @@ export class OTAView { | |||||||
|             } |             } | ||||||
|             controller.uploadNewFirmware(selectedFile); |             controller.uploadNewFirmware(selectedFile); | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |         test.onclick = () => { | ||||||
|  |             controller.selftest(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setVersion(versionInfo: VersionInfo) { |     setVersion(versionInfo: VersionInfo) { | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | import {PlantConfig} from "./api"; | ||||||
|  |  | ||||||
| const PLANT_COUNT = 8; | const PLANT_COUNT = 8; | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { Controller } from "./main"; | import { Controller } from "./main"; | ||||||
|  | import {BackupHeader} from "./api"; | ||||||
|  |  | ||||||
| export class SubmitView { | export class SubmitView { | ||||||
|   json: HTMLDivElement; |   json: HTMLDivElement; | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { Controller } from "./main"; | import { Controller } from "./main"; | ||||||
|  | import {TankConfig, TankInfo} from "./api"; | ||||||
|  |  | ||||||
| export class TankConfigView { | export class TankConfigView { | ||||||
|     private readonly tank_useable_ml: HTMLInputElement; |     private readonly tank_useable_ml: HTMLInputElement; | ||||||
|   | |||||||
| @@ -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.251.37'; |   host = 'http://192.168.71.1'; | ||||||
| } else { | } else { | ||||||
|   host = ''; |   host = ''; | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user