diff --git a/rust/.cargo/config.toml b/rust/.cargo/config.toml index 0fbaf50..94709f7 100644 --- a/rust/.cargo/config.toml +++ b/rust/.cargo/config.toml @@ -24,6 +24,8 @@ CHRONO_TZ_TIMEZONE_FILTER = "UTC|America/New_York|America/Chicago|America/Los_An CARGO_WORKSPACE_DIR = { value = "", relative = true } ESP_LOG = "info" + + [unstable] build-std = ["alloc", "core"] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index edbd5ed..6879782 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -72,7 +72,8 @@ esp-println = { version = "0.15.0", features = ["esp32c6", "log-04"] } # for more networking protocol support see https://crates.io/crates/edge-net embassy-executor = { version = "0.7.0", features = [ "log", - "task-arena-size-98304" + "task-arena-size-64", + "nightly" ] } embassy-time = { version = "0.5.0", features = ["log"], default-features = false } esp-hal-embassy = { version = "0.9.0", features = ["esp32c6", "log-04"] } diff --git a/rust/src/FatError.rs b/rust/src/FatError.rs index 7e66649..7a0a03e 100644 --- a/rust/src/FatError.rs +++ b/rust/src/FatError.rs @@ -10,6 +10,7 @@ use esp_hal::i2c::master::ConfigError; use esp_wifi::wifi::WifiError; use littlefs2_core::PathError; use onewire::Error; +use pca9535::ExpanderError; //All error superconstruct #[derive(Debug)] @@ -54,6 +55,9 @@ pub enum FatError { Eeprom24x { error: String, }, + ExpanderError { + error: String, + }, } pub type FatResult = Result; @@ -81,6 +85,7 @@ impl fmt::Display for FatError { 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), } } } @@ -178,7 +183,7 @@ impl From for FatError { impl From> for FatError { fn from(value: edge_http::io::Error) -> Self { FatError::String { - error: format!("HttpIoError {:?}", value), + error: format!("{:?}", value), } } } @@ -186,7 +191,7 @@ impl From> for FatError { impl From> for FatError { fn from(value: ds323x::Error) -> Self { FatError::DS323 { - error: format!("Ds323xError {:?}", value), + error: format!("{:?}", value), } } } @@ -194,7 +199,15 @@ impl From> for FatError { impl From> for FatError { fn from(value: eeprom24x::Error) -> Self { FatError::Eeprom24x { - error: format!("Eeprom24xError {:?}", value), + error: format!("{:?}", value), + } + } +} + +impl From>> for FatError { + fn from(value: ExpanderError>) -> Self { + FatError::ExpanderError { + error: format!("{:?}", value), } } } @@ -202,7 +215,7 @@ impl From> for FatError { impl From for FatError { fn from(value: bincode::error::DecodeError) -> Self { FatError::Eeprom24x { - error: format!("Eeprom24xError {:?}", value), + error: format!("{:?}", value), } } } @@ -210,7 +223,7 @@ impl From for FatError { impl From for FatError { fn from(value: bincode::error::EncodeError) -> Self { FatError::Eeprom24x { - error: format!("Eeprom24xError {:?}", value), + error: format!("{:?}", value), } } } diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs index e5523a7..15358e7 100644 --- a/rust/src/hal/mod.rs +++ b/rust/src/hal/mod.rs @@ -104,7 +104,7 @@ pub static TIME_ACCESS: OnceLock = OnceLock::new(); pub const PLANT_COUNT: usize = 8; const TANK_MULTI_SAMPLE: usize = 11; -static I2C_DRIVER: OnceLock< +pub static I2C_DRIVER: OnceLock< embassy_sync::blocking_mutex::Mutex>>, > = OnceLock::new(); @@ -144,13 +144,13 @@ pub trait BoardInteraction<'a> { async fn get_mptt_current(&mut self) -> Result; async fn progress(&mut self, counter: u32) { - let even = counter % 2 == 0; 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); } } + let even = counter % 2 == 0; let _ = self.general_fault(even.into()).await; } } @@ -529,7 +529,8 @@ impl PlantHal { // v3_hal::create_v3(free_pins, esp, config, battery_interaction, rtc_module)? // } BoardVersion::V4 => { - v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module)? + v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module) + .await? } _ => { todo!() diff --git a/rust/src/hal/v4_hal.rs b/rust/src/hal/v4_hal.rs index bc95a88..14c21a8 100644 --- a/rust/src/hal/v4_hal.rs +++ b/rust/src/hal/v4_hal.rs @@ -3,14 +3,21 @@ use crate::hal::battery::BatteryInteraction; use crate::hal::esp::Esp; use crate::hal::rtc::RTCModuleInteraction; use crate::hal::water::TankSensor; -use crate::hal::{BoardInteraction, FreePeripherals, Sensor}; +use crate::hal::{BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER}; use alloc::boxed::Box; use async_trait::async_trait; +use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use esp_hal::Blocking; //use embedded_hal_bus::i2c::MutexDevice; use crate::bail; -use crate::FatError::FatError; +use crate::FatError::{FatError, FatResult}; use esp_hal::gpio::{Flex, Level, Output, OutputConfig}; +use esp_hal::i2c::master::I2c; +use ina219::address::{Address, Pin}; +use ina219::SyncIna219; use measurements::{Current, Voltage}; +use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; // pub enum Charger<'a> { // SolarMpptV1 { // mppt_ina: SyncIna219>, UnCalibrated>, @@ -101,7 +108,7 @@ pub struct V4<'a> { awake: Output<'a>, light: Output<'a>, general_fault: Output<'a>, - //pump_expander: Pca9535Immediate>>, + pump_expander: Pca9535Immediate>>, //pump_ina: Option>, UnCalibrated>>, //sensor: SensorImpl<'a>, extra1: Output<'a>, @@ -112,7 +119,7 @@ struct InputOutput<'a> { pin: Flex<'a>, } -pub(crate) fn create_v4( +pub(crate) async fn create_v4( peripherals: FreePeripherals<'static>, esp: Esp<'static>, config: PlantControllerConfig, @@ -209,19 +216,18 @@ pub(crate) fn create_v4( let mut light = Output::new(peripherals.gpio10, Level::Low, Default::default()); let mut charge_indicator = Output::new(peripherals.gpio3, Level::Low, Default::default()); - // let mut pump_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 32); - // for pin in 0..8 { - // let _ = pump_expander.pin_into_output(GPIOBank::Bank0, pin); - // let _ = pump_expander.pin_into_output(GPIOBank::Bank1, pin); - // let _ = pump_expander.pin_set_low(GPIOBank::Bank0, pin); - // let _ = pump_expander.pin_set_low(GPIOBank::Bank1, pin); - // } - // - // let mppt_ina = SyncIna219::new( - // MutexDevice::new(&I2C_DRIVER), - // Address::from_pins(Pin::Vcc, Pin::Gnd), - // ); - // + let pump_device = I2cDevice::new(I2C_DRIVER.get().await); + let mut pump_expander = Pca9535Immediate::new(pump_device, 32); + for pin in 0..8 { + let _ = pump_expander.pin_into_output(GPIOBank::Bank0, pin); + let _ = pump_expander.pin_into_output(GPIOBank::Bank1, pin); + let _ = pump_expander.pin_set_low(GPIOBank::Bank0, pin); + let _ = pump_expander.pin_set_low(GPIOBank::Bank1, pin); + } + + let mppt_current = I2cDevice::new(I2C_DRIVER.get().await); + let mppt_ina = SyncIna219::new(mppt_current, Address::from_pins(Pin::Vcc, Pin::Gnd)); + // let charger = match mppt_ina { // Ok(mut mppt_ina) => { // mppt_ina.set_configuration(Configuration { @@ -261,7 +267,7 @@ pub(crate) fn create_v4( light, general_fault, //pump_ina, - //pump_expander, + pump_expander, config, battery_monitor, //charger, @@ -318,16 +324,15 @@ impl<'a> BoardInteraction<'a> for V4<'a> { // anyhow::Ok(()) } - async fn pump(&mut self, plant: usize, enable: bool) -> Result<(), FatError> { - bail!("not implemented"); - // if enable { - // self.pump_expander - // .pin_set_high(GPIOBank::Bank0, plant.try_into()?)?; - // } else { - // self.pump_expander - // .pin_set_low(GPIOBank::Bank0, plant.try_into()?)?; - // } - // anyhow::Ok(()) + async fn pump(&mut self, plant: usize, enable: bool) -> FatResult<()> { + if enable { + self.pump_expander + .pin_set_high(GPIOBank::Bank0, plant as u8)?; + } else { + self.pump_expander + .pin_set_low(GPIOBank::Bank0, plant as u8)?; + } + Ok(()) } async fn pump_current(&mut self, _plant: usize) -> Result { @@ -349,16 +354,15 @@ impl<'a> BoardInteraction<'a> for V4<'a> { // } } - async fn fault(&mut self, plant: usize, enable: bool) -> Result<(), FatError> { - bail!("not implemented"); - // if enable { - // self.pump_expander - // .pin_set_high(GPIOBank::Bank1, plant.try_into()?)? - // } else { - // self.pump_expander - // .pin_set_low(GPIOBank::Bank1, plant.try_into()?)? - // } - // anyhow::Ok(()) + async fn fault(&mut self, plant: usize, enable: bool) -> FatResult<()> { + if enable { + self.pump_expander + .pin_set_high(GPIOBank::Bank1, plant as u8)?; + } else { + self.pump_expander + .pin_set_low(GPIOBank::Bank1, plant as u8)?; + } + Ok(()) } async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result { diff --git a/rust/src/main.rs b/rust/src/main.rs index 80fc1f9..7e6bde7 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -2,6 +2,7 @@ #![no_main] #![feature(never_type)] #![feature(string_from_utf8_lossy_owned)] +#![feature(impl_trait_in_assoc_type)] #![deny( clippy::mem_forget, reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ diff --git a/rust/src/webserver/mod.rs b/rust/src/webserver/mod.rs index bfc0d89..3014e03 100644 --- a/rust/src/webserver/mod.rs +++ b/rust/src/webserver/mod.rs @@ -276,6 +276,7 @@ impl Handler for HttpHandler { let mut chunk = 0; loop { let mut board = BOARD_ACCESS.get().await.lock().await; + board.board_hal.progress(chunk as u32).await; let read_chunk = board .board_hal .get_esp() @@ -308,6 +309,7 @@ impl Handler for HttpHandler { } let mut offset = 0_usize; + let mut chunk = 0; loop { let mut buf = [0_u8; 1024]; let to_write = conn.read(&mut buf).await?; @@ -316,13 +318,15 @@ impl Handler for HttpHandler { break; } else { let mut board = BOARD_ACCESS.get().await.lock().await; + board.board_hal.progress(chunk as u32).await; board .board_hal .get_esp() .write_file(filename.to_owned(), offset as u32, &buf[0..to_write]) .await?; } - offset = offset + to_write + offset = offset + to_write; + chunk = chunk + 1; } Some(200) @@ -437,80 +441,67 @@ async fn get_backup_config( where T: Read + Write, { + // First pass: verify checksum without sending data let mut checksum = X25.digest(); let mut chunk = 0_usize; loop { - info!("Chunk {}", chunk); let mut board = BOARD_ACCESS.get().await.lock().await; board.board_hal.progress(chunk as u32).await; - let read_chunk = board + let (buf, len, expected_crc) = board .board_hal .get_rtc_module() .get_backup_config(chunk) .await?; - let length = read_chunk.1; - info!("Length {}", length); - if length == 0 { - let check = checksum.finalize(); - if check != read_chunk.2 { - if check != read_chunk.2 { - conn.initiate_response( - 409, - Some( - format!("Checksum mismatch expected {} got {}", read_chunk.2, check) - .as_str(), - ), - &[], - ) - .await?; - } - } - info!("file request for backup finished"); - break; - } - let data = &read_chunk.0[0..length]; - checksum.update(&data); - if length < read_chunk.0.len() { - let check = checksum.finalize(); - if check != read_chunk.2 { + + // Update checksum with the actual data bytes of this chunk + checksum.update(&buf[..len]); + + let is_last = len == 0 || len < buf.len(); + if is_last { + let actual_crc = checksum.finalize(); + if actual_crc != expected_crc { conn.initiate_response( 409, Some( - format!("Checksum mismatch expected {} got {}", read_chunk.2, check) - .as_str(), + format!( + "Checksum mismatch expected {} got {}", + expected_crc, actual_crc + ) + .as_str(), ), &[], ) .await?; + return Ok(()); } - info!("file request for backup finished"); break; } - chunk = chunk + 1; + chunk += 1; } + + // Second pass: stream data conn.initiate_response(200, Some("OK"), &[]).await?; - let mut chunk = 0; + let mut chunk = 0_usize; loop { let mut board = BOARD_ACCESS.get().await.lock().await; board.board_hal.progress(chunk as u32).await; - let read_chunk = board + let (buf, len, _expected_crc) = board .board_hal .get_rtc_module() .get_backup_config(chunk) .await?; - let length = read_chunk.1; - if length == 0 { - info!("file request for backup finished"); + + if len == 0 { break; } - let data = &read_chunk.0[0..length]; - conn.write_all(data).await?; - if length < read_chunk.0.len() { - info!("file request for backup finished"); + + conn.write_all(&buf[..len]).await?; + + if len < buf.len() { break; } - chunk = chunk + 1; + chunk += 1; } Ok(()) @@ -756,18 +747,18 @@ async fn get_time( _request: &mut Connection<'_, T, N>, ) -> FatResult> { let mut board = BOARD_ACCESS.get().await.lock().await; - //TODO do not fail if rtc module is missing + let native = esp_time().await.to_rfc3339(); - let rtc = "todo"; - // board - // .board_hal - // .get_rtc_module() - // .get_rtc_time() - // .await? - // .to_rfc3339(); + + let rtc = match board.board_hal.get_rtc_module().get_rtc_time().await { + Ok(time) => time.to_rfc3339(), + Err(err) => { + format!("Error getting time: {}", err) + } + }; let data = LoadData { - rtc, + rtc: rtc.as_str(), native: native.as_str(), }; let json = serde_json::to_string(&data)?; @@ -872,32 +863,6 @@ pub async fn httpd(reboot_now: Arc, stack: Stack<'static>) { // cors_response(request, 200, "") // }) // .unwrap(); - // server - // .fn_handler("/get_config", Method::Get, move |request| { - // handle_error_to500(request, get_config) - // }) - // .unwrap(); - // server - // .fn_handler("/get_backup_config", Method::Get, move |request| { - // handle_error_to500(request, get_backup_config) - // }) - // .unwrap(); - // - // server - // .fn_handler("/backup_config", Method::Post, move |request| { - // handle_error_to500(request, backup_config) - // }) - // .unwrap(); - // server - // .fn_handler("/backup_info", Method::Get, move |request| { - // handle_error_to500(request, backup_info) - // }) - // .unwrap(); - // server - // .fn_handler("/files", Method::Get, move |request| { - // handle_error_to500(request, list_files) - // }) - // .unwrap(); // let reboot_now_for_reboot = reboot_now.clone(); // server // .fn_handler("/reboot", Method::Post, move |_| {