Housekeeping #18

Merged
EmpirePhoenix merged 8 commits from housekeeping into develop 2025-06-23 22:15:51 +02:00
16 changed files with 259 additions and 254 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

@ -38,13 +38,13 @@ command = [
partition_table = "partitions.csv" partition_table = "partitions.csv"
[features] [features]
default = ["std", "embassy", "esp-idf-svc/native"] default = ["std", "esp-idf-svc/native"]
pio = ["esp-idf-svc/pio"] pio = ["esp-idf-svc/pio"]
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"] std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
alloc = ["esp-idf-svc/alloc"] alloc = ["esp-idf-svc/alloc"]
nightly = ["esp-idf-svc/nightly"] nightly = ["esp-idf-svc/nightly"]
experimental = ["esp-idf-svc/experimental"] experimental = ["esp-idf-svc/experimental"]
embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"] #embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"]
[dependencies] [dependencies]
#ESP stuff #ESP stuff

View File

@ -22,6 +22,22 @@ fn main() {
println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success()); assert!(output.status.success());
// move webpack results to rust webserver src
let _ = Command::new("cmd")
.arg("/K")
.arg("move")
.arg("./src_webpack/bundle.js")
.arg("./src/webserver")
.output()
.unwrap();
let _ = Command::new("cmd")
.arg("/K")
.arg("move")
.arg("./src_webpack/index.html")
.arg("./src/webserver")
.output()
.unwrap();
} }
Err(_) => { Err(_) => {
println!("Assuming build on linux"); println!("Assuming build on linux");
@ -34,6 +50,18 @@ fn main() {
println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success()); assert!(output.status.success());
// move webpack results to rust webserver src
let _ = Command::new("mv")
.arg("./src_webpack/bundle.js")
.arg("./src/webserver")
.output()
.unwrap();
let _ = Command::new("mv")
.arg("./src_webpack/index.html")
.arg("./src/webserver")
.output()
.unwrap();
} }
} }

View File

