chore: 📎 + fmt
This commit is contained in:
		| @@ -3,25 +3,25 @@ | ||||
| extern crate alloc; | ||||
|  | ||||
| use crate::hal::peripherals::CAN1; | ||||
| use core::fmt::Write as _; | ||||
| use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET}; | ||||
| use canapi::SensorSlot; | ||||
| use ch32_hal::gpio::{Level, Output, Speed}; | ||||
| use ch32_hal::adc::{Adc, SampleTime, ADC_MAX}; | ||||
| use ch32_hal::can; | ||||
| use ch32_hal::can::{Can, CanFifo, CanFilter, CanFrame, CanMode}; | ||||
| use ch32_hal::mode::{NonBlocking}; | ||||
| use ch32_hal::gpio::{Level, Output, Speed}; | ||||
| use ch32_hal::mode::NonBlocking; | ||||
| use ch32_hal::peripherals::USBD; | ||||
| use embassy_executor::{Spawner, task}; | ||||
| use core::fmt::Write as _; | ||||
| use embassy_executor::{task, Spawner}; | ||||
| use embassy_futures::yield_now; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::channel::Channel; | ||||
| use embassy_time::{Delay, Duration, Instant, Timer}; | ||||
| use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||||
| use embassy_usb::{Builder, UsbDevice}; | ||||
| use embassy_futures::yield_now; | ||||
| use hal::usbd::{Driver}; | ||||
| use hal::{bind_interrupts}; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::channel::{Channel}; | ||||
| use embassy_time::{Instant, Duration, Delay, Timer}; | ||||
| use embedded_can::{Id, StandardId}; | ||||
| use hal::bind_interrupts; | ||||
| use hal::usbd::Driver; | ||||
| use {ch32_hal as hal, panic_halt as _}; | ||||
|  | ||||
| macro_rules! mk_static { | ||||
| @@ -33,20 +33,18 @@ macro_rules! mk_static { | ||||
|     }}; | ||||
| } | ||||
|  | ||||
|  bind_interrupts!(struct Irqs { | ||||
|      USB_LP_CAN1_RX0 => hal::usbd::InterruptHandler<hal::peripherals::USBD>; | ||||
|  }); | ||||
| bind_interrupts!(struct Irqs { | ||||
|     USB_LP_CAN1_RX0 => hal::usbd::InterruptHandler<hal::peripherals::USBD>; | ||||
| }); | ||||
|  | ||||
|  | ||||
|  use embedded_alloc::LlffHeap as Heap; | ||||
| use embedded_alloc::LlffHeap as Heap; | ||||
| use embedded_can::nb::Can as nb_can; | ||||
| use qingke::riscv::asm::delay; | ||||
| use log::log; | ||||
| use qingke::riscv::asm::delay; | ||||
|  | ||||
| #[global_allocator] | ||||
| static HEAP: Heap = Heap::empty(); | ||||
|  | ||||
|  | ||||
| static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new(); | ||||
|  | ||||
| #[embassy_executor::main(entry = "qingke_rt::entry")] | ||||
| @@ -63,8 +61,6 @@ async fn main(spawner: Spawner) { | ||||
|         ..Default::default() | ||||
|     }); | ||||
|  | ||||
|  | ||||
|  | ||||
|     // Build driver and USB stack using 'static buffers | ||||
|     let driver = Driver::new(p.USBD, Irqs, p.PA12, p.PA11); | ||||
|  | ||||
| @@ -84,22 +80,19 @@ async fn main(spawner: Spawner) { | ||||
|     let mut builder = Builder::new( | ||||
|         driver, | ||||
|         config, | ||||
|         mk_static!([u8;256], [0; 256]), | ||||
|         mk_static!([u8;256], [0; 256]), | ||||
|         mk_static!([u8; 256], [0; 256]), | ||||
|         mk_static!([u8; 256], [0; 256]), | ||||
|         &mut [], // no msos descriptors | ||||
|         mk_static!([u8;64], [0; 64]), | ||||
|         mk_static!([u8; 64], [0; 64]), | ||||
|     ); | ||||
|     // Initialize CDC state and create CDC-ACM class | ||||
|     let class = mk_static!(CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>, | ||||
|             CdcAcmClass::new( | ||||
|             &mut builder, | ||||
|             mk_static!(State, State::new()), | ||||
|             64 | ||||
|         ) | ||||
|     let class = mk_static!( | ||||
|         CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>, | ||||
|         CdcAcmClass::new(&mut builder, mk_static!(State, State::new()), 64) | ||||
|     ); | ||||
|  | ||||
|     // Build USB device | ||||
|     let usb = mk_static!(UsbDevice<Driver<USBD>>, builder.build()) ; | ||||
|     let usb = mk_static!(UsbDevice<Driver<USBD>>, builder.build()); | ||||
|  | ||||
|     // Create GPIO for 555 Q output (PB0) | ||||
|     let q_out = Output::new(p.PB0, Level::Low, Speed::Low); | ||||
| @@ -112,7 +105,16 @@ async fn main(spawner: Spawner) { | ||||
|     let adc = Adc::new(p.ADC1, Default::default()); | ||||
|     let ain = p.PA1; | ||||
|     let config = can::can::Config::default(); | ||||
|     let can: Can<CAN1, NonBlocking> = Can::new_nb(p.CAN1, p.PB8, p.PB9, CanFifo::Fifo0, CanMode::Normal, 125_000, config).expect("Valid"); | ||||
|     let can: Can<CAN1, NonBlocking> = Can::new_nb( | ||||
|         p.CAN1, | ||||
|         p.PB8, | ||||
|         p.PB9, | ||||
|         CanFifo::Fifo0, | ||||
|         CanMode::Normal, | ||||
|         125_000, | ||||
|         config, | ||||
|     ) | ||||
|     .expect("Valid"); | ||||
|     ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2)); | ||||
|  | ||||
|     spawner.spawn(usb_task(usb)).unwrap(); | ||||
| @@ -120,8 +122,6 @@ async fn main(spawner: Spawner) { | ||||
|     // move Q output, LED, ADC and analog input into worker task | ||||
|     spawner.spawn(worker(q_out, led, adc, ain, can)).unwrap(); | ||||
|  | ||||
|  | ||||
|  | ||||
|     // Prevent main from exiting | ||||
|     core::future::pending::<()>().await; | ||||
| } | ||||
| @@ -136,12 +136,13 @@ async fn worker( | ||||
| ) { | ||||
|     // 555 emulation state: Q initially Low | ||||
|     let mut q_high = false; | ||||
|     let low_th: u16 = (ADC_MAX as u16) / 3;       // ~1/3 Vref | ||||
|     let low_th: u16 = (ADC_MAX as u16) / 3; // ~1/3 Vref | ||||
|     let high_th: u16 = ((ADC_MAX as u32 * 2) / 3) as u16; // ~2/3 Vref | ||||
|  | ||||
|  | ||||
|     let moisture_address = StandardId::new(plant_id(MOISTURE_DATA_OFFSET, SensorSlot::A,  0)).unwrap(); | ||||
|     let identity_address = StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, SensorSlot::A,  0)).unwrap(); | ||||
|     let moisture_address = | ||||
|         StandardId::new(plant_id(MOISTURE_DATA_OFFSET, SensorSlot::A, 0)).unwrap(); | ||||
|     let identity_address = | ||||
|         StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, SensorSlot::A, 0)).unwrap(); | ||||
|  | ||||
|     let mut filter = CanFilter::new_id_list(); | ||||
|  | ||||
| @@ -153,14 +154,15 @@ async fn worker( | ||||
|     can.add_filter(filter); | ||||
|     //can.add_filter(CanFilter::accept_all()); | ||||
|  | ||||
|  | ||||
|     loop { | ||||
|         // Count rising edges of Q in a 100 ms window | ||||
|         let start = Instant::now(); | ||||
|         let mut pulses: u32 = 0; | ||||
|         let mut last_q = q_high; | ||||
|  | ||||
|         while Instant::now().checked_duration_since(start).unwrap_or(Duration::from_millis(0)) | ||||
|         while Instant::now() | ||||
|             .checked_duration_since(start) | ||||
|             .unwrap_or(Duration::from_millis(0)) | ||||
|             < Duration::from_millis(1000) | ||||
|         { | ||||
|             // Sample the analog input (Threshold/Trigger on A1) | ||||
| @@ -204,24 +206,16 @@ async fn worker( | ||||
|         ); | ||||
|         log(msg); | ||||
|  | ||||
|  | ||||
|         let mut moisture = CanFrame::new(moisture_address, &[freq_hz as u8]).unwrap(); | ||||
|         match can.transmit(&mut moisture){ | ||||
|         match can.transmit(&mut moisture) { | ||||
|             Ok(..) => { | ||||
|                 let mut msg: heapless::String<128> = heapless::String::new(); | ||||
|                 let _ = write!( | ||||
|                     &mut msg, | ||||
|                     "Send to canbus" | ||||
|                 ); | ||||
|                 let _ = write!(&mut msg, "Send to canbus"); | ||||
|                 log(msg); | ||||
|             } | ||||
|             Err(err) => { | ||||
|                 let mut msg: heapless::String<128> = heapless::String::new(); | ||||
|                 let _ = write!( | ||||
|                     &mut msg, | ||||
|                     "err {:?}" | ||||
|                     ,err | ||||
|                 ); | ||||
|                 let _ = write!(&mut msg, "err {:?}", err); | ||||
|                 log(msg); | ||||
|             } | ||||
|         } | ||||
| @@ -229,35 +223,31 @@ async fn worker( | ||||
|         loop { | ||||
|             yield_now().await; | ||||
|             match can.receive() { | ||||
|                 Ok(frame) => { | ||||
|                     match frame.id() { | ||||
|                         Id::Standard(s_frame) => { | ||||
|                             let mut msg: heapless::String<128> = heapless::String::new(); | ||||
|                             let _ = write!( | ||||
|                                 &mut msg, | ||||
|                                 "Received from canbus: {:?} ident is {:?} \r\n", | ||||
|                                 s_frame.as_raw(), | ||||
|                                 identity_address.as_raw() | ||||
|                             ); | ||||
|                             log(msg); | ||||
|                             if s_frame.as_raw() == identity_address.as_raw() { | ||||
|                                 for _ in 0..10 { | ||||
|                                     Timer::after_millis(250).await; | ||||
|                                     led.toggle(); | ||||
|                                 } | ||||
|                                 led.set_low(); | ||||
|                 Ok(frame) => match frame.id() { | ||||
|                     Id::Standard(s_frame) => { | ||||
|                         let mut msg: heapless::String<128> = heapless::String::new(); | ||||
|                         let _ = write!( | ||||
|                             &mut msg, | ||||
|                             "Received from canbus: {:?} ident is {:?} \r\n", | ||||
|                             s_frame.as_raw(), | ||||
|                             identity_address.as_raw() | ||||
|                         ); | ||||
|                         log(msg); | ||||
|                         if s_frame.as_raw() == identity_address.as_raw() { | ||||
|                             for _ in 0..10 { | ||||
|                                 Timer::after_millis(250).await; | ||||
|                                 led.toggle(); | ||||
|                             } | ||||
|                             led.set_low(); | ||||
|                         } | ||||
|                         Id::Extended(_) => {} | ||||
|                     } | ||||
|                 } | ||||
|                     Id::Extended(_) => {} | ||||
|                 }, | ||||
|                 _ => { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -275,10 +265,9 @@ async fn usb_task(usb: &'static mut UsbDevice<'static, Driver<'static, hal::peri | ||||
|  | ||||
| #[task] | ||||
| async fn usb_writer( | ||||
|     class: &'static mut CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>> | ||||
|     class: &'static mut CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>, | ||||
| ) { | ||||
|     loop { | ||||
|  | ||||
|         class.wait_connection().await; | ||||
|         printer(class).await; | ||||
|     } | ||||
|   | ||||
| @@ -89,7 +89,7 @@ pub enum BatteryBoardVersion { | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] | ||||
| pub enum BoardVersion { | ||||
|     #[default] | ||||
|     INITIAL, | ||||
|     Initial, | ||||
|     V3, | ||||
|     V4, | ||||
| } | ||||
| @@ -135,7 +135,7 @@ pub struct PlantConfig { | ||||
| impl Default for PlantConfig { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             mode: PlantWateringMode::OFF, | ||||
|             mode: PlantWateringMode::Off, | ||||
|             target_moisture: 40., | ||||
|             min_moisture: 30., | ||||
|             pump_time_s: 30, | ||||
|   | ||||
| @@ -78,28 +78,28 @@ impl fmt::Display for FatError { | ||||
|             FatError::SpawnError { error } => { | ||||
|                 write!(f, "SpawnError {:?}", error.to_string()) | ||||
|             } | ||||
|             FatError::OneWireError { error } => write!(f, "OneWireError {:?}", error), | ||||
|             FatError::String { error } => write!(f, "{}", error), | ||||
|             FatError::LittleFSError { error } => write!(f, "LittleFSError {:?}", error), | ||||
|             FatError::PathError { error } => write!(f, "PathError {:?}", error), | ||||
|             FatError::TryLockError { error } => write!(f, "TryLockError {:?}", error), | ||||
|             FatError::WifiError { error } => write!(f, "WifiError {:?}", error), | ||||
|             FatError::SerdeError { error } => write!(f, "SerdeError {:?}", error), | ||||
|             FatError::PreconditionFailed { error } => write!(f, "PreconditionFailed {:?}", error), | ||||
|             FatError::OneWireError { error } => write!(f, "OneWireError {error:?}"), | ||||
|             FatError::String { error } => write!(f, "{error}"), | ||||
|             FatError::LittleFSError { error } => write!(f, "LittleFSError {error:?}"), | ||||
|             FatError::PathError { error } => write!(f, "PathError {error:?}"), | ||||
|             FatError::TryLockError { error } => write!(f, "TryLockError {error:?}"), | ||||
|             FatError::WifiError { error } => write!(f, "WifiError {error:?}"), | ||||
|             FatError::SerdeError { error } => write!(f, "SerdeError {error:?}"), | ||||
|             FatError::PreconditionFailed { error } => write!(f, "PreconditionFailed {error:?}"), | ||||
|             FatError::PartitionError { error } => { | ||||
|                 write!(f, "PartitionError {:?}", error) | ||||
|                 write!(f, "PartitionError {error:?}") | ||||
|             } | ||||
|             FatError::NoBatteryMonitor => { | ||||
|                 write!(f, "No Battery Monitor") | ||||
|             } | ||||
|             FatError::I2CConfigError { error } => write!(f, "I2CConfigError {:?}", error), | ||||
|             FatError::DS323 { error } => write!(f, "DS323 {:?}", error), | ||||
|             FatError::Eeprom24x { error } => write!(f, "Eeprom24x {:?}", error), | ||||
|             FatError::ExpanderError { error } => write!(f, "ExpanderError {:?}", error), | ||||
|             FatError::I2CConfigError { error } => write!(f, "I2CConfigError {error:?}"), | ||||
|             FatError::DS323 { error } => write!(f, "DS323 {error:?}"), | ||||
|             FatError::Eeprom24x { error } => write!(f, "Eeprom24x {error:?}"), | ||||
|             FatError::ExpanderError { error } => write!(f, "ExpanderError {error:?}"), | ||||
|             FatError::CanBusError { error } => { | ||||
|                 write!(f, "CanBusError {:?}", error) | ||||
|                 write!(f, "CanBusError {error:?}") | ||||
|             } | ||||
|             FatError::SNTPError { error } => write!(f, "SNTPError {:?}", error), | ||||
|             FatError::SNTPError { error } => write!(f, "SNTPError {error:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -197,7 +197,7 @@ impl From<Utf8Error> for FatError { | ||||
| impl<E: core::fmt::Debug> From<edge_http::io::Error<E>> for FatError { | ||||
|     fn from(value: edge_http::io::Error<E>) -> Self { | ||||
|         FatError::String { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -205,7 +205,7 @@ impl<E: core::fmt::Debug> From<edge_http::io::Error<E>> for FatError { | ||||
| impl<E: core::fmt::Debug> From<ds323x::Error<E>> for FatError { | ||||
|     fn from(value: ds323x::Error<E>) -> Self { | ||||
|         FatError::DS323 { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -213,7 +213,7 @@ impl<E: core::fmt::Debug> From<ds323x::Error<E>> for FatError { | ||||
| impl<E: core::fmt::Debug> From<eeprom24x::Error<E>> for FatError { | ||||
|     fn from(value: eeprom24x::Error<E>) -> Self { | ||||
|         FatError::Eeprom24x { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -221,7 +221,7 @@ impl<E: core::fmt::Debug> From<eeprom24x::Error<E>> for FatError { | ||||
| impl<E: core::fmt::Debug> From<ExpanderError<I2cDeviceError<E>>> for FatError { | ||||
|     fn from(value: ExpanderError<I2cDeviceError<E>>) -> Self { | ||||
|         FatError::ExpanderError { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -229,7 +229,7 @@ impl<E: core::fmt::Debug> From<ExpanderError<I2cDeviceError<E>>> for FatError { | ||||
| impl From<bincode::error::DecodeError> for FatError { | ||||
|     fn from(value: bincode::error::DecodeError) -> Self { | ||||
|         FatError::Eeprom24x { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -237,7 +237,7 @@ impl From<bincode::error::DecodeError> for FatError { | ||||
| impl From<bincode::error::EncodeError> for FatError { | ||||
|     fn from(value: bincode::error::EncodeError) -> Self { | ||||
|         FatError::Eeprom24x { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -251,7 +251,7 @@ impl From<ConfigError> for FatError { | ||||
| impl<E: core::fmt::Debug> From<I2cDeviceError<E>> for FatError { | ||||
|     fn from(value: I2cDeviceError<E>) -> Self { | ||||
|         FatError::String { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -259,14 +259,14 @@ impl<E: core::fmt::Debug> From<I2cDeviceError<E>> for FatError { | ||||
| impl<E: core::fmt::Debug> From<BusVoltageReadError<I2cDeviceError<E>>> for FatError { | ||||
|     fn from(value: BusVoltageReadError<I2cDeviceError<E>>) -> Self { | ||||
|         FatError::String { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl<E: core::fmt::Debug> From<ShuntVoltageReadError<I2cDeviceError<E>>> for FatError { | ||||
|     fn from(value: ShuntVoltageReadError<I2cDeviceError<E>>) -> Self { | ||||
|         FatError::String { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -280,14 +280,14 @@ impl From<Infallible> for FatError { | ||||
| impl From<InvalidLowLimit> for FatError { | ||||
|     fn from(value: InvalidLowLimit) -> Self { | ||||
|         FatError::String { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl From<InvalidHighLimit> for FatError { | ||||
|     fn from(value: InvalidHighLimit) -> Self { | ||||
|         FatError::String { | ||||
|             error: format!("{:?}", value), | ||||
|             error: format!("{value:?}"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -105,7 +105,7 @@ impl BatteryInteraction for BQ34Z100G1 { | ||||
|             .state_of_charge() | ||||
|             .map(|v| v as f32) | ||||
|             .map_err(|e| FatError::String { | ||||
|                 error: alloc::format!("{:?}", e), | ||||
|                 error: alloc::format!("{e:?}"), | ||||
|             }) | ||||
|     } | ||||
|  | ||||
| @@ -113,7 +113,7 @@ impl BatteryInteraction for BQ34Z100G1 { | ||||
|         self.battery_driver | ||||
|             .remaining_capacity() | ||||
|             .map_err(|e| FatError::String { | ||||
|                 error: alloc::format!("{:?}", e), | ||||
|                 error: alloc::format!("{e:?}"), | ||||
|             }) | ||||
|     } | ||||
|  | ||||
| @@ -121,7 +121,7 @@ impl BatteryInteraction for BQ34Z100G1 { | ||||
|         self.battery_driver | ||||
|             .full_charge_capacity() | ||||
|             .map_err(|e| FatError::String { | ||||
|                 error: alloc::format!("{:?}", e), | ||||
|                 error: alloc::format!("{e:?}"), | ||||
|             }) | ||||
|     } | ||||
|  | ||||
| @@ -129,13 +129,13 @@ impl BatteryInteraction for BQ34Z100G1 { | ||||
|         self.battery_driver | ||||
|             .design_capacity() | ||||
|             .map_err(|e| FatError::String { | ||||
|                 error: alloc::format!("{:?}", e), | ||||
|                 error: alloc::format!("{e:?}"), | ||||
|             }) | ||||
|     } | ||||
|  | ||||
|     async fn voltage_milli_volt(&mut self) -> FatResult<u16> { | ||||
|         self.battery_driver.voltage().map_err(|e| FatError::String { | ||||
|             error: alloc::format!("{:?}", e), | ||||
|             error: alloc::format!("{e:?}"), | ||||
|         }) | ||||
|     } | ||||
|  | ||||
| @@ -143,7 +143,7 @@ impl BatteryInteraction for BQ34Z100G1 { | ||||
|         self.battery_driver | ||||
|             .average_current() | ||||
|             .map_err(|e| FatError::String { | ||||
|                 error: alloc::format!("{:?}", e), | ||||
|                 error: alloc::format!("{e:?}"), | ||||
|             }) | ||||
|     } | ||||
|  | ||||
| @@ -151,7 +151,7 @@ impl BatteryInteraction for BQ34Z100G1 { | ||||
|         self.battery_driver | ||||
|             .cycle_count() | ||||
|             .map_err(|e| FatError::String { | ||||
|                 error: alloc::format!("{:?}", e), | ||||
|                 error: alloc::format!("{e:?}"), | ||||
|             }) | ||||
|     } | ||||
|  | ||||
| @@ -159,7 +159,7 @@ impl BatteryInteraction for BQ34Z100G1 { | ||||
|         self.battery_driver | ||||
|             .state_of_health() | ||||
|             .map_err(|e| FatError::String { | ||||
|                 error: alloc::format!("{:?}", e), | ||||
|                 error: alloc::format!("{e:?}"), | ||||
|             }) | ||||
|     } | ||||
|  | ||||
| @@ -167,7 +167,7 @@ impl BatteryInteraction for BQ34Z100G1 { | ||||
|         self.battery_driver | ||||
|             .temperature() | ||||
|             .map_err(|e| FatError::String { | ||||
|                 error: alloc::format!("{:?}", e), | ||||
|                 error: alloc::format!("{e:?}"), | ||||
|             }) | ||||
|     } | ||||
|  | ||||
| @@ -190,16 +190,16 @@ pub fn print_battery_bq34z100( | ||||
| ) -> FatResult<()> { | ||||
|     log::info!("Try communicating with battery"); | ||||
|     let fwversion = battery_driver.fw_version().unwrap_or_else(|e| { | ||||
|         log::info!("Firmware {:?}", e); | ||||
|         log::info!("Firmware {e:?}"); | ||||
|         0 | ||||
|     }); | ||||
|     log::info!("fw version is {}", fwversion); | ||||
|     log::info!("fw version is {fwversion}"); | ||||
|  | ||||
|     let design_capacity = battery_driver.design_capacity().unwrap_or_else(|e| { | ||||
|         log::info!("Design capacity {:?}", e); | ||||
|         log::info!("Design capacity {e:?}"); | ||||
|         0 | ||||
|     }); | ||||
|     log::info!("Design Capacity {}", design_capacity); | ||||
|     log::info!("Design Capacity {design_capacity}"); | ||||
|     if design_capacity == 1000 { | ||||
|         log::info!("Still stock configuring battery, readouts are likely to be wrong!"); | ||||
|     } | ||||
| @@ -219,39 +219,39 @@ pub fn print_battery_bq34z100( | ||||
|         cf: false, | ||||
|         ocv_taken: false, | ||||
|     }); | ||||
|     log::info!("Flags {:?}", flags); | ||||
|     log::info!("Flags {flags:?}"); | ||||
|  | ||||
|     let chem_id = battery_driver.chem_id().unwrap_or_else(|e| { | ||||
|         log::info!("Chemid {:?}", e); | ||||
|         log::info!("Chemid {e:?}"); | ||||
|         0 | ||||
|     }); | ||||
|  | ||||
|     let bat_temp = battery_driver.internal_temperature().unwrap_or_else(|e| { | ||||
|         log::info!("Bat Temp {:?}", e); | ||||
|         log::info!("Bat Temp {e:?}"); | ||||
|         0 | ||||
|     }); | ||||
|     let temp_c = Temperature::from_kelvin(bat_temp as f64 / 10_f64).as_celsius(); | ||||
|     let voltage = battery_driver.voltage().unwrap_or_else(|e| { | ||||
|         log::info!("Bat volt {:?}", e); | ||||
|         log::info!("Bat volt {e:?}"); | ||||
|         0 | ||||
|     }); | ||||
|     let current = battery_driver.current().unwrap_or_else(|e| { | ||||
|         log::info!("Bat current {:?}", e); | ||||
|         log::info!("Bat current {e:?}"); | ||||
|         0 | ||||
|     }); | ||||
|     let state = battery_driver.state_of_charge().unwrap_or_else(|e| { | ||||
|         log::info!("Bat Soc {:?}", e); | ||||
|         log::info!("Bat Soc {e:?}"); | ||||
|         0 | ||||
|     }); | ||||
|     let charge_voltage = battery_driver.charge_voltage().unwrap_or_else(|e| { | ||||
|         log::info!("Bat Charge Volt {:?}", e); | ||||
|         log::info!("Bat Charge Volt {e:?}"); | ||||
|         0 | ||||
|     }); | ||||
|     let charge_current = battery_driver.charge_current().unwrap_or_else(|e| { | ||||
|         log::info!("Bat Charge Current {:?}", e); | ||||
|         log::info!("Bat Charge Current {e:?}"); | ||||
|         0 | ||||
|     }); | ||||
|     log::info!("ChemId: {} Current voltage {} and current {} with charge {}% and temp {} CVolt: {} CCur {}", chem_id, voltage, current, state, temp_c, charge_voltage, charge_current); | ||||
|     log::info!("ChemId: {chem_id} Current voltage {voltage} and current {current} with charge {state}% and temp {temp_c} CVolt: {charge_voltage} CCur {charge_current}"); | ||||
|     let _ = battery_driver.unsealed(); | ||||
|     let _ = battery_driver.it_enable(); | ||||
|     Ok(()) | ||||
|   | ||||
| @@ -155,7 +155,7 @@ impl Esp<'_> { | ||||
|     pub(crate) async fn delete_file(&self, filename: String) -> FatResult<()> { | ||||
|         let file = PathBuf::try_from(filename.as_str())?; | ||||
|         let access = self.fs.lock().await; | ||||
|         access.remove(&*file)?; | ||||
|         access.remove(&file)?; | ||||
|         Ok(()) | ||||
|     } | ||||
|     pub(crate) async fn write_file( | ||||
| @@ -168,7 +168,7 @@ impl Esp<'_> { | ||||
|         let access = self.fs.lock().await; | ||||
|         access.open_file_with_options_and_then( | ||||
|             |options| options.read(true).write(true).create(true), | ||||
|             &*file, | ||||
|             &file, | ||||
|             |file| { | ||||
|                 file.seek(SeekFrom::Start(offset))?; | ||||
|                 file.write(buf)?; | ||||
| @@ -181,7 +181,7 @@ impl Esp<'_> { | ||||
|     pub async fn get_size(&mut self, filename: String) -> FatResult<usize> { | ||||
|         let file = PathBuf::try_from(filename.as_str())?; | ||||
|         let access = self.fs.lock().await; | ||||
|         let data = access.metadata(&*file)?; | ||||
|         let data = access.metadata(&file)?; | ||||
|         Ok(data.len()) | ||||
|     } | ||||
|     pub(crate) async fn get_file( | ||||
| @@ -198,7 +198,7 @@ impl Esp<'_> { | ||||
|         let offset = chunk * buf.len() as u32; | ||||
|         access.open_file_with_options_and_then( | ||||
|             |options| options.read(true), | ||||
|             &*file, | ||||
|             &file, | ||||
|             |file| { | ||||
|                 let length = file.len()? as u32; | ||||
|                 if length == 0 { | ||||
| @@ -226,7 +226,7 @@ impl Esp<'_> { | ||||
|         self.ota_target.write(offset, buf)?; | ||||
|         self.ota_target.read(offset, read_back)?; | ||||
|         if buf != read_back { | ||||
|             info!("Expected {:?} but got {:?}", buf, read_back); | ||||
|             info!("Expected {buf:?} but got {read_back:?}"); | ||||
|             bail!( | ||||
|                 "Flash error, read back does not match write buffer at offset {:x}", | ||||
|                 offset | ||||
| @@ -238,10 +238,7 @@ impl Esp<'_> { | ||||
|     pub(crate) async fn finalize_ota(&mut self) -> Result<(), FatError> { | ||||
|         let current = self.ota.current_slot()?; | ||||
|         if self.ota.current_ota_state()? != OtaImageState::Valid { | ||||
|             info!( | ||||
|                 "Validating current slot {:?} as it was able to ota", | ||||
|                 current | ||||
|             ); | ||||
|             info!("Validating current slot {current:?} as it was able to ota"); | ||||
|             self.ota.set_current_ota_state(Valid)?; | ||||
|         } | ||||
|  | ||||
| @@ -250,7 +247,7 @@ impl Esp<'_> { | ||||
|         self.ota.set_current_ota_state(OtaImageState::New)?; | ||||
|         info!("switched state for new partition"); | ||||
|         let state_new = self.ota.current_ota_state()?; | ||||
|         info!("state on new partition now {:?}", state_new); | ||||
|         info!("state on new partition now {state_new:?}"); | ||||
|         //determine nextslot crc | ||||
|  | ||||
|         self.set_restart_to_conf(true); | ||||
| @@ -290,7 +287,7 @@ impl Esp<'_> { | ||||
|         if ntp_addrs.is_empty() { | ||||
|             bail!("Failed to resolve DNS"); | ||||
|         } | ||||
|         info!("NTP server: {:?}", ntp_addrs); | ||||
|         info!("NTP server: {ntp_addrs:?}"); | ||||
|  | ||||
|         let mut counter = 0; | ||||
|         loop { | ||||
| @@ -302,12 +299,12 @@ impl Esp<'_> { | ||||
|             match timeout { | ||||
|                 Ok(result) => { | ||||
|                     let time = result?; | ||||
|                     info!("Time: {:?}", time); | ||||
|                     info!("Time: {time:?}"); | ||||
|                     return DateTime::from_timestamp(time.seconds as i64, 0) | ||||
|                         .context("Could not convert Sntp result"); | ||||
|                 } | ||||
|                 Err(err) => { | ||||
|                     warn!("sntp timeout, retry: {:?}", err); | ||||
|                     warn!("sntp timeout, retry: {err:?}"); | ||||
|                     counter += 1; | ||||
|                     if counter > 10 { | ||||
|                         bail!("Failed to get time from NTP server"); | ||||
| @@ -426,7 +423,7 @@ impl Esp<'_> { | ||||
|         println!("start net task"); | ||||
|         spawner.spawn(net_task(runner)).ok(); | ||||
|         println!("run dhcp"); | ||||
|         spawner.spawn(run_dhcp(stack.clone(), gw_ip_addr_str)).ok(); | ||||
|         spawner.spawn(run_dhcp(*stack, gw_ip_addr_str)).ok(); | ||||
|  | ||||
|         loop { | ||||
|             if stack.is_link_up() { | ||||
| @@ -442,7 +439,7 @@ impl Esp<'_> { | ||||
|             .config_v4() | ||||
|             .inspect(|c| println!("ipv4 config: {c:?}")); | ||||
|  | ||||
|         Ok(stack.clone()) | ||||
|         Ok(*stack) | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn wifi( | ||||
| @@ -505,12 +502,9 @@ impl Esp<'_> { | ||||
|         } + max_wait as u64 * 1000; | ||||
|         loop { | ||||
|             let state = esp_wifi::wifi::sta_state(); | ||||
|             match state { | ||||
|                 WifiState::StaStarted => { | ||||
|                     self.controller.lock().await.connect()?; | ||||
|                     break; | ||||
|                 } | ||||
|                 _ => {} | ||||
|             if state == WifiState::StaStarted { | ||||
|                 self.controller.lock().await.connect()?; | ||||
|                 break; | ||||
|             } | ||||
|             if { | ||||
|                 let guard = TIME_ACCESS.get().await.lock().await; | ||||
| @@ -527,11 +521,8 @@ impl Esp<'_> { | ||||
|         } + max_wait as u64 * 1000; | ||||
|         loop { | ||||
|             let state = esp_wifi::wifi::sta_state(); | ||||
|             match state { | ||||
|                 WifiState::StaConnected => { | ||||
|                     break; | ||||
|                 } | ||||
|                 _ => {} | ||||
|             if state == WifiState::StaConnected { | ||||
|                 break; | ||||
|             } | ||||
|             if { | ||||
|                 let guard = TIME_ACCESS.get().await.lock().await; | ||||
| @@ -572,7 +563,7 @@ impl Esp<'_> { | ||||
|         } | ||||
|  | ||||
|         info!("Connected WIFI, dhcp: {:?}", stack.config_v4()); | ||||
|         Ok(stack.clone()) | ||||
|         Ok(*stack) | ||||
|     } | ||||
|  | ||||
|     pub fn deep_sleep( | ||||
| @@ -611,12 +602,12 @@ impl Esp<'_> { | ||||
|         } | ||||
|         let data = self.fs.lock().await.read::<4096>(&cfg)?; | ||||
|         let config: PlantControllerConfig = serde_json::from_slice(&data)?; | ||||
|         return Ok(config); | ||||
|         Ok(config) | ||||
|     } | ||||
|     pub(crate) async fn save_config(&mut self, config: Vec<u8>) -> FatResult<()> { | ||||
|         let filesystem = self.fs.lock().await; | ||||
|         let cfg = PathBuf::try_from(CONFIG_FILE)?; | ||||
|         filesystem.write(&cfg, &*config)?; | ||||
|         filesystem.write(&cfg, &config)?; | ||||
|         Ok(()) | ||||
|     } | ||||
|     pub(crate) async fn list_files(&self) -> FatResult<FileList> { | ||||
| @@ -690,19 +681,15 @@ impl Esp<'_> { | ||||
|                         "", | ||||
|                     ) | ||||
|                     .await; | ||||
|                 for i in 0..PLANT_COUNT { | ||||
|                     log::info!( | ||||
|                         "LAST_WATERING_TIMESTAMP[{}] = UTC {}", | ||||
|                         i, | ||||
|                         LAST_WATERING_TIMESTAMP[i] | ||||
|                     ); | ||||
|                 // is executed before main, no other code will alter these values during printing | ||||
|                 #[allow(static_mut_refs)] | ||||
|                 for (i, time) in LAST_WATERING_TIMESTAMP.iter().enumerate() { | ||||
|                     log::info!("LAST_WATERING_TIMESTAMP[{i}] = UTC {time}"); | ||||
|                 } | ||||
|                 for i in 0..PLANT_COUNT { | ||||
|                     log::info!( | ||||
|                         "CONSECUTIVE_WATERING_PLANT[{}] = {}", | ||||
|                         i, | ||||
|                         CONSECUTIVE_WATERING_PLANT[i] | ||||
|                     ); | ||||
|                 // is executed before main, no other code will alter these values during printing | ||||
|                 #[allow(static_mut_refs)] | ||||
|                 for (i, item) in CONSECUTIVE_WATERING_PLANT.iter().enumerate() { | ||||
|                     log::info!("CONSECUTIVE_WATERING_PLANT[{i}] = {item}"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -734,9 +721,9 @@ impl Esp<'_> { | ||||
|             bail!("Mqtt url was empty") | ||||
|         } | ||||
|  | ||||
|         let last_will_topic = format!("{}/state", base_topic); | ||||
|         let round_trip_topic = format!("{}/internal/roundtrip", base_topic); | ||||
|         let stay_alive_topic = format!("{}/stay_alive", base_topic); | ||||
|         let last_will_topic = format!("{base_topic}/state"); | ||||
|         let round_trip_topic = format!("{base_topic}/internal/roundtrip"); | ||||
|         let stay_alive_topic = format!("{base_topic}/stay_alive"); | ||||
|  | ||||
|         let mut builder: McutieBuilder<'_, String, PublishDisplay<String, &str>, 0> = | ||||
|             McutieBuilder::new(stack, "plant ctrl", mqtt_url); | ||||
| @@ -879,8 +866,7 @@ impl Esp<'_> { | ||||
|             Ok(()) => {} | ||||
|             Err(err) => { | ||||
|                 info!( | ||||
|                     "Error during mqtt send on topic {} with message {:#?} error is {:?}", | ||||
|                     subtopic, message, err | ||||
|                     "Error during mqtt send on topic {subtopic} with message {message:#?} error is {err:?}" | ||||
|                 ); | ||||
|             } | ||||
|         }; | ||||
| @@ -932,7 +918,7 @@ async fn mqtt_incoming_task( | ||||
|                         LOG_ACCESS | ||||
|                             .lock() | ||||
|                             .await | ||||
|                             .log(LogMessage::UnknownTopic, 0, 0, "", &*topic) | ||||
|                             .log(LogMessage::UnknownTopic, 0, 0, "", &topic) | ||||
|                             .await; | ||||
|                     } | ||||
|                 } | ||||
|   | ||||
| @@ -121,7 +121,6 @@ impl<'a> BoardInteraction<'a> for Initial<'a> { | ||||
|         bail!("Please configure board revision") | ||||
|     } | ||||
|  | ||||
|  | ||||
|     async fn general_fault(&mut self, enable: bool) { | ||||
|         self.general_fault.set_level(enable.into()); | ||||
|     } | ||||
|   | ||||
| @@ -24,7 +24,7 @@ impl lfs2Storage for LittleFs2Filesystem { | ||||
|     fn read(&mut self, off: usize, buf: &mut [u8]) -> lfs2Result<usize> { | ||||
|         let read_size: usize = Self::READ_SIZE; | ||||
|         if off % read_size != 0 { | ||||
|             error!("Littlefs2Filesystem read error: offset not aligned to read size offset: {} read_size: {}", off, read_size); | ||||
|             error!("Littlefs2Filesystem read error: offset not aligned to read size offset: {off} read_size: {read_size}"); | ||||
|             return Err(lfs2Error::IO); | ||||
|         } | ||||
|         if buf.len() % read_size != 0 { | ||||
| @@ -34,7 +34,7 @@ impl lfs2Storage for LittleFs2Filesystem { | ||||
|         match self.storage.read(off as u32, buf) { | ||||
|             Ok(..) => Ok(buf.len()), | ||||
|             Err(err) => { | ||||
|                 error!("Littlefs2Filesystem read error: {:?}", err); | ||||
|                 error!("Littlefs2Filesystem read error: {err:?}"); | ||||
|                 Err(lfs2Error::IO) | ||||
|             } | ||||
|         } | ||||
| @@ -43,7 +43,7 @@ impl lfs2Storage for LittleFs2Filesystem { | ||||
|     fn write(&mut self, off: usize, data: &[u8]) -> lfs2Result<usize> { | ||||
|         let write_size: usize = Self::WRITE_SIZE; | ||||
|         if off % write_size != 0 { | ||||
|             error!("Littlefs2Filesystem write error: offset not aligned to write size offset: {} write_size: {}", off, write_size); | ||||
|             error!("Littlefs2Filesystem write error: offset not aligned to write size offset: {off} write_size: {write_size}"); | ||||
|             return Err(lfs2Error::IO); | ||||
|         } | ||||
|         if data.len() % write_size != 0 { | ||||
| @@ -53,7 +53,7 @@ impl lfs2Storage for LittleFs2Filesystem { | ||||
|         match self.storage.write(off as u32, data) { | ||||
|             Ok(..) => Ok(data.len()), | ||||
|             Err(err) => { | ||||
|                 error!("Littlefs2Filesystem write error: {:?}", err); | ||||
|                 error!("Littlefs2Filesystem write error: {err:?}"); | ||||
|                 Err(lfs2Error::IO) | ||||
|             } | ||||
|         } | ||||
| @@ -62,25 +62,25 @@ impl lfs2Storage for LittleFs2Filesystem { | ||||
|     fn erase(&mut self, off: usize, len: usize) -> lfs2Result<usize> { | ||||
|         let block_size: usize = Self::BLOCK_SIZE; | ||||
|         if off % block_size != 0 { | ||||
|             error!("Littlefs2Filesystem erase error: offset not aligned to block size offset: {} block_size: {}", off, block_size); | ||||
|             error!("Littlefs2Filesystem erase error: offset not aligned to block size offset: {off} block_size: {block_size}"); | ||||
|             return lfs2Result::Err(lfs2Error::IO); | ||||
|         } | ||||
|         if len % block_size != 0 { | ||||
|             error!("Littlefs2Filesystem erase error: length not aligned to block size length: {} block_size: {}", len, block_size); | ||||
|             error!("Littlefs2Filesystem erase error: length not aligned to block size length: {len} block_size: {block_size}"); | ||||
|             return lfs2Result::Err(lfs2Error::IO); | ||||
|         } | ||||
|  | ||||
|         match check_erase(self.storage, off as u32, (off+len) as u32) { | ||||
|         match check_erase(self.storage, off as u32, (off + len) as u32) { | ||||
|             Ok(_) => {} | ||||
|             Err(err) => { | ||||
|                 error!("Littlefs2Filesystem check erase error: {:?}", err); | ||||
|                 error!("Littlefs2Filesystem check erase error: {err:?}"); | ||||
|                 return lfs2Result::Err(lfs2Error::IO); | ||||
|             } | ||||
|         } | ||||
|         match self.storage.erase(off as u32, (off + len) as u32) { | ||||
|             Ok(..) => lfs2Result::Ok(len), | ||||
|             Err(err) => { | ||||
|                 error!("Littlefs2Filesystem erase error: {:?}", err); | ||||
|                 error!("Littlefs2Filesystem erase error: {err:?}"); | ||||
|                 lfs2Result::Err(lfs2Error::IO) | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -58,9 +58,9 @@ use alloc::sync::Arc; | ||||
| use async_trait::async_trait; | ||||
| use bincode::{Decode, Encode}; | ||||
| use bq34z100::Bq34z100g1Driver; | ||||
| use canapi::SensorSlot; | ||||
| use chrono::{DateTime, FixedOffset, Utc}; | ||||
| use core::cell::RefCell; | ||||
| use canapi::SensorSlot; | ||||
| use ds323x::ic::DS3231; | ||||
| use ds323x::interface::I2cInterface; | ||||
| use ds323x::{DateTimeAccess, Ds323x}; | ||||
| @@ -108,7 +108,6 @@ use log::{error, info, warn}; | ||||
| use portable_atomic::AtomicBool; | ||||
| use serde::Serialize; | ||||
|  | ||||
|  | ||||
| pub static TIME_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, Rtc>> = OnceLock::new(); | ||||
|  | ||||
| //Only support for 8 right now! | ||||
| @@ -127,9 +126,9 @@ pub enum Sensor { | ||||
|     B, | ||||
| } | ||||
|  | ||||
| impl Into<SensorSlot> for Sensor { | ||||
|     fn into(self) -> SensorSlot { | ||||
|         match self { | ||||
| impl From<Sensor> for SensorSlot { | ||||
|     fn from(val: Sensor) -> Self { | ||||
|         match val { | ||||
|             Sensor::A => SensorSlot::A, | ||||
|             Sensor::B => SensorSlot::B, | ||||
|         } | ||||
| @@ -138,6 +137,7 @@ impl Into<SensorSlot> for Sensor { | ||||
|  | ||||
| pub struct PlantHal {} | ||||
|  | ||||
| #[allow(clippy::upper_case_acronyms)] | ||||
| pub struct HAL<'a> { | ||||
|     pub board_hal: Box<dyn BoardInteraction<'a> + Send>, | ||||
| } | ||||
| @@ -177,17 +177,17 @@ pub trait BoardInteraction<'a> { | ||||
|         let current = counter % PLANT_COUNT as u32; | ||||
|         for led in 0..PLANT_COUNT { | ||||
|             if let Err(err) = self.fault(led, current == led as u32).await { | ||||
|                 warn!("Fault on plant {}: {:?}", led, err); | ||||
|                 warn!("Fault on plant {led}: {err:?}"); | ||||
|             } | ||||
|         } | ||||
|         let even = counter % 2 == 0; | ||||
|         let _ = self.general_fault(even.into()).await; | ||||
|         let _ = self.general_fault(even).await; | ||||
|     } | ||||
|  | ||||
|     async fn clear_progress(&mut self) { | ||||
|         for led in 0..PLANT_COUNT { | ||||
|             if let Err(err) = self.fault(led, false).await { | ||||
|                 warn!("Fault on plant {}: {:?}", led, err); | ||||
|                 warn!("Fault on plant {led}: {err:?}"); | ||||
|             } | ||||
|         } | ||||
|         let _ = self.general_fault(false).await; | ||||
| @@ -272,11 +272,11 @@ impl PlantHal { | ||||
|         let timg0 = TimerGroup::new(peripherals.TIMG0); | ||||
|         let esp_wifi_ctrl = &*mk_static!( | ||||
|             EspWifiController<'static>, | ||||
|             init(timg0.timer0, rng.clone()).expect("Could not init wifi controller") | ||||
|             init(timg0.timer0, rng).expect("Could not init wifi controller") | ||||
|         ); | ||||
|  | ||||
|         let (controller, interfaces) = | ||||
|             esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).expect("Could not init wifi"); | ||||
|             esp_wifi::wifi::new(esp_wifi_ctrl, peripherals.WIFI).expect("Could not init wifi"); | ||||
|  | ||||
|         use esp_hal::timer::systimer::SystemTimer; | ||||
|         esp_hal_embassy::init(systimer.alarm0); | ||||
| @@ -351,9 +351,9 @@ impl PlantHal { | ||||
|         let running = get_current_slot_and_fix_ota_data(&mut ota, state_0, state_1)?; | ||||
|         let target = running.next(); | ||||
|  | ||||
|         info!("Currently running OTA slot: {:?}", running); | ||||
|         info!("Slot0 state: {:?}", state_0); | ||||
|         info!("Slot1 state: {:?}", state_1); | ||||
|         info!("Currently running OTA slot: {running:?}"); | ||||
|         info!("Slot0 state: {state_0:?}"); | ||||
|         info!("Slot1 state: {state_1:?}"); | ||||
|  | ||||
|         //obtain current_state and next_state here! | ||||
|         let ota_target = match target { | ||||
| @@ -401,11 +401,12 @@ impl PlantHal { | ||||
|                     log::info!("Littlefs2 filesystem is formatted"); | ||||
|                 } | ||||
|                 Err(err) => { | ||||
|                     error!("Littlefs2 filesystem could not be formatted: {:?}", err); | ||||
|                     error!("Littlefs2 filesystem could not be formatted: {err:?}"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #[allow(clippy::arc_with_non_send_sync)] | ||||
|         let fs = Arc::new(Mutex::new( | ||||
|             lfs2Filesystem::mount(alloc, lfs2filesystem).expect("Could not mount lfs2 filesystem"), | ||||
|         )); | ||||
| @@ -498,8 +499,8 @@ impl PlantHal { | ||||
|         I2C_DRIVER.init(i2c_bus).expect("Could not init i2c driver"); | ||||
|  | ||||
|         let i2c_bus = I2C_DRIVER.get().await; | ||||
|         let rtc_device = I2cDevice::new(&i2c_bus); | ||||
|         let eeprom_device = I2cDevice::new(&i2c_bus); | ||||
|         let rtc_device = I2cDevice::new(i2c_bus); | ||||
|         let eeprom_device = I2cDevice::new(i2c_bus); | ||||
|  | ||||
|         let mut rtc: Ds323x< | ||||
|             I2cInterface<I2cDevice<CriticalSectionRawMutex, I2c<Blocking>>>, | ||||
| @@ -511,10 +512,10 @@ impl PlantHal { | ||||
|         let rtc_time = rtc.datetime(); | ||||
|         match rtc_time { | ||||
|             Ok(tt) => { | ||||
|                 log::info!("Rtc Module reports time at UTC {}", tt); | ||||
|                 log::info!("Rtc Module reports time at UTC {tt}"); | ||||
|             } | ||||
|             Err(err) => { | ||||
|                 log::info!("Rtc Module could not be read {:?}", err); | ||||
|                 log::info!("Rtc Module could not be read {err:?}"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -566,7 +567,7 @@ impl PlantHal { | ||||
|                     }; | ||||
|  | ||||
|                 let board_hal: Box<dyn BoardInteraction + Send> = match config.hardware.board { | ||||
|                     BoardVersion::INITIAL => { | ||||
|                     BoardVersion::Initial => { | ||||
|                         initial_hal::create_initial_board(free_pins, config, esp)? | ||||
|                     } | ||||
|                     BoardVersion::V3 => { | ||||
| @@ -620,8 +621,8 @@ fn ota_state(slot: ota_slot, ota_data: &mut FlashRegion<FlashStorage>) -> OtaIma | ||||
|         let _ = ota_data.read(0x1000, &mut slot_buf); | ||||
|     } | ||||
|     let raw_state = u32::from_le_bytes(slot_buf[24..28].try_into().unwrap_or([0xff; 4])); | ||||
|     let state0 = OtaImageState::try_from(raw_state).unwrap_or(OtaImageState::Undefined); | ||||
|     state0 | ||||
|  | ||||
|     OtaImageState::try_from(raw_state).unwrap_or(OtaImageState::Undefined) | ||||
| } | ||||
|  | ||||
| fn get_current_slot_and_fix_ota_data( | ||||
| @@ -666,10 +667,7 @@ fn get_current_slot_and_fix_ota_data( | ||||
|             } | ||||
|             _ => {} | ||||
|         } | ||||
|         info!( | ||||
|             "Current slot has state {:?} other state has {:?} swapping", | ||||
|             state, other | ||||
|         ); | ||||
|         info!("Current slot has state {state:?} other state has {other:?} swapping"); | ||||
|         ota.set_current_slot(current.next())?; | ||||
|         //we actually booted other slot, than partition table assumes | ||||
|         return Ok(ota.current_slot()?); | ||||
| @@ -699,17 +697,17 @@ pub async fn esp_set_time(time: DateTime<FixedOffset>) -> FatResult<()> { | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize)] | ||||
| pub struct Moistures{ | ||||
| pub struct Moistures { | ||||
|     pub sensor_a_hz: [f32; PLANT_COUNT], | ||||
|     pub sensor_b_hz: [f32; PLANT_COUNT], | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)] | ||||
| pub struct DetectionResult { | ||||
|     plant: [DetectionSensorResult; crate::hal::PLANT_COUNT] | ||||
|     plant: [DetectionSensorResult; crate::hal::PLANT_COUNT], | ||||
| } | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)] | ||||
| pub struct DetectionSensorResult{ | ||||
| pub struct DetectionSensorResult { | ||||
|     sensor_a: bool, | ||||
|     sensor_b: bool, | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use crate::hal::Box; | ||||
| use crate::fat_error::FatResult; | ||||
| use crate::hal::Box; | ||||
| use async_trait::async_trait; | ||||
| use bincode::config::Configuration; | ||||
| use bincode::{config, Decode, Encode}; | ||||
| @@ -65,7 +65,7 @@ impl RTCModuleInteraction for DS3231Module { | ||||
|         let (header, len): (BackupHeader, usize) = | ||||
|             bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?; | ||||
|  | ||||
|         log::info!("Raw header is {:?} with size {}", header_page_buffer, len); | ||||
|         log::info!("Raw header is {header_page_buffer:?} with size {len}"); | ||||
|         Ok(header) | ||||
|     } | ||||
|  | ||||
| @@ -97,7 +97,7 @@ impl RTCModuleInteraction for DS3231Module { | ||||
|     async fn backup_config(&mut self, offset: usize, bytes: &[u8]) -> FatResult<()> { | ||||
|         //skip header and write after | ||||
|         self.storage | ||||
|             .write((BACKUP_HEADER_MAX_SIZE + offset) as u32, &bytes)?; | ||||
|             .write((BACKUP_HEADER_MAX_SIZE + offset) as u32, bytes)?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| @@ -113,11 +113,7 @@ impl RTCModuleInteraction for DS3231Module { | ||||
|         }; | ||||
|         let config = config::standard(); | ||||
|         let encoded = bincode::encode_into_slice(&header, &mut header_page_buffer, config)?; | ||||
|         log::info!( | ||||
|             "Raw header is {:?} with size {}", | ||||
|             header_page_buffer, | ||||
|             encoded | ||||
|         ); | ||||
|         log::info!("Raw header is {header_page_buffer:?} with size {encoded}"); | ||||
|         self.storage.write(0, &header_page_buffer)?; | ||||
|         Ok(()) | ||||
|     } | ||||
|   | ||||
| @@ -171,10 +171,13 @@ pub(crate) fn create_v3( | ||||
| } | ||||
|  | ||||
| impl V3<'_> { | ||||
|  | ||||
|     async fn inner_measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32, FatError> { | ||||
|     async fn inner_measure_moisture_hz( | ||||
|         &mut self, | ||||
|         plant: usize, | ||||
|         sensor: Sensor, | ||||
|     ) -> Result<f32, FatError> { | ||||
|         let mut results = [0_f32; REPEAT_MOIST_MEASURE]; | ||||
|         for repeat in 0..REPEAT_MOIST_MEASURE { | ||||
|         for sample in results.iter_mut() { | ||||
|             self.signal_counter.pause(); | ||||
|             self.signal_counter.clear(); | ||||
|             //Disable all | ||||
| @@ -266,7 +269,7 @@ impl V3<'_> { | ||||
|                     &format!("{sensor:?}"), | ||||
|                 ) | ||||
|                 .await; | ||||
|             results[repeat] = hz; | ||||
|             *sample = hz; | ||||
|         } | ||||
|         results.sort_by(|a, b| a.partial_cmp(b).unwrap()); // floats don't seem to implement total_ord | ||||
|  | ||||
| @@ -386,12 +389,18 @@ impl<'a> BoardInteraction<'a> for V3<'a> { | ||||
|         for plant in 0..PLANT_COUNT { | ||||
|             let a = self.inner_measure_moisture_hz(plant, Sensor::A).await; | ||||
|             let b = self.inner_measure_moisture_hz(plant, Sensor::B).await; | ||||
|             let aa = a.unwrap_or_else(|_| u32::MAX as f32); | ||||
|             let bb = b.unwrap_or_else(|_| u32::MAX as f32); | ||||
|             let aa = a.unwrap_or(u32::MAX as f32); | ||||
|             let bb = b.unwrap_or(u32::MAX as f32); | ||||
|             LOG_ACCESS | ||||
|                 .lock() | ||||
|                 .await | ||||
|                 .log(LogMessage::TestSensor, aa as u32, bb as u32, &plant.to_string(), "") | ||||
|                 .log( | ||||
|                     LogMessage::TestSensor, | ||||
|                     aa as u32, | ||||
|                     bb as u32, | ||||
|                     &plant.to_string(), | ||||
|                     "", | ||||
|                 ) | ||||
|                 .await; | ||||
|             result.sensor_a_hz[plant] = aa; | ||||
|             result.sensor_b_hz[plant] = bb; | ||||
| @@ -399,7 +408,6 @@ impl<'a> BoardInteraction<'a> for V3<'a> { | ||||
|         Ok(result) | ||||
|     } | ||||
|  | ||||
|  | ||||
|     async fn general_fault(&mut self, enable: bool) { | ||||
|         hold_disable(6); | ||||
|         if enable { | ||||
|   | ||||
| @@ -6,7 +6,10 @@ use crate::hal::esp::{hold_disable, hold_enable, Esp}; | ||||
| use crate::hal::rtc::RTCModuleInteraction; | ||||
| use crate::hal::v4_sensor::{SensorImpl, SensorInteraction}; | ||||
| use crate::hal::water::TankSensor; | ||||
| use crate::hal::{BoardInteraction, DetectionResult, FreePeripherals, Moistures, I2C_DRIVER, PLANT_COUNT, TIME_ACCESS}; | ||||
| use crate::hal::{ | ||||
|     BoardInteraction, DetectionResult, FreePeripherals, Moistures, I2C_DRIVER, PLANT_COUNT, | ||||
|     TIME_ACCESS, | ||||
| }; | ||||
| use crate::log::{LogMessage, LOG_ACCESS}; | ||||
| use alloc::boxed::Box; | ||||
| use alloc::string::ToString; | ||||
| @@ -74,35 +77,29 @@ impl<'a> Charger<'a> { | ||||
|  | ||||
| impl Charger<'_> { | ||||
|     pub(crate) fn power_save(&mut self) { | ||||
|         match self { | ||||
|             Charger::SolarMpptV1 { mppt_ina, .. } => { | ||||
|                 let _ = mppt_ina | ||||
|                     .set_configuration(Configuration { | ||||
|                         reset: Default::default(), | ||||
|                         bus_voltage_range: Default::default(), | ||||
|                         shunt_voltage_range: Default::default(), | ||||
|                         bus_resolution: Default::default(), | ||||
|                         shunt_resolution: Default::default(), | ||||
|                         operating_mode: OperatingMode::PowerDown, | ||||
|                     }) | ||||
|                     .map_err(|e| { | ||||
|                         log::info!( | ||||
|                     "Error setting ina mppt configuration during deep sleep preparation{:?}", | ||||
|                     e | ||||
|                 ); | ||||
|                     }); | ||||
|             } | ||||
|             _ => {} | ||||
|         if let Charger::SolarMpptV1 { mppt_ina, .. } = self { | ||||
|             let _ = mppt_ina | ||||
|                 .set_configuration(Configuration { | ||||
|                     reset: Default::default(), | ||||
|                     bus_voltage_range: Default::default(), | ||||
|                     shunt_voltage_range: Default::default(), | ||||
|                     bus_resolution: Default::default(), | ||||
|                     shunt_resolution: Default::default(), | ||||
|                     operating_mode: OperatingMode::PowerDown, | ||||
|                 }) | ||||
|                 .map_err(|e| { | ||||
|                     log::info!( | ||||
|                         "Error setting ina mppt configuration during deep sleep preparation{e:?}" | ||||
|                     ); | ||||
|                 }); | ||||
|         } | ||||
|     } | ||||
|     fn set_charge_indicator(&mut self, charging: bool) -> FatResult<()> { | ||||
|         match self { | ||||
|             Self::SolarMpptV1 { | ||||
|                 charge_indicator, .. | ||||
|             } => { | ||||
|                 charge_indicator.set_level(charging.into()); | ||||
|             } | ||||
|             _ => {} | ||||
|         if let Self::SolarMpptV1 { | ||||
|             charge_indicator, .. | ||||
|         } = self | ||||
|         { | ||||
|             charge_indicator.set_level(charging.into()); | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| @@ -244,7 +241,7 @@ pub(crate) async fn create_v4( | ||||
|             Some(ina) | ||||
|         } | ||||
|         Err(err) => { | ||||
|             log::info!("Error creating mppt ina: {:?}", err); | ||||
|             log::info!("Error creating mppt ina: {err:?}"); | ||||
|             None | ||||
|         } | ||||
|     }; | ||||
| @@ -253,7 +250,7 @@ pub(crate) async fn create_v4( | ||||
|     let pump_ina = match SyncIna219::new(pump_current_dev, Address::from_pins(Pin::Gnd, Pin::Sda)) { | ||||
|         Ok(ina) => Some(ina), | ||||
|         Err(err) => { | ||||
|             log::info!("Error creating pump ina: {:?}", err); | ||||
|             log::info!("Error creating pump ina: {err:?}"); | ||||
|             None | ||||
|         } | ||||
|     }; | ||||
| @@ -362,7 +359,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> { | ||||
|                 let v = pump_ina | ||||
|                     .shunt_voltage() | ||||
|                     .map_err(|e| FatError::String { | ||||
|                         error: alloc::format!("{:?}", e), | ||||
|                         error: alloc::format!("{e:?}"), | ||||
|                     }) | ||||
|                     .map(|v| { | ||||
|                         let shunt_voltage = | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET}; | ||||
| use crate::bail; | ||||
| use crate::fat_error::{ContextExt, FatError, FatResult}; | ||||
| use canapi::{SensorSlot}; | ||||
| use crate::hal::{DetectionResult, Moistures, Sensor}; | ||||
| use crate::hal::Box; | ||||
| use crate::hal::{DetectionResult, Moistures, Sensor}; | ||||
| use crate::log::{LogMessage, LOG_ACCESS}; | ||||
| use alloc::format; | ||||
| use alloc::string::ToString; | ||||
| use async_trait::async_trait; | ||||
| use bincode::config; | ||||
| use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET}; | ||||
| use canapi::SensorSlot; | ||||
| use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_time::{Duration, Instant, Timer, WithTimeout}; | ||||
| @@ -23,8 +23,6 @@ use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; | ||||
|  | ||||
| const REPEAT_MOIST_MEASURE: usize = 10; | ||||
|  | ||||
|  | ||||
|  | ||||
| #[async_trait(?Send)] | ||||
| pub trait SensorInteraction { | ||||
|     async fn measure_moisture_hz(&mut self) -> FatResult<Moistures>; | ||||
| @@ -59,11 +57,25 @@ impl SensorInteraction for SensorImpl { | ||||
|                 .. | ||||
|             } => { | ||||
|                 let mut result = Moistures::default(); | ||||
|                 for plant in 0..crate::hal::PLANT_COUNT{ | ||||
|                     result.sensor_a_hz[plant] =  Self::inner_pulse(plant, Sensor::A, signal_counter, sensor_expander).await?; | ||||
|                     info!("Sensor {} {:?}: {}", plant, Sensor::A, result.sensor_a_hz[plant]); | ||||
|                     result.sensor_b_hz[plant] =  Self::inner_pulse(plant, Sensor::B, signal_counter, sensor_expander).await?; | ||||
|                     info!("Sensor {} {:?}: {}", plant, Sensor::B, result.sensor_b_hz[plant]); | ||||
|                 for plant in 0..crate::hal::PLANT_COUNT { | ||||
|                     result.sensor_a_hz[plant] = | ||||
|                         Self::inner_pulse(plant, Sensor::A, signal_counter, sensor_expander) | ||||
|                             .await?; | ||||
|                     info!( | ||||
|                         "Sensor {} {:?}: {}", | ||||
|                         plant, | ||||
|                         Sensor::A, | ||||
|                         result.sensor_a_hz[plant] | ||||
|                     ); | ||||
|                     result.sensor_b_hz[plant] = | ||||
|                         Self::inner_pulse(plant, Sensor::B, signal_counter, sensor_expander) | ||||
|                             .await?; | ||||
|                     info!( | ||||
|                         "Sensor {} {:?}: {}", | ||||
|                         plant, | ||||
|                         Sensor::B, | ||||
|                         result.sensor_b_hz[plant] | ||||
|                     ); | ||||
|                 } | ||||
|                 Ok(result) | ||||
|             } | ||||
| @@ -81,7 +93,7 @@ impl SensorInteraction for SensorImpl { | ||||
|                     match rec { | ||||
|                         Ok(_) => {} | ||||
|                         Err(err) => { | ||||
|                             info!("Error receiving CAN message: {:?}", err); | ||||
|                             info!("Error receiving CAN message: {err:?}"); | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
| @@ -102,8 +114,6 @@ impl SensorInteraction for SensorImpl { | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| impl SensorImpl { | ||||
|     pub async fn autodetect(&mut self) -> FatResult<DetectionResult> { | ||||
|         match self { | ||||
| @@ -124,164 +134,176 @@ impl SensorImpl { | ||||
|                 // Send a few test messages per potential sensor node | ||||
|                 for plant in 0..crate::hal::PLANT_COUNT { | ||||
|                     for sensor in [Sensor::A, Sensor::B] { | ||||
|                         let target = StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, sensor.into(),  plant as u16)).context(">> Could not create address for sensor! (plant: {}) <<")?; | ||||
|                         let target = StandardId::new(plant_id( | ||||
|                             IDENTIFY_CMD_OFFSET, | ||||
|                             sensor.into(), | ||||
|                             plant as u16, | ||||
|                         )) | ||||
|                         .context(">> Could not create address for sensor! (plant: {}) <<")?; | ||||
|                         let can_buffer = [0_u8; 0]; | ||||
|                         if let Some(frame) = EspTwaiFrame::new(target, &can_buffer) { | ||||
|                             // Try a few times; we intentionally ignore rx here and rely on stub logic | ||||
|                             let resu = as_async.transmit_async(&frame).await; | ||||
|                             match resu { | ||||
|                                 Ok(_) => { | ||||
|                                     info!( | ||||
|                                         "Sent test message to plant {} sensor {:?}", | ||||
|                                         plant, sensor | ||||
|                                     ); | ||||
|                                     info!("Sent test message to plant {plant} sensor {sensor:?}"); | ||||
|                                 } | ||||
|                                 Err(err) => { | ||||
|                                     info!("Error sending test message to plant {} sensor {:?}: {:?}", plant, sensor, err); | ||||
|                                     info!( | ||||
|                                         "Error sending test message to plant {plant} sensor {sensor:?}: {err:?}" | ||||
|                                     ); | ||||
|                                 } | ||||
|                             } | ||||
|                         } else { | ||||
|                             info!("Error building CAN frame"); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 let mut result = DetectionResult::default(); | ||||
|                 // Wait for messages to arrive | ||||
|                 let _ = Self::wait_for_can_measurements(&mut as_async, &mut result).with_timeout(Duration::from_millis(5000)).await; | ||||
|                 let _ = Self::wait_for_can_measurements(&mut as_async, &mut result) | ||||
|                     .with_timeout(Duration::from_millis(5000)) | ||||
|                     .await; | ||||
|  | ||||
|                 let config = as_async.stop().into_blocking(); | ||||
|                 can_power.set_low(); | ||||
|                 twai_config.replace(config); | ||||
|  | ||||
|                 info!("Autodetection result: {:?}", result); | ||||
|                 info!("Autodetection result: {result:?}"); | ||||
|                 Ok(result) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn wait_for_can_measurements(as_async: &mut Twai<'_, Async>, result: &mut DetectionResult) { | ||||
|     async fn wait_for_can_measurements( | ||||
|         as_async: &mut Twai<'_, Async>, | ||||
|         result: &mut DetectionResult, | ||||
|     ) { | ||||
|         loop { | ||||
|             match as_async.receive_async().await { | ||||
|                 Ok(can_frame) => { | ||||
|                     match can_frame.id() { | ||||
|                         Id::Standard(id) => { | ||||
|                             info!("Received CAN message: {:?}", id); | ||||
|                             let rawid = id.as_raw(); | ||||
|                             match classify(rawid) { | ||||
|                                 None => {} | ||||
|                                 Some(msg) => { | ||||
|                                     info!("received message of kind {:?} (plant: {}, sensor: {:?})", msg.0, msg.1, msg.2); | ||||
|                                     if msg.0 == MessageKind::MoistureData { | ||||
|                                         let plant = msg.1 as usize; | ||||
|                                         let sensor = msg.2; | ||||
|                                         match sensor { | ||||
|                                             SensorSlot::A => { | ||||
|                                                 result.plant[plant].sensor_a = true; | ||||
|                                             } | ||||
|                                             SensorSlot::B => { | ||||
|                                                 result.plant[plant].sensor_b = true; | ||||
|                                             } | ||||
|                 Ok(can_frame) => match can_frame.id() { | ||||
|                     Id::Standard(id) => { | ||||
|                         info!("Received CAN message: {id:?}"); | ||||
|                         let rawid = id.as_raw(); | ||||
|                         match classify(rawid) { | ||||
|                             None => {} | ||||
|                             Some(msg) => { | ||||
|                                 info!( | ||||
|                                     "received message of kind {:?} (plant: {}, sensor: {:?})", | ||||
|                                     msg.0, msg.1, msg.2 | ||||
|                                 ); | ||||
|                                 if msg.0 == MessageKind::MoistureData { | ||||
|                                     let plant = msg.1 as usize; | ||||
|                                     let sensor = msg.2; | ||||
|                                     match sensor { | ||||
|                                         SensorSlot::A => { | ||||
|                                             result.plant[plant].sensor_a = true; | ||||
|                                         } | ||||
|                                         SensorSlot::B => { | ||||
|                                             result.plant[plant].sensor_b = true; | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         Id::Extended(ext) => { | ||||
|                             warn!("Received extended ID: {:?}", ext); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                     Id::Extended(ext) => { | ||||
|                         warn!("Received extended ID: {ext:?}"); | ||||
|                     } | ||||
|                 }, | ||||
|                 Err(err) => { | ||||
|                     error!("Error receiving CAN message: {:?}", err); | ||||
|                     error!("Error receiving CAN message: {err:?}"); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub async fn inner_pulse(plant: usize, sensor: Sensor, signal_counter: &mut Unit<'_, 0>, sensor_expander: &mut Pca9535Immediate<I2cDevice<'static, CriticalSectionRawMutex, I2c<'static, Blocking>>>) -> FatResult<f32> { | ||||
|     pub async fn inner_pulse( | ||||
|         plant: usize, | ||||
|         sensor: Sensor, | ||||
|         signal_counter: &mut Unit<'_, 0>, | ||||
|         sensor_expander: &mut Pca9535Immediate< | ||||
|             I2cDevice<'static, CriticalSectionRawMutex, I2c<'static, Blocking>>, | ||||
|         >, | ||||
|     ) -> FatResult<f32> { | ||||
|         let mut results = [0_f32; REPEAT_MOIST_MEASURE]; | ||||
|         for sample in results.iter_mut() { | ||||
|             signal_counter.pause(); | ||||
|             signal_counter.clear(); | ||||
|  | ||||
|     let mut results = [0_f32; REPEAT_MOIST_MEASURE]; | ||||
|     for repeat in 0..REPEAT_MOIST_MEASURE { | ||||
|     signal_counter.pause(); | ||||
|     signal_counter.clear(); | ||||
|             //Disable all | ||||
|             sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?; | ||||
|  | ||||
|     //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 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)?; | ||||
|             } | ||||
|  | ||||
|     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 measurement = 100; // TODO what is this scaling factor? what is its purpose? | ||||
|             let factor = 1000f32 / measurement as f32; | ||||
|  | ||||
|             //give some time to stabilize | ||||
|             Timer::after_millis(10).await; | ||||
|             signal_counter.resume(); | ||||
|             Timer::after_millis(measurement).await; | ||||
|             signal_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)?; | ||||
|             Timer::after_millis(10).await; | ||||
|             let unscaled = 1337; //signal_counter.get_counter_value()? as i32; | ||||
|             let hz = unscaled as f32 * factor; | ||||
|             LOG_ACCESS | ||||
|                 .lock() | ||||
|                 .await | ||||
|                 .log( | ||||
|                     LogMessage::RawMeasure, | ||||
|                     unscaled as u32, | ||||
|                     hz as u32, | ||||
|                     &plant.to_string(), | ||||
|                     &format!("{sensor:?}"), | ||||
|                 ) | ||||
|                 .await; | ||||
|             *sample = 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) | ||||
|     } | ||||
|  | ||||
|     sensor_expander.pin_set_low(GPIOBank::Bank0, MS4)?; | ||||
|     sensor_expander.pin_set_high(GPIOBank::Bank0, SENSOR_ON)?; | ||||
|  | ||||
|     let measurement = 100; // TODO what is this scaling factor? what is its purpose? | ||||
|     let factor = 1000f32 / measurement as f32; | ||||
|  | ||||
|     //give some time to stabilize | ||||
|     Timer::after_millis(10).await; | ||||
|     signal_counter.resume(); | ||||
|     Timer::after_millis(measurement).await; | ||||
|     signal_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)?; | ||||
|     Timer::after_millis(10).await; | ||||
|     let unscaled = 1337; //signal_counter.get_counter_value()? as i32; | ||||
|     let hz = unscaled as f32 * factor; | ||||
|     LOG_ACCESS | ||||
|     .lock() | ||||
|     .await | ||||
|     .log( | ||||
|     LogMessage::RawMeasure, | ||||
|     unscaled as u32, | ||||
|     hz as u32, | ||||
|     &plant.to_string(), | ||||
|     &format!("{sensor:?}"), | ||||
|     ) | ||||
|     .await; | ||||
|     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) | ||||
| } | ||||
|  | ||||
|     async fn inner_can( | ||||
|         twai: &mut Twai<'static, Blocking>, | ||||
|     ) -> FatResult<Moistures> { | ||||
|         [0_u8; 8]; | ||||
|     async fn inner_can(twai: &mut Twai<'static, Blocking>) -> FatResult<Moistures> { | ||||
|         config::standard(); | ||||
|  | ||||
|         let timeout = Instant::now() | ||||
| @@ -291,7 +313,7 @@ impl SensorImpl { | ||||
|             let answer = twai.receive(); | ||||
|             match answer { | ||||
|                 Ok(answer) => { | ||||
|                     info!("Received CAN message: {:?}", answer); | ||||
|                     info!("Received CAN message: {answer:?}"); | ||||
|                 } | ||||
|                 Err(error) => match error { | ||||
|                     nb::Error::Other(error) => { | ||||
|   | ||||
| @@ -137,11 +137,11 @@ impl<'a> TankSensor<'a> { | ||||
|         Timer::after_millis(100).await; | ||||
|  | ||||
|         let mut store = [0_u16; TANK_MULTI_SAMPLE]; | ||||
|         for multisample in 0..TANK_MULTI_SAMPLE { | ||||
|         for sample in store.iter_mut() { | ||||
|             let value = self.tank_channel.read_oneshot(&mut self.tank_pin); | ||||
|             //force yield | ||||
|             Timer::after_millis(10).await; | ||||
|             store[multisample] = value.unwrap(); | ||||
|             *sample = value.unwrap(); | ||||
|         } | ||||
|         self.tank_power.set_low(); | ||||
|  | ||||
|   | ||||
| @@ -26,8 +26,11 @@ static mut LOG_ARRAY: LogArray = LogArray { | ||||
|     }; LOG_ARRAY_SIZE as usize], | ||||
|     head: 0, | ||||
| }; | ||||
|  | ||||
| // this is the only reference that is created for LOG_ARRAY and the only way to access it | ||||
| #[allow(static_mut_refs)] | ||||
| pub static LOG_ACCESS: Mutex<CriticalSectionRawMutex, &'static mut LogArray> = | ||||
|     unsafe { Mutex::new(&mut *&raw mut LOG_ARRAY) }; | ||||
|     unsafe { Mutex::new(&mut LOG_ARRAY) }; | ||||
|  | ||||
| const TXT_SHORT_LENGTH: usize = 8; | ||||
| const TXT_LONG_LENGTH: usize = 32; | ||||
| @@ -138,7 +141,7 @@ impl LogArray { | ||||
|         template_string = template_string.replace("${txt_long}", txt_long); | ||||
|         template_string = template_string.replace("${txt_short}", txt_short); | ||||
|  | ||||
|         info!("{}", template_string); | ||||
|         info!("{template_string}"); | ||||
|  | ||||
|         let to_modify = &mut self.buffer[head.get() as usize]; | ||||
|         to_modify.timestamp = time; | ||||
| @@ -147,10 +150,10 @@ impl LogArray { | ||||
|         to_modify.b = number_b; | ||||
|         to_modify | ||||
|             .txt_short | ||||
|             .clone_from_slice(&txt_short_stack.as_bytes()); | ||||
|             .clone_from_slice(txt_short_stack.as_bytes()); | ||||
|         to_modify | ||||
|             .txt_long | ||||
|             .clone_from_slice(&txt_long_stack.as_bytes()); | ||||
|             .clone_from_slice(txt_long_stack.as_bytes()); | ||||
|         head = head.wrapping_add(1); | ||||
|         self.head = head.get(); | ||||
|     } | ||||
|   | ||||
| @@ -16,13 +16,13 @@ use esp_backtrace as _; | ||||
| use crate::config::{NetworkConfig, PlantConfig}; | ||||
| use crate::fat_error::FatResult; | ||||
| use crate::hal::esp::MQTT_STAY_ALIVE; | ||||
| use crate::hal::{esp_time, TIME_ACCESS}; | ||||
| use crate::hal::PROGRESS_ACTIVE; | ||||
| use crate::hal::{esp_time, TIME_ACCESS}; | ||||
| use crate::log::{log, LOG_ACCESS}; | ||||
| use crate::tank::{determine_tank_state, TankError, TankState, WATER_FROZEN_THRESH}; | ||||
| use crate::webserver::http_server; | ||||
| use crate::{ | ||||
|     config::BoardVersion::INITIAL, | ||||
|     config::BoardVersion::Initial, | ||||
|     hal::{PlantHal, HAL, PLANT_COUNT}, | ||||
| }; | ||||
| use ::log::{info, warn}; | ||||
| @@ -136,18 +136,18 @@ pub struct PumpResult { | ||||
|  | ||||
| #[derive(Serialize, Debug, PartialEq)] | ||||
| enum SntpMode { | ||||
|     OFFLINE, | ||||
|     SYNC { current: DateTime<Utc> }, | ||||
|     Offline, | ||||
|     Sync { current: DateTime<Utc> }, | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Debug, PartialEq)] | ||||
| enum NetworkMode { | ||||
|     WIFI { | ||||
|     Wifi { | ||||
|         sntp: SntpMode, | ||||
|         mqtt: bool, | ||||
|         ip_address: String, | ||||
|     }, | ||||
|     OFFLINE, | ||||
|     Offline, | ||||
| } | ||||
|  | ||||
| async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
| @@ -172,7 +172,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|             value | ||||
|         } | ||||
|         Err(err) => { | ||||
|             info!("rtc module error: {:?}", err); | ||||
|             info!("rtc module error: {err:?}"); | ||||
|             board.board_hal.general_fault(true).await; | ||||
|             esp_time().await | ||||
|         } | ||||
| @@ -187,7 +187,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|             .log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "") | ||||
|             .await; | ||||
|     } | ||||
|     info!("cur is {}", cur); | ||||
|     info!("cur is {cur}"); | ||||
|     update_charge_indicator(&mut board).await; | ||||
|     if board.board_hal.get_esp().get_restart_to_conf() { | ||||
|         LOG_ACCESS | ||||
| @@ -228,7 +228,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         info!("no mode override"); | ||||
|     } | ||||
|  | ||||
|     if board.board_hal.get_config().hardware.board == INITIAL | ||||
|     if board.board_hal.get_config().hardware.board == Initial | ||||
|         && board.board_hal.get_config().network.ssid.is_none() | ||||
|     { | ||||
|         info!("No wifi configured, starting initial config mode"); | ||||
| @@ -249,10 +249,10 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         info!("No wifi configured"); | ||||
|         //the current sensors require this amount to stabilize, in the case of Wi-Fi this is already handled due to connect timings; | ||||
|         Timer::after_millis(100).await; | ||||
|         NetworkMode::OFFLINE | ||||
|         NetworkMode::Offline | ||||
|     }; | ||||
|  | ||||
|     if matches!(network_mode, NetworkMode::OFFLINE) && to_config { | ||||
|     if matches!(network_mode, NetworkMode::Offline) && to_config { | ||||
|         info!("Could not connect to station and config mode forced, switching to ap mode!"); | ||||
|  | ||||
|         let res = { | ||||
| @@ -264,14 +264,14 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|                 stack.replace(ap_stack); | ||||
|                 info!("Started ap, continuing") | ||||
|             } | ||||
|             Err(err) => info!("Could not start config override ap mode due to {}", err), | ||||
|             Err(err) => info!("Could not start config override ap mode due to {err}"), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let tz = &board.board_hal.get_config().timezone; | ||||
|     let timezone = match tz { | ||||
|         Some(tz_str) => tz_str.parse::<Tz>().unwrap_or_else(|_| { | ||||
|             info!("Invalid timezone '{}', falling back to UTC", tz_str); | ||||
|             info!("Invalid timezone '{tz_str}', falling back to UTC"); | ||||
|             UTC | ||||
|         }), | ||||
|         None => UTC, // Fallback to UTC if no timezone is set | ||||
| @@ -286,7 +286,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         timezone_time | ||||
|     ); | ||||
|  | ||||
|     if let NetworkMode::WIFI { ref ip_address, .. } = network_mode { | ||||
|     if let NetworkMode::Wifi { ref ip_address, .. } = network_mode { | ||||
|         publish_firmware_info(&mut board, version, ip_address, &timezone_time.to_rfc3339()).await; | ||||
|         publish_battery_state(&mut board).await; | ||||
|         let _ = publish_mppt_state(&mut board).await; | ||||
| @@ -297,15 +297,15 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         .await | ||||
|         .log( | ||||
|             LogMessage::StartupInfo, | ||||
|             matches!(network_mode, NetworkMode::WIFI { .. }) as u32, | ||||
|             matches!(network_mode, NetworkMode::Wifi { .. }) as u32, | ||||
|             matches!( | ||||
|                 network_mode, | ||||
|                 NetworkMode::WIFI { | ||||
|                     sntp: SntpMode::SYNC { .. }, | ||||
|                 NetworkMode::Wifi { | ||||
|                     sntp: SntpMode::Sync { .. }, | ||||
|                     .. | ||||
|                 } | ||||
|             ) as u32, | ||||
|             matches!(network_mode, NetworkMode::WIFI { mqtt: true, .. }) | ||||
|             matches!(network_mode, NetworkMode::Wifi { mqtt: true, .. }) | ||||
|                 .to_string() | ||||
|                 .as_str(), | ||||
|             "", | ||||
| @@ -356,7 +356,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|                             LogMessage::TankSensorValueRangeError, | ||||
|                             min as u32, | ||||
|                             max as u32, | ||||
|                             &format!("{}", value), | ||||
|                             &format!("{value}"), | ||||
|                             "", | ||||
|                         ) | ||||
|                         .await | ||||
| @@ -402,14 +402,14 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|     let moisture = board.board_hal.measure_moisture_hz().await?; | ||||
|  | ||||
|     let plantstate: [PlantState; PLANT_COUNT] = [ | ||||
|         PlantState::read_hardware_state(moisture,0, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture,1, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture,2, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture,3, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture,4, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture,5, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture,6, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture,7, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture, 0, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture, 1, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture, 2, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture, 3, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture, 4, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture, 5, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture, 6, &mut board).await, | ||||
|         PlantState::read_hardware_state(moisture, 7, &mut board).await, | ||||
|     ]; | ||||
|  | ||||
|     publish_plant_states(&mut board, &timezone_time.clone(), &plantstate).await; | ||||
| @@ -507,7 +507,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         .await | ||||
|         .unwrap_or(BatteryState::Unknown); | ||||
|  | ||||
|     info!("Battery state is {:?}", battery_state); | ||||
|     info!("Battery state is {battery_state:?}"); | ||||
|     let mut light_state = LightState { | ||||
|         enabled: board.board_hal.get_config().night_lamp.enabled, | ||||
|         ..Default::default() | ||||
| @@ -573,7 +573,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|             board.board_hal.light(false).await?; | ||||
|         } | ||||
|  | ||||
|         info!("Lightstate is {:?}", light_state); | ||||
|         info!("Lightstate is {light_state:?}"); | ||||
|     } | ||||
|  | ||||
|     match &serde_json::to_string(&light_state) { | ||||
| @@ -585,7 +585,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|                 .await; | ||||
|         } | ||||
|         Err(err) => { | ||||
|             info!("Error publishing lightstate {}", err); | ||||
|             info!("Error publishing lightstate {err}"); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| @@ -616,7 +616,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         .mqtt_publish("/state", "sleep") | ||||
|         .await; | ||||
|  | ||||
|     info!("Go to sleep for {} minutes", deep_sleep_duration_minutes); | ||||
|     info!("Go to sleep for {deep_sleep_duration_minutes} minutes"); | ||||
|     //determine next event | ||||
|     //is light out of work trigger soon? | ||||
|     //is battery low ?? | ||||
| @@ -625,7 +625,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|     //mark_app_valid(); | ||||
|  | ||||
|     let stay_alive = MQTT_STAY_ALIVE.load(Ordering::Relaxed); | ||||
|     info!("Check stay alive, current state is {}", stay_alive); | ||||
|     info!("Check stay alive, current state is {stay_alive}"); | ||||
|  | ||||
|     if stay_alive { | ||||
|         let reboot_now = Arc::new(AtomicBool::new(false)); | ||||
| @@ -679,49 +679,45 @@ pub async fn do_secure_pump( | ||||
|                     let current_ma = current.as_milliamperes() as u16; | ||||
|                     current_collector[step] = current_ma; | ||||
|                     let high_current = current_ma > plant_config.max_pump_current_ma; | ||||
|                     if high_current { | ||||
|                         if first_error { | ||||
|                             log( | ||||
|                                 LogMessage::PumpOverCurrent, | ||||
|                                 plant_id as u32 + 1, | ||||
|                                 current_ma as u32, | ||||
|                                 plant_config.max_pump_current_ma.to_string().as_str(), | ||||
|                                 step.to_string().as_str(), | ||||
|                             ) | ||||
|                             .await; | ||||
|                             board.board_hal.general_fault(true).await; | ||||
|                             board.board_hal.fault(plant_id, true).await?; | ||||
|                             if !plant_config.ignore_current_error { | ||||
|                                 error = true; | ||||
|                                 break; | ||||
|                             } | ||||
|                             first_error = false; | ||||
|                     if high_current && first_error { | ||||
|                         log( | ||||
|                             LogMessage::PumpOverCurrent, | ||||
|                             plant_id as u32 + 1, | ||||
|                             current_ma as u32, | ||||
|                             plant_config.max_pump_current_ma.to_string().as_str(), | ||||
|                             step.to_string().as_str(), | ||||
|                         ) | ||||
|                         .await; | ||||
|                         board.board_hal.general_fault(true).await; | ||||
|                         board.board_hal.fault(plant_id, true).await?; | ||||
|                         if !plant_config.ignore_current_error { | ||||
|                             error = true; | ||||
|                             break; | ||||
|                         } | ||||
|                         first_error = false; | ||||
|                     } | ||||
|                     let low_current = current_ma < plant_config.min_pump_current_ma; | ||||
|                     if low_current { | ||||
|                         if first_error { | ||||
|                             log( | ||||
|                                 LogMessage::PumpOpenLoopCurrent, | ||||
|                                 plant_id as u32 + 1, | ||||
|                                 current_ma as u32, | ||||
|                                 plant_config.min_pump_current_ma.to_string().as_str(), | ||||
|                                 step.to_string().as_str(), | ||||
|                             ) | ||||
|                             .await; | ||||
|                             board.board_hal.general_fault(true).await; | ||||
|                             board.board_hal.fault(plant_id, true).await?; | ||||
|                             if !plant_config.ignore_current_error { | ||||
|                                 error = true; | ||||
|                                 break; | ||||
|                             } | ||||
|                             first_error = false; | ||||
|                     if low_current && first_error { | ||||
|                         log( | ||||
|                             LogMessage::PumpOpenLoopCurrent, | ||||
|                             plant_id as u32 + 1, | ||||
|                             current_ma as u32, | ||||
|                             plant_config.min_pump_current_ma.to_string().as_str(), | ||||
|                             step.to_string().as_str(), | ||||
|                         ) | ||||
|                         .await; | ||||
|                         board.board_hal.general_fault(true).await; | ||||
|                         board.board_hal.fault(plant_id, true).await?; | ||||
|                         if !plant_config.ignore_current_error { | ||||
|                             error = true; | ||||
|                             break; | ||||
|                         } | ||||
|                         first_error = false; | ||||
|                     } | ||||
|                 } | ||||
|                 Err(err) => { | ||||
|                     if !plant_config.ignore_current_error { | ||||
|                         info!("Error getting pump current: {}", err); | ||||
|                         info!("Error getting pump current: {err}"); | ||||
|                         log( | ||||
|                             LogMessage::PumpMissingSensorCurrent, | ||||
|                             plant_id as u32, | ||||
| @@ -744,10 +740,7 @@ pub async fn do_secure_pump( | ||||
|     board.board_hal.get_tank_sensor()?.stop_flow_meter(); | ||||
|     let final_flow_value = board.board_hal.get_tank_sensor()?.get_flow_meter_value(); | ||||
|     let flow_value_ml = final_flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse; | ||||
|     info!( | ||||
|         "Final flow value is {} with {} ml", | ||||
|         final_flow_value, flow_value_ml | ||||
|     ); | ||||
|     info!("Final flow value is {final_flow_value} with {flow_value_ml} ml"); | ||||
|     current_collector.sort(); | ||||
|     Ok(PumpResult { | ||||
|         median_current_ma: current_collector[current_collector.len() / 2], | ||||
| @@ -767,7 +760,8 @@ async fn update_charge_indicator( | ||||
|     if let Ok(current) = board.board_hal.get_mptt_current().await { | ||||
|         let _ = board | ||||
|             .board_hal | ||||
|             .set_charge_indicator(current.as_milliamperes() > 20_f64); | ||||
|             .set_charge_indicator(current.as_milliamperes() > 20_f64) | ||||
|             .await; | ||||
|     } | ||||
|     //fallback to battery controller and ask it instead | ||||
|     else if let Ok(charging) = board | ||||
| @@ -776,10 +770,10 @@ async fn update_charge_indicator( | ||||
|         .average_current_milli_ampere() | ||||
|         .await | ||||
|     { | ||||
|         let _ = board.board_hal.set_charge_indicator(charging > 20); | ||||
|         let _ = board.board_hal.set_charge_indicator(charging > 20).await; | ||||
|     } else { | ||||
|         //who knows | ||||
|         let _ = board.board_hal.set_charge_indicator(false); | ||||
|         let _ = board.board_hal.set_charge_indicator(false).await; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -792,7 +786,11 @@ async fn publish_tank_state( | ||||
|         &tank_state.as_mqtt_info(&board.board_hal.get_config().tank, &water_temp), | ||||
|     ) | ||||
|     .unwrap(); | ||||
|     let _ = board.board_hal.get_esp().mqtt_publish("/water", &*state); | ||||
|     board | ||||
|         .board_hal | ||||
|         .get_esp() | ||||
|         .mqtt_publish("/water", &state) | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| async fn publish_plant_states( | ||||
| @@ -819,16 +817,16 @@ async fn publish_plant_states( | ||||
| async fn publish_firmware_info( | ||||
|     board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>, | ||||
|     version: VersionInfo, | ||||
|     ip_address: &String, | ||||
|     timezone_time: &String, | ||||
|     ip_address: &str, | ||||
|     timezone_time: &str, | ||||
| ) { | ||||
|     let esp = board.board_hal.get_esp(); | ||||
|     let _ = esp.mqtt_publish("/firmware/address", ip_address).await; | ||||
|     let _ = esp | ||||
|         .mqtt_publish("/firmware/state", format!("{:?}", &version).as_str()) | ||||
|     esp.mqtt_publish("/firmware/address", ip_address).await; | ||||
|     esp.mqtt_publish("/firmware/state", format!("{:?}", &version).as_str()) | ||||
|         .await; | ||||
|     let _ = esp.mqtt_publish("/firmware/last_online", timezone_time); | ||||
|     let _ = esp.mqtt_publish("/state", "online").await; | ||||
|     esp.mqtt_publish("/firmware/last_online", timezone_time) | ||||
|         .await; | ||||
|     esp.mqtt_publish("/state", "online").await; | ||||
| } | ||||
| macro_rules! mk_static { | ||||
|     ($t:ty,$val:expr) => {{ | ||||
| @@ -847,21 +845,20 @@ async fn try_connect_wifi_sntp_mqtt( | ||||
|         Ok(stack) => { | ||||
|             stack_store.replace(stack); | ||||
|  | ||||
|             let sntp_mode: SntpMode = match board | ||||
|                 .board_hal | ||||
|                 .get_esp() | ||||
|                 .sntp(1000 * 10, stack.clone()) | ||||
|                 .await | ||||
|             { | ||||
|             let sntp_mode: SntpMode = match board.board_hal.get_esp().sntp(1000 * 10, stack).await { | ||||
|                 Ok(new_time) => { | ||||
|                     info!("Using time from sntp {}", new_time.to_rfc3339()); | ||||
|                     let _ = board.board_hal.get_rtc_module().set_rtc_time(&new_time); | ||||
|                     SntpMode::SYNC { current: new_time } | ||||
|                     let _ = board | ||||
|                         .board_hal | ||||
|                         .get_rtc_module() | ||||
|                         .set_rtc_time(&new_time) | ||||
|                         .await; | ||||
|                     SntpMode::Sync { current: new_time } | ||||
|                 } | ||||
|                 Err(err) => { | ||||
|                     warn!("sntp error: {}", err); | ||||
|                     warn!("sntp error: {err}"); | ||||
|                     board.board_hal.general_fault(true).await; | ||||
|                     SntpMode::OFFLINE | ||||
|                     SntpMode::Offline | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
| @@ -874,23 +871,23 @@ async fn try_connect_wifi_sntp_mqtt( | ||||
|                         true | ||||
|                     } | ||||
|                     Err(err) => { | ||||
|                         warn!("Could not connect mqtt due to {}", err); | ||||
|                         warn!("Could not connect mqtt due to {err}"); | ||||
|                         false | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 false | ||||
|             }; | ||||
|             NetworkMode::WIFI { | ||||
|             NetworkMode::Wifi { | ||||
|                 sntp: sntp_mode, | ||||
|                 mqtt: mqtt_connected, | ||||
|                 ip_address: stack.hardware_address().to_string(), | ||||
|             } | ||||
|         } | ||||
|         Err(err) => { | ||||
|             info!("Offline mode due to {}", err); | ||||
|             info!("Offline mode due to {err}"); | ||||
|             board.board_hal.general_fault(true).await; | ||||
|             NetworkMode::OFFLINE | ||||
|             NetworkMode::Offline | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -926,7 +923,7 @@ async fn pump_info( | ||||
|                 .await; | ||||
|         } | ||||
|         Err(err) => { | ||||
|             warn!("Error publishing pump state {}", err); | ||||
|             warn!("Error publishing pump state {err}"); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| @@ -941,10 +938,11 @@ async fn publish_mppt_state( | ||||
|         voltage_ma: voltage.as_millivolts() as u32, | ||||
|     }; | ||||
|     if let Ok(serialized_solar_state_bytes) = serde_json::to_string(&solar_state) { | ||||
|         let _ = board | ||||
|         board | ||||
|             .board_hal | ||||
|             .get_esp() | ||||
|             .mqtt_publish("/mppt", &serialized_solar_state_bytes); | ||||
|             .mqtt_publish("/mppt", &serialized_solar_state_bytes) | ||||
|             .await; | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
| @@ -968,7 +966,7 @@ async fn publish_battery_state( | ||||
|         let _ = board | ||||
|             .board_hal | ||||
|             .get_esp() | ||||
|             .mqtt_publish("/battery", &*value) | ||||
|             .mqtt_publish("/battery", &value) | ||||
|             .await; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,5 @@ | ||||
| use crate::hal::Moistures; | ||||
| use crate::{ | ||||
|     config::PlantConfig, | ||||
|     hal::HAL, | ||||
|     in_time_range, | ||||
| }; | ||||
| use crate::{config::PlantConfig, hal::HAL, in_time_range}; | ||||
| use chrono::{DateTime, TimeDelta, Utc}; | ||||
| use chrono_tz::Tz; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| @@ -76,7 +72,7 @@ impl PumpState { | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)] | ||||
| pub enum PlantWateringMode { | ||||
|     OFF, | ||||
|     Off, | ||||
|     TargetMoisture, | ||||
|     MinMoisture, | ||||
|     TimerOnly, | ||||
| @@ -115,20 +111,24 @@ fn map_range_moisture( | ||||
| } | ||||
|  | ||||
| impl PlantState { | ||||
|     pub async fn read_hardware_state(moistures: Moistures, plant_id: usize, board: &mut HAL<'_>) -> Self { | ||||
|     pub async fn read_hardware_state( | ||||
|         moistures: Moistures, | ||||
|         plant_id: usize, | ||||
|         board: &mut HAL<'_>, | ||||
|     ) -> Self { | ||||
|         let sensor_a = if board.board_hal.get_config().plants[plant_id].sensor_a { | ||||
|             let raw = moistures.sensor_a_hz[plant_id]; | ||||
|             match map_range_moisture( | ||||
|                 raw, | ||||
|                     board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency, | ||||
|                     board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency, | ||||
|                 ) { | ||||
|                     Ok(moisture_percent) => MoistureSensorState::MoistureValue { | ||||
|                         raw_hz: raw, | ||||
|                         moisture_percent, | ||||
|                     }, | ||||
|                     Err(err) => MoistureSensorState::SensorError(err), | ||||
|                 } | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency, | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency, | ||||
|             ) { | ||||
|                 Ok(moisture_percent) => MoistureSensorState::MoistureValue { | ||||
|                     raw_hz: raw, | ||||
|                     moisture_percent, | ||||
|                 }, | ||||
|                 Err(err) => MoistureSensorState::SensorError(err), | ||||
|             } | ||||
|         } else { | ||||
|             MoistureSensorState::Disabled | ||||
|         }; | ||||
| @@ -161,13 +161,13 @@ impl PlantState { | ||||
|             }, | ||||
|         }; | ||||
|         if state.is_err() { | ||||
|             let _ = board.board_hal.fault(plant_id, true); | ||||
|             let _ = board.board_hal.fault(plant_id, true).await; | ||||
|         } | ||||
|         state | ||||
|     } | ||||
|  | ||||
|     pub fn pump_in_timeout(&self, plant_conf: &PlantConfig, current_time: &DateTime<Tz>) -> bool { | ||||
|         if matches!(plant_conf.mode, PlantWateringMode::OFF) { | ||||
|         if matches!(plant_conf.mode, PlantWateringMode::Off) { | ||||
|             return false; | ||||
|         } | ||||
|         self.pump.previous_pump.is_some_and(|last_pump| { | ||||
| @@ -208,7 +208,7 @@ impl PlantState { | ||||
|         current_time: &DateTime<Tz>, | ||||
|     ) -> bool { | ||||
|         match plant_conf.mode { | ||||
|             PlantWateringMode::OFF => false, | ||||
|             PlantWateringMode::Off => false, | ||||
|             PlantWateringMode::TargetMoisture => { | ||||
|                 let (moisture_percent, _) = self.plant_moisture(); | ||||
|                 if let Some(moisture_percent) = moisture_percent { | ||||
| @@ -229,28 +229,8 @@ impl PlantState { | ||||
|                 } | ||||
|             } | ||||
|             PlantWateringMode::MinMoisture => { | ||||
|                 let (moisture_percent, _) = self.plant_moisture(); | ||||
|                 if let Some(_moisture_percent) = moisture_percent { | ||||
|                     if self.pump_in_timeout(plant_conf, current_time) { | ||||
|                         false | ||||
|                     } else if !in_time_range( | ||||
|                         current_time, | ||||
|                         plant_conf.pump_hour_start, | ||||
|                         plant_conf.pump_hour_end, | ||||
|                     ) { | ||||
|                         false | ||||
|                     } else if true { | ||||
|                         //if not cooldown min and below max | ||||
|                         true | ||||
|                     } else if true { | ||||
|                         //if below min disable cooldown min | ||||
|                         true | ||||
|                     } else { | ||||
|                         false | ||||
|                     } | ||||
|                 } else { | ||||
|                     false | ||||
|                 } | ||||
|                 // TODO | ||||
|                 false | ||||
|             } | ||||
|             PlantWateringMode::TimerOnly => !self.pump_in_timeout(plant_conf, current_time), | ||||
|         } | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| use crate::alloc::string::{String, ToString}; | ||||
| use crate::config::TankConfig; | ||||
| use crate::hal::HAL; | ||||
| use crate::fat_error::FatResult; | ||||
| use crate::hal::HAL; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::mutex::MutexGuard; | ||||
| use serde::Serialize; | ||||
| @@ -161,7 +161,7 @@ pub async fn determine_tank_state( | ||||
|         match board | ||||
|             .board_hal | ||||
|             .get_tank_sensor() | ||||
|             .and_then(|f| core::prelude::v1::Ok(f.tank_sensor_voltage())) | ||||
|             .map(|f| f.tank_sensor_voltage()) | ||||
|         { | ||||
|             Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv.await.unwrap()), | ||||
|             Err(err) => TankState::Error(TankError::BoardError(err.to_string())), | ||||
|   | ||||
| @@ -51,11 +51,8 @@ where | ||||
|                 conn.initiate_response( | ||||
|                     409, | ||||
|                     Some( | ||||
|                         format!( | ||||
|                             "Checksum mismatch expected {} got {}", | ||||
|                             expected_crc, actual_crc | ||||
|                         ) | ||||
|                         .as_str(), | ||||
|                         format!("Checksum mismatch expected {expected_crc} got {actual_crc}") | ||||
|                             .as_str(), | ||||
|                     ), | ||||
|                     &[], | ||||
|                 ) | ||||
| @@ -131,7 +128,7 @@ where | ||||
|             let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|             board.board_hal.progress(counter).await; | ||||
|  | ||||
|             counter = counter + 1; | ||||
|             counter += 1; | ||||
|             board | ||||
|                 .board_hal | ||||
|                 .get_rtc_module() | ||||
| @@ -139,7 +136,7 @@ where | ||||
|                 .await?; | ||||
|             checksum.update(&buf[0..to_write]); | ||||
|         } | ||||
|         offset = offset + to_write; | ||||
|         offset += to_write; | ||||
|     } | ||||
|  | ||||
|     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|   | ||||
| @@ -27,7 +27,7 @@ where | ||||
|     T: Read + Write, | ||||
| { | ||||
|     let filename = &path[prefix.len()..]; | ||||
|     info!("file request for {} with method {}", filename, method); | ||||
|     info!("file request for {filename} with method {method}"); | ||||
|     Ok(match method { | ||||
|         Method::Delete => { | ||||
|             let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
| @@ -65,7 +65,7 @@ where | ||||
|                 &[ | ||||
|                     ("Content-Type", "application/octet-stream"), | ||||
|                     ("Content-Disposition", disposition.as_str()), | ||||
|                     ("Content-Length", &format!("{}", size)), | ||||
|                     ("Content-Length", &format!("{size}")), | ||||
|                     ("Access-Control-Allow-Origin", "*"), | ||||
|                     ("Access-Control-Allow-Headers", "*"), | ||||
|                     ("Access-Control-Allow-Methods", "*"), | ||||
| @@ -84,16 +84,16 @@ where | ||||
|                     .await?; | ||||
|                 let length = read_chunk.1; | ||||
|                 if length == 0 { | ||||
|                     info!("file request for {} finished", filename); | ||||
|                     info!("file request for {filename} finished"); | ||||
|                     break; | ||||
|                 } | ||||
|                 let data = &read_chunk.0[0..length]; | ||||
|                 conn.write_all(data).await?; | ||||
|                 if length < read_chunk.0.len() { | ||||
|                     info!("file request for {} finished", filename); | ||||
|                     info!("file request for {filename} finished"); | ||||
|                     break; | ||||
|                 } | ||||
|                 chunk = chunk + 1; | ||||
|                 chunk += 1; | ||||
|             } | ||||
|             BOARD_ACCESS | ||||
|                 .get() | ||||
| @@ -120,8 +120,8 @@ where | ||||
|             let mut chunk = 0; | ||||
|             loop { | ||||
|                 let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?; | ||||
|                 if buf.len() == 0 { | ||||
|                     info!("file request for {} finished", filename); | ||||
|                 if buf.is_empty() { | ||||
|                     info!("file request for {filename} finished"); | ||||
|                     break; | ||||
|                 } else { | ||||
|                     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
| @@ -132,8 +132,8 @@ where | ||||
|                         .write_file(filename.to_owned(), offset as u32, &buf) | ||||
|                         .await?; | ||||
|                 } | ||||
|                 offset = offset + buf.len(); | ||||
|                 chunk = chunk + 1; | ||||
|                 offset += buf.len(); | ||||
|                 chunk += 1; | ||||
|             } | ||||
|             BOARD_ACCESS | ||||
|                 .get() | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| use core::str::FromStr; | ||||
| use crate::fat_error::{FatError, FatResult}; | ||||
| use crate::hal::{esp_time, PLANT_COUNT}; | ||||
| use crate::log::LogMessage; | ||||
| @@ -9,6 +8,7 @@ use alloc::format; | ||||
| use alloc::string::{String, ToString}; | ||||
| use alloc::vec::Vec; | ||||
| use chrono_tz::Tz; | ||||
| use core::str::FromStr; | ||||
| use edge_http::io::server::Connection; | ||||
| use embedded_io_async::{Read, Write}; | ||||
| use log::info; | ||||
| @@ -142,21 +142,15 @@ pub(crate) async fn get_time<T, const N: usize>( | ||||
|     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|     let conf = board.board_hal.get_config(); | ||||
|  | ||||
|     let tz:Tz = match conf.timezone.as_ref(){ | ||||
|         None => { | ||||
|             Tz::UTC | ||||
|         } | ||||
|         Some(tz_string) => { | ||||
|             match Tz::from_str(tz_string) { | ||||
|                 Ok(tz) => { | ||||
|                     tz | ||||
|                 } | ||||
|                 Err(err) => { | ||||
|                     info!("failed parsing timezone {}", err); | ||||
|                     Tz::UTC | ||||
|                 } | ||||
|     let tz: Tz = match conf.timezone.as_ref() { | ||||
|         None => Tz::UTC, | ||||
|         Some(tz_string) => match Tz::from_str(tz_string) { | ||||
|             Ok(tz) => tz, | ||||
|             Err(err) => { | ||||
|                 info!("failed parsing timezone {err}"); | ||||
|                 Tz::UTC | ||||
|             } | ||||
|         } | ||||
|         }, | ||||
|     }; | ||||
|  | ||||
|     let native = esp_time().await.with_timezone(&tz).to_rfc3339(); | ||||
| @@ -164,7 +158,7 @@ pub(crate) async fn get_time<T, const N: usize>( | ||||
|     let rtc = match board.board_hal.get_rtc_module().get_rtc_time().await { | ||||
|         Ok(time) => time.with_timezone(&tz).to_rfc3339(), | ||||
|         Err(err) => { | ||||
|             format!("Error getting time: {}", err) | ||||
|             format!("Error getting time: {err}") | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,7 @@ use crate::webserver::get_log::get_log; | ||||
| use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index}; | ||||
| use crate::webserver::ota::ota_operations; | ||||
| use crate::webserver::post_json::{ | ||||
|     board_test, night_lamp_test, pump_test, set_config, wifi_scan, write_time, detect_sensors, | ||||
|     board_test, detect_sensors, night_lamp_test, pump_test, set_config, wifi_scan, write_time, | ||||
| }; | ||||
| use crate::{bail, BOARD_ACCESS}; | ||||
| use alloc::borrow::ToOwned; | ||||
| @@ -64,7 +64,7 @@ impl Handler for HTTPRequestRouter { | ||||
|             file_operations(conn, method, &path, &prefix).await? | ||||
|         } else if path == "/ota" { | ||||
|             ota_operations(conn, method).await.map_err(|e| { | ||||
|                 error!("Error handling ota: {}", e); | ||||
|                 error!("Error handling ota: {e}"); | ||||
|                 e | ||||
|             })? | ||||
|         } else { | ||||
| @@ -238,7 +238,7 @@ where | ||||
|         }, | ||||
|         Err(err) => { | ||||
|             let error_text = err.to_string(); | ||||
|             info!("error handling process {}", error_text); | ||||
|             info!("error handling process {error_text}"); | ||||
|             conn.initiate_response( | ||||
|                 500, | ||||
|                 Some("OK"), | ||||
|   | ||||
| @@ -32,7 +32,7 @@ where | ||||
|             let mut chunk = 0; | ||||
|             loop { | ||||
|                 let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?; | ||||
|                 if buf.len() == 0 { | ||||
|                 if buf.is_empty() { | ||||
|                     info!("file request for  ota finished"); | ||||
|                     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|                     board.board_hal.get_esp().finalize_ota().await?; | ||||
| @@ -45,11 +45,11 @@ where | ||||
|                     board | ||||
|                         .board_hal | ||||
|                         .get_esp() | ||||
|                         .write_ota(offset as u32, &*buf) | ||||
|                         .write_ota(offset as u32, &buf) | ||||
|                         .await?; | ||||
|                 } | ||||
|                 offset = offset + buf.len(); | ||||
|                 chunk = chunk + 1; | ||||
|                 offset += buf.len(); | ||||
|                 chunk += 1; | ||||
|             } | ||||
|             BOARD_ACCESS | ||||
|                 .get() | ||||
|   | ||||
| @@ -113,7 +113,7 @@ where | ||||
|  | ||||
|     let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|     board.board_hal.get_esp().save_config(all).await?; | ||||
|     info!("Wrote config config {:?} with size {}", config, length); | ||||
|     info!("Wrote config config {config:?} with size {length}"); | ||||
|     board.board_hal.set_config(config); | ||||
|     Ok(Some("Ok".to_string())) | ||||
| } | ||||
|   | ||||
| @@ -84,7 +84,7 @@ export enum BatteryBoardVersion { | ||||
| } | ||||
|  | ||||
| export enum BoardVersion { | ||||
|     INITIAL = "INITIAL", | ||||
|     Initial = "Initial", | ||||
|     V3 = "V3", | ||||
|     V4 = "V4" | ||||
| } | ||||
| @@ -200,4 +200,4 @@ export interface TankInfo { | ||||
|     /// water temperature | ||||
|     water_temp: number | null, | ||||
|     temp_sensor_error: string | null | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ export class HardwareConfigView { | ||||
|  | ||||
|       Object.keys(BoardVersion).forEach(version => { | ||||
|         let option = document.createElement("option"); | ||||
|         if (version == BoardVersion.INITIAL.toString()){ | ||||
|         if (version == BoardVersion.Initial.toString()){ | ||||
|           option.selected = true | ||||
|         } | ||||
|         option.innerText = version.toString(); | ||||
| @@ -42,4 +42,4 @@ export class HardwareConfigView { | ||||
|         battery :  BatteryBoardVersion[this.hardware_battery_value.value as keyof typeof BatteryBoardVersion], | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   } | ||||
|   | ||||
| @@ -44,7 +44,6 @@ pub mod id { | ||||
|     pub const MOISTURE_DATA_OFFSET: u16 = 0; // periodic data from sensor (sensor -> controller) | ||||
|     pub const IDENTIFY_CMD_OFFSET: u16 = 32; // identify LED command (controller -> sensor) | ||||
|  | ||||
|  | ||||
|     #[inline] | ||||
|     pub const fn plant_id(message_type_offset: u16, sensor: SensorSlot, plant: u16) -> u16 { | ||||
|         match sensor { | ||||
| @@ -56,8 +55,8 @@ pub mod id { | ||||
|     /// Kinds of message spaces recognized by the addressing scheme. | ||||
|     #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
|     pub enum MessageKind { | ||||
|         MoistureData,  // sensor -> controller | ||||
|         IdentifyCmd,   // controller -> sensor | ||||
|         MoistureData, // sensor -> controller | ||||
|         IdentifyCmd,  // controller -> sensor | ||||
|     } | ||||
|  | ||||
|     /// Try to classify a received 11-bit standard ID into a known message kind and extract plant and sensor slot. | ||||
| @@ -72,11 +71,15 @@ pub mod id { | ||||
|  | ||||
|         // Helper: decode within a given group offset | ||||
|         const fn decode_in_group(rel: u16, group_base: u16) -> Option<(u8, SensorSlot)> { | ||||
|             if rel < group_base { return None; } | ||||
|             if rel < group_base { | ||||
|                 return None; | ||||
|             } | ||||
|             let inner = rel - group_base; | ||||
|             if inner < PLANTS_PER_GROUP { // A slot | ||||
|             if inner < PLANTS_PER_GROUP { | ||||
|                 // A slot | ||||
|                 Some((inner as u8, SensorSlot::A)) | ||||
|             } else if inner >= B_OFFSET && inner < B_OFFSET + PLANTS_PER_GROUP { // B slot | ||||
|             } else if inner >= B_OFFSET && inner < B_OFFSET + PLANTS_PER_GROUP { | ||||
|                 // B slot | ||||
|                 Some(((inner - B_OFFSET) as u8, SensorSlot::B)) | ||||
|             } else { | ||||
|                 None | ||||
| @@ -118,9 +121,9 @@ pub mod id { | ||||
| /// Fits into 5 bytes with bincode-v2 (no varint): u8 + u8 + u16 = 4, alignment may keep 4. | ||||
| #[derive(Debug, Clone, Copy, Encode, Decode)] | ||||
| pub struct MoistureData { | ||||
|     pub plant: u8,         // 0..MAX_PLANTS-1 | ||||
|     pub plant: u8,          // 0..MAX_PLANTS-1 | ||||
|     pub sensor: SensorSlot, // A/B | ||||
|     pub hz: u16,           // measured frequency of moisture sensor | ||||
|     pub hz: u16,            // measured frequency of moisture sensor | ||||
| } | ||||
|  | ||||
| /// Request a sensor to report immediately (controller -> sensor). | ||||
|   | ||||
		Reference in New Issue
	
	Block a user