add pump expander

This commit is contained in:
2025-09-23 00:26:05 +02:00
parent 5b0e2b6797
commit 1f3349c348
7 changed files with 111 additions and 124 deletions

View File

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

View File

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

View File

@@ -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<T> = Result<T, FatError>;
@@ -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<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!("HttpIoError {:?}", value),
error: format!("{:?}", value),
}
}
}
@@ -186,7 +191,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!("Ds323xError {:?}", value),
error: format!("{:?}", value),
}
}
}
@@ -194,7 +199,15 @@ 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!("Eeprom24xError {:?}", value),
error: format!("{:?}", value),
}
}
}
impl<E: core::fmt::Debug> From<ExpanderError<I2cDeviceError<E>>> for FatError {
fn from(value: ExpanderError<I2cDeviceError<E>>) -> Self {
FatError::ExpanderError {
error: format!("{:?}", value),
}
}
}
@@ -202,7 +215,7 @@ impl<E: core::fmt::Debug> From<eeprom24x::Error<E>> for FatError {
impl From<bincode::error::DecodeError> for FatError {
fn from(value: bincode::error::DecodeError) -> Self {
FatError::Eeprom24x {
error: format!("Eeprom24xError {:?}", value),
error: format!("{:?}", value),
}
}
}
@@ -210,7 +223,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!("Eeprom24xError {:?}", value),
error: format!("{:?}", value),
}
}
}

View File

@@ -104,7 +104,7 @@ pub static TIME_ACCESS: OnceLock<Rtc> = 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<CriticalSectionRawMutex, RefCell<I2c<Blocking>>>,
> = OnceLock::new();
@@ -144,13 +144,13 @@ pub trait BoardInteraction<'a> {
async fn get_mptt_current(&mut self) -> Result<Current, FatError>;
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!()

View File

@@ -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<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>,
@@ -101,7 +108,7 @@ pub struct V4<'a> {
awake: Output<'a>,
light: Output<'a>,
general_fault: Output<'a>,
//pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
pump_expander: Pca9535Immediate<I2cDevice<'a, CriticalSectionRawMutex, I2c<'static, Blocking>>>,
//pump_ina: Option<SyncIna219<MutexDevice<'a, I2cDriver<'a>>, 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<Current, FatError> {
@@ -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<f32, FatError> {

View File

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

View File

@@ -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<T, const N: usize>(
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<T, const N: usize>(
_request: &mut Connection<'_, T, N>,
) -> FatResult<Option<String>> {
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<AtomicBool>, 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 |_| {