@ -1,4 +1,3 @@
use crate::to_string;
use anyhow::anyhow; use anyhow::anyhow;
use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver}; use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
use embedded_hal_bus::i2c::MutexDevice; use embedded_hal_bus::i2c::MutexDevice;
@ -98,6 +97,7 @@ impl BatteryInteraction for NoBatteryMonitor {
} }
//TODO implement this battery monitor kind once controller is complete //TODO implement this battery monitor kind once controller is complete
#[allow(dead_code)]
pub struct WchI2cSlave {} pub struct WchI2cSlave {}
pub struct BQ34Z100G1<'a> { pub struct BQ34Z100G1<'a> {

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,7 +1,9 @@
use crate::config::{BoardHardware, PlantControllerConfig}; use crate::hal::esp::Esp;
use crate::hal::battery::{BatteryInteraction, NoBatteryMonitor};
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::{
config::PlantControllerConfig,
hal::battery::{BatteryInteraction, NoBatteryMonitor},
};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
@ -11,7 +13,7 @@ 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>,
} }
@ -20,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)?;
@ -39,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
} }
@ -51,7 +53,7 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
&mut self.battery &mut self.battery
} }
fn set_charge_indicator(&mut self, charging: bool) -> Result<()> { fn set_charge_indicator(&mut self, _charging: bool) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
@ -67,7 +69,7 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
fn backup_config(&mut self, bytes: &[u8]) -> Result<()> { fn backup_config(&mut self, _bytes: &[u8]) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
@ -82,17 +84,17 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
fn tank_sensor_voltage(&mut self) -> Result<f32> { fn tank_sensor_voltage(&mut self) -> Result<f32> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
fn light(&mut self, enable: bool) -> Result<()> { fn light(&mut self, _enable: bool) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
fn pump(&mut self, plant: usize, enable: bool) -> Result<()> { fn pump(&mut self, _plant: usize, _enable: bool) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
fn fault(&mut self, plant: usize, _enable: bool) -> Result<()> { fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32> { fn measure_moisture_hz(&mut self, _plant: usize, _sensor: Sensor) -> Result<f32> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
@ -108,10 +110,10 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> Result<()> { fn set_rtc_time(&mut self, _time: &DateTime<Utc>) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
fn test_pump(&mut self, plant: usize) -> Result<()> { fn test_pump(&mut self, _plant: usize) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }

View File

@ -4,55 +4,46 @@ mod initial_hal;
mod v3_hal; mod v3_hal;
mod v4_hal; mod v4_hal;
use crate::{
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
hal::{
battery::{print_battery_bq34z100, BatteryInteraction, NoBatteryMonitor},
esp::Esp,
},
log::{log, LogMessage},
};
use anyhow::{Ok, Result};
use battery::BQ34Z100G1; use battery::BQ34Z100G1;
use bq34z100::Bq34z100g1Driver; use bq34z100::Bq34z100g1Driver;
use chrono::{DateTime, Utc};
use crate::log::LogMessage;
use ds323x::DateTimeAccess;
use esp_ota::mark_app_valid;
use eeprom24x::Eeprom24xTrait;
use embedded_hal_bus::i2c::MutexDevice; use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::{
use esp_idf_hal::adc::ADC1; adc::ADC1,
use esp_idf_hal::i2c::{APBTickType, I2cConfig, I2cDriver}; delay::Delay,
use esp_idf_hal::units::FromValueType; gpio::{
use esp_idf_svc::eventloop::EspSystemEventLoop; Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18,
use esp_idf_svc::nvs::EspDefaultNvsPartition; Gpio2, Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, Gpio29, Gpio3,
use esp_idf_svc::wifi::EspWifi; Gpio30, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8, IOPin, PinDriver, Pull,
use esp_idf_sys::esp_restart; },
i2c::{APBTickType, I2cConfig, I2cDriver},
pcnt::PCNT0,
prelude::Peripherals,
reset::ResetReason,
units::FromValueType,
};
use esp_idf_svc::{eventloop::EspSystemEventLoop, nvs::EspDefaultNvsPartition, wifi::EspWifi};
use esp_idf_sys::{ use esp_idf_sys::{
esp_deep_sleep, esp_sleep_enable_ext1_wakeup, esp_deep_sleep, esp_restart, esp_sleep_enable_ext1_wakeup,
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 measurements::{Current, Voltage};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use anyhow::{Ok, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use std::result::Result::Ok as OkStd; use std::result::Result::Ok as OkStd;
use std::sync::Mutex; use std::sync::Mutex;
use std::time::Duration; use std::time::Duration;
use crate::config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig};
use crate::hal::battery::{print_battery_bq34z100, BatteryInteraction, NoBatteryMonitor};
use crate::hal::esp::ESP;
use crate::hal::initial_hal::Initial;
use crate::log::log;
use embedded_hal::digital::OutputPin;
use esp_idf_hal::delay::Delay;
use esp_idf_hal::gpio::{
Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18, Gpio2,
Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, Gpio29, Gpio3, Gpio30, Gpio4,
Gpio5, Gpio6, Gpio7, Gpio8, IOPin, PinDriver, Pull,
};
use esp_idf_hal::pcnt::PCNT0;
use esp_idf_hal::prelude::Peripherals;
use esp_idf_hal::reset::ResetReason;
use measurements::{Current, Voltage};
use pca9535::StandardExpanderInterface;
//Only support for 8 right now! //Only support for 8 right now!
pub const PLANT_COUNT: usize = 8; pub const PLANT_COUNT: usize = 8;
const REPEAT_MOIST_MEASURE: usize = 1; const REPEAT_MOIST_MEASURE: usize = 1;
@ -61,11 +52,6 @@ const TANK_MULTI_SAMPLE: usize = 11;
pub static I2C_DRIVER: Lazy<Mutex<I2cDriver<'static>>> = Lazy::new(PlantHal::create_i2c); pub static I2C_DRIVER: Lazy<Mutex<I2cDriver<'static>>> = Lazy::new(PlantHal::create_i2c);
#[non_exhaustive]
struct V3Constants;
impl V3Constants {}
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);
fn deep_sleep(duration_in_ms: u64) -> ! { fn deep_sleep(duration_in_ms: u64) -> ! {
@ -98,36 +84,23 @@ 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 set_charge_indicator(&mut self, charging: bool) -> Result<()>; fn get_esp(&mut self) -> &mut Esp<'a>;
fn is_day(&self) -> bool;
fn get_mptt_voltage(&mut self) -> Result<Voltage>;
fn get_mptt_current(&mut self) -> Result<Current>;
fn get_esp(&mut self) -> &mut ESP<'a>;
fn get_config(&mut self) -> &PlantControllerConfig; fn get_config(&mut self) -> &PlantControllerConfig;
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>; fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
fn deep_sleep(&mut self, duration_in_ms: u64) -> !; fn deep_sleep(&mut self, duration_in_ms: u64) -> !;
fn get_backup_info(&mut self) -> Result<BackupHeader>; fn get_backup_info(&mut self) -> Result<BackupHeader>;
fn get_backup_config(&mut self) -> Result<Vec<u8>>; fn get_backup_config(&mut self) -> Result<Vec<u8>>;
fn backup_config(&mut self, bytes: &[u8]) -> Result<()>; fn backup_config(&mut self, bytes: &[u8]) -> Result<()>;
fn is_day(&self) -> bool;
//should be multsampled //should be multsampled
fn water_temperature_c(&mut self) -> Result<f32>; fn water_temperature_c(&mut self) -> Result<f32>;
/// return median tank sensor value in milli volt /// return median tank sensor value in milli volt
@ -143,8 +116,11 @@ 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)]
pub struct FreePeripherals { pub struct FreePeripherals {
pub gpio0: Gpio0, pub gpio0: Gpio0,
pub gpio1: Gpio1, pub gpio1: Gpio1,
@ -239,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

@ -1,11 +1,12 @@
use crate::config::PlantControllerConfig;
use crate::hal::battery::BatteryInteraction;
use crate::hal::esp::ESP;
use crate::hal::{ use crate::hal::{
deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, V3Constants, I2C_DRIVER, deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT,
PLANT_COUNT, REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25, REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25,
}; };
use crate::log::{log, LogMessage}; use crate::log::{log, LogMessage};
use crate::{
config::PlantControllerConfig,
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};
use ds18b20::Ds18b20; use ds18b20::Ds18b20;
@ -13,21 +14,22 @@ 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::adc::oneshot::config::AdcChannelConfig; use esp_idf_hal::{
use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver}; adc::{
use esp_idf_hal::adc::{attenuation, Resolution}; attenuation,
use esp_idf_hal::delay::Delay; oneshot::{config::AdcChannelConfig, AdcChannelDriver, AdcDriver},
use esp_idf_hal::gpio::{AnyInputPin, Gpio5, IOPin, InputOutput, PinDriver, Pull}; Resolution,
use esp_idf_hal::i2c::I2cDriver; },
use esp_idf_hal::pcnt::{ delay::Delay,
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, gpio::{AnyInputPin, Gpio5, IOPin, InputOutput, PinDriver, Pull},
i2c::I2cDriver,
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 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;
use std::sync::Arc;
const PUMP8_BIT: usize = 0; const PUMP8_BIT: usize = 0;
const PUMP1_BIT: usize = 1; const PUMP1_BIT: usize = 1;
@ -77,13 +79,13 @@ 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>,
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
>, >,
shift_register_enable_invert: _shift_register_enable_invert:
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Output>, PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Output>,
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>,
@ -105,10 +107,10 @@ 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 + Send + '_>> { ) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
let mut clock = PinDriver::input_output(peripherals.gpio15.downgrade())?; let mut clock = PinDriver::input_output(peripherals.gpio15.downgrade())?;
clock.set_pull(Pull::Floating)?; clock.set_pull(Pull::Floating)?;
let mut latch = PinDriver::input_output(peripherals.gpio3.downgrade())?; let mut latch = PinDriver::input_output(peripherals.gpio3.downgrade())?;
@ -233,7 +235,7 @@ pub(crate) fn create_v3(
battery_monitor, battery_monitor,
esp, esp,
shift_register, shift_register,
shift_register_enable_invert, _shift_register_enable_invert: shift_register_enable_invert,
tank_channel, tank_channel,
solar_is_day, solar_is_day,
light, light,
@ -268,7 +270,7 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
fn get_mptt_current(&mut self) -> Result<Current> { fn get_mptt_current(&mut self) -> Result<Current> {
bail!("Board does not have current sensor") bail!("Board does not have current sensor")
} }
fn get_esp(&mut self) -> &mut ESP<'a> { fn get_esp(&mut self) -> &mut Esp<'a> {
&mut self.esp &mut self.esp
} }

View File

@ -1,6 +1,6 @@
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,
@ -22,15 +22,15 @@ use esp_idf_hal::i2c::I2cDriver;
use esp_idf_hal::pcnt::{ use esp_idf_hal::pcnt::{
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
}; };
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError}; use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, EspError};
use ina219::address::{Address, Pin}; use ina219::address::{Address, Pin};
use ina219::calibration::{Calibration, UnCalibrated}; use ina219::calibration::UnCalibrated;
use ina219::configuration::{Configuration, OperatingMode};
use ina219::SyncIna219; use ina219::SyncIna219;
use measurements::{Current, Resistance, Voltage}; use measurements::{Current, Resistance, Voltage};
use one_wire_bus::OneWire; use one_wire_bus::OneWire;
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
use std::result::Result::Ok as OkStd; use std::result::Result::Ok as OkStd;
use ina219::configuration::{Configuration, OperatingMode};
const MS0: u8 = 1_u8; const MS0: u8 = 1_u8;
const MS1: u8 = 0_u8; const MS1: u8 = 0_u8;
@ -47,23 +47,27 @@ pub enum Charger<'a> {
}, },
} }
impl<'a> Charger<'a> { impl Charger<'_> {
pub(crate) fn powersave(&mut self) { pub(crate) fn power_save(&mut self) {
match self { Charger::SolarMpptV1 { mppt_ina, .. } => { match self {
let _ = mppt_ina.set_configuration(Configuration { Charger::SolarMpptV1 { mppt_ina, .. } => {
let _ = mppt_ina
.set_configuration(Configuration {
reset: Default::default(), reset: Default::default(),
bus_voltage_range: Default::default(), bus_voltage_range: Default::default(),
shunt_voltage_range: Default::default(), shunt_voltage_range: Default::default(),
bus_resolution: Default::default(), bus_resolution: Default::default(),
shunt_resolution: Default::default(), shunt_resolution: Default::default(),
operating_mode: OperatingMode::PowerDown, operating_mode: OperatingMode::PowerDown,
}).map_err(|e| { })
.map_err(|e| {
println!( println!(
"Error setting ina mppt configuration during deepsleep preparation{:?}", "Error setting ina mppt configuration during deep sleep preparation{:?}",
e e
); );
}); });
} } }
}
} }
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> { fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
match self { match self {
@ -105,7 +109,7 @@ impl<'a> Charger<'a> {
} }
pub struct V4<'a> { pub struct V4<'a> {
esp: ESP<'a>, esp: Esp<'a>,
charger: Charger<'a>, charger: Charger<'a>,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
config: PlantControllerConfig, config: PlantControllerConfig,
@ -130,10 +134,10 @@ pub struct V4<'a> {
pub(crate) fn create_v4( 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 + 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()?;
@ -244,7 +248,7 @@ pub(crate) fn create_v4(
Address::from_pins(Pin::Vcc, Pin::Gnd), Address::from_pins(Pin::Vcc, Pin::Gnd),
)?; )?;
mppt_ina.set_configuration(Configuration{ mppt_ina.set_configuration(Configuration {
reset: Default::default(), reset: Default::default(),
bus_voltage_range: Default::default(), bus_voltage_range: Default::default(),
shunt_voltage_range: Default::default(), shunt_voltage_range: Default::default(),
@ -252,7 +256,7 @@ pub(crate) fn create_v4(
shunt_resolution: ina219::configuration::Resolution::Avg128, shunt_resolution: ina219::configuration::Resolution::Avg128,
operating_mode: Default::default(), operating_mode: Default::default(),
})?; })?;
//TODO this is probably laready done untill we are ready first time?, maybee add startup time comparison on access? //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()?
@ -288,7 +292,7 @@ pub(crate) fn create_v4(
} }
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
} }
@ -306,7 +310,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
self.awake.set_low().unwrap(); self.awake.set_low().unwrap();
self.charger.powersave(); self.charger.power_save();
deep_sleep(duration_in_ms); deep_sleep(duration_in_ms);
} }

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,43 +1,39 @@
use crate::webserver::webserver::httpd; use crate::{
config::BoardVersion::INITIAL,
hal::{PlantHal, HAL, PLANT_COUNT},
webserver::httpd,
};
use anyhow::bail; use anyhow::bail;
use chrono::{DateTime, Datelike, Timelike, Utc}; use chrono::{DateTime, Datelike, Timelike, Utc};
use chrono_tz::Tz; use chrono_tz::Tz::{self, UTC};
use chrono_tz::Tz::UTC;
use esp_idf_hal::delay::Delay; use esp_idf_hal::delay::Delay;
use esp_idf_sys::{ use esp_idf_sys::{
esp_ota_get_app_partition_count, esp_ota_get_running_partition, esp_ota_get_state_partition, esp_ota_get_app_partition_count, esp_ota_get_running_partition, esp_ota_get_state_partition,
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;
use log::{log, LogMessage}; use log::{log, LogMessage};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use plant_state::PlantState;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::MutexGuard; use std::sync::{atomic::AtomicBool, Arc, Mutex, MutexGuard};
use std::{ use tank::*;
fmt::Display,
sync::{atomic::AtomicBool, Arc, Mutex},
};
mod config; mod config;
mod hal; mod hal;
mod log; mod log;
mod plant_state; mod plant_state;
mod tank; mod tank;
mod webserver;
use crate::config::BoardVersion::INITIAL;
use crate::hal::battery::BatteryInteraction;
use crate::hal::{BoardInteraction, PlantHal, HAL, PLANT_COUNT};
use plant_state::PlantState;
use tank::*;
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 {
@ -72,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,
@ -178,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, "", "");
@ -268,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);
@ -424,12 +411,12 @@ fn safe_main() -> anyhow::Result<()> {
.state_charge_percent() .state_charge_percent()
.unwrap_or(0.); .unwrap_or(0.);
/// try to load full battery state if failed the battery state is unknown // try to load full battery state if failed the battery state is unknown
let battery_state = board let battery_state = board
.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,
@ -559,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;
@ -611,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
@ -682,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(_) => {
@ -739,7 +746,8 @@ fn pump_info(
fn publish_battery_state(board: &mut MutexGuard<'_, HAL<'_>>) { fn publish_battery_state(board: &mut MutexGuard<'_, HAL<'_>>) {
let state = board.board_hal.get_battery_monitor().get_battery_state(); let state = board.board_hal.get_battery_monitor().get_battery_state();
if let Ok(serialized_battery_state_bytes) = serde_json::to_string(&state).map(|s| s.into_bytes()) if let Ok(serialized_battery_state_bytes) =
serde_json::to_string(&state).map(|s| s.into_bytes())
{ {
let _ = board let _ = board
.board_hal .board_hal
@ -752,27 +760,10 @@ 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);
//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);
}
match wait_type { match wait_type {
WaitType::MissingConfig => { WaitType::MissingConfig => {
@ -801,7 +792,8 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
board.board_hal.general_fault(true); board.board_hal.general_fault(true);
drop(board); drop(board);
vTaskDelay(delay); //cannot use shared delay as that is inside the mutex here
delay_handle.delay_ms(delay);
let mut board = BOARD_ACCESS.lock().unwrap(); let mut board = BOARD_ACCESS.lock().unwrap();
board.board_hal.general_fault(false); board.board_hal.general_fault(false);
@ -810,7 +802,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
let _ = board.board_hal.fault(i, false); let _ = board.board_hal.fault(i, false);
} }
drop(board); drop(board);
vTaskDelay(delay); delay_handle.delay_ms(delay);
if wait_type == WaitType::MqttConfig if wait_type == WaitType::MqttConfig
&& !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed) && !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed)
@ -823,13 +815,12 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1); BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1);
} }
} }
}
} }
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");
@ -850,23 +841,14 @@ fn main() {
} }
} }
fn to_string<T: Display>(value: anyhow::Result<T>) -> String {
match value {
Ok(v) => v.to_string(),
Err(err) => {
format!("{:?}", err)
}
}
}
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

@ -1,10 +1,12 @@
use crate::{
config::PlantConfig,
hal::{Sensor, HAL},
in_time_range,
};
use chrono::{DateTime, TimeDelta, Utc}; use chrono::{DateTime, TimeDelta, Utc};
use chrono_tz::Tz; use chrono_tz::Tz;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::hal::{BoardInteraction, Sensor, HAL};
use crate::{config::PlantConfig, in_time_range};
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 7500.; // 60kHz (500Hz margin) const MOIST_SENSOR_MAX_FREQUENCY: f32 = 7500.; // 60kHz (500Hz margin)
const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, think like cactus levels const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, think like cactus levels
@ -237,7 +239,11 @@ impl PlantState {
} }
} }
pub fn to_mqtt_info(&self, plant_conf: &PlantConfig, current_time: &DateTime<Tz>) -> PlantInfo { pub fn to_mqtt_info(
&self,
plant_conf: &PlantConfig,
current_time: &DateTime<Tz>,
) -> PlantInfo<'_> {
PlantInfo { PlantInfo {
sensor_a: &self.sensor_a, sensor_a: &self.sensor_a,
sensor_b: &self.sensor_b, sensor_b: &self.sensor_b,

View File

@ -100,7 +100,7 @@ macro_rules! ShiftRegisterBuilder {
} }
/// Get embedded-hal output pins to control the shift register outputs /// Get embedded-hal output pins to control the shift register outputs
pub fn decompose(&self) -> [ShiftRegisterPin; $size] { pub fn decompose(&self) -> [ShiftRegisterPin<'_>; $size] {
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
// safe because the type we are claiming to have initialized here is a // safe because the type we are claiming to have initialized here is a
// bunch of `MaybeUninit`s, which do not require initialization. // bunch of `MaybeUninit`s, which do not require initialization.

View File

@ -1,8 +1,6 @@
use crate::{config::TankConfig, hal::HAL};
use serde::Serialize; use serde::Serialize;
use crate::config::TankConfig;
use crate::hal::{BoardInteraction, HAL};
const OPEN_TANK_VOLTAGE: f32 = 3.0; const OPEN_TANK_VOLTAGE: f32 = 3.0;
pub const WATER_FROZEN_THRESH: f32 = 4.0; pub const WATER_FROZEN_THRESH: f32 = 4.0;

View File

@ -1,8 +1,12 @@
//offer ota and config mode //offer ota and config mode
use crate::hal::battery::BatteryInteraction;
use crate::{ use crate::{
determine_tank_state, get_version, log::LogMessage, plant_state::PlantState, BOARD_ACCESS, config::PlantControllerConfig,
determine_tank_state, get_version,
hal::PLANT_COUNT,
log::LogMessage,
plant_state::{MoistureSensorState, PlantState},
BOARD_ACCESS,
}; };
use anyhow::bail; use anyhow::bail;
use chrono::DateTime; use chrono::DateTime;
@ -19,10 +23,6 @@ use std::{
}; };
use url::Url; use url::Url;
use crate::config::PlantControllerConfig;
use crate::hal::{BoardInteraction, PLANT_COUNT};
use crate::plant_state::MoistureSensorState;
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
struct SSIDList<'a> { struct SSIDList<'a> {
ssids: Vec<&'a String<32>>, ssids: Vec<&'a String<32>>,
@ -221,7 +221,7 @@ fn set_config(
let config: PlantControllerConfig = serde_json::from_slice(&all)?; let config: PlantControllerConfig = serde_json::from_slice(&all)?;
let mut board = BOARD_ACCESS.lock().expect("board access"); let mut board = BOARD_ACCESS.lock().expect("board access");
board.board_hal.set_config(config); let _ = board.board_hal.set_config(config);
anyhow::Ok(Some("saved".to_owned())) anyhow::Ok(Some("saved".to_owned()))
} }

View File

@ -49,7 +49,7 @@ module.exports = {
}, },
output: { output: {
filename: 'bundle.js', filename: 'bundle.js',
path: path.resolve(__dirname, '../src/webserver'), path: path.resolve(__dirname, '.'),
}, },
devServer: { devServer: {
} }