refactor: unify moisture handling, update config structure, and add peer dependencies

This commit is contained in:
2025-10-23 22:44:44 +02:00
parent 1db3f7af64
commit cafe1b264e
26 changed files with 516 additions and 69 deletions

View File

@@ -115,8 +115,8 @@ pub struct PlantControllerConfig {
#[serde(default)]
pub struct PlantConfig {
pub mode: PlantWateringMode,
pub target_moisture: f32,
pub min_moisture: f32,
pub target_moisture: u8,
pub min_moisture: u8,
pub pump_time_s: u16,
pub pump_limit_ml: u16,
pub pump_cooldown_min: u16,
@@ -125,8 +125,8 @@ pub struct PlantConfig {
pub sensor_a: bool,
pub sensor_b: bool,
pub max_consecutive_pump_count: u8,
pub moisture_sensor_min_frequency: Option<f32>, // Optional min frequency
pub moisture_sensor_max_frequency: Option<f32>, // Optional max frequency
pub moisture_sensor_min_frequency: Option<u16>, // Optional min frequency
pub moisture_sensor_max_frequency: Option<u16>, // Optional max frequency
pub min_pump_current_ma: u16,
pub max_pump_current_ma: u16,
pub ignore_current_error: bool,
@@ -136,8 +136,8 @@ impl Default for PlantConfig {
fn default() -> Self {
Self {
mode: PlantWateringMode::Off,
target_moisture: 40.,
min_moisture: 30.,
target_moisture: 40,
min_moisture: 30,
pump_time_s: 30,
pump_limit_ml: 5000,
pump_cooldown_min: 60,

View File

@@ -316,3 +316,4 @@ impl From<sntpc::Error> for FatError {
FatError::SNTPError { error: value }
}
}

View File

@@ -146,7 +146,7 @@ macro_rules! mk_static {
($t:ty,$val:expr) => {{
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
#[deny(unused_attributes)]
let x = STATIC_CELL.uninit().write(($val));
let x = STATIC_CELL.uninit().write($val);
x
}};
}

View File

@@ -238,7 +238,7 @@ macro_rules! mk_static {
($t:ty,$val:expr) => {{
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
#[deny(unused_attributes)]
let x = STATIC_CELL.uninit().write(($val));
let x = STATIC_CELL.uninit().write($val);
x
}};
}

View File

@@ -1,17 +1,16 @@
use crate::bail;
use crate::fat_error::{ContextExt, FatError, FatResult};
use crate::fat_error::{ContextExt, FatResult};
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};
use embassy_time::{Duration, Timer, WithTimeout};
use embedded_can::{Frame, Id};
use esp_hal::gpio::Output;
use esp_hal::i2c::master::I2c;
@@ -86,7 +85,7 @@ impl SensorInteraction for SensorImpl {
} => {
can_power.set_high();
let config = twai_config.take().expect("twai config not set");
let mut twai = config.start();
let mut twai = config.into_async().start();
loop {
let rec = twai.receive();
@@ -100,15 +99,17 @@ impl SensorInteraction for SensorImpl {
}
Timer::after_millis(10).await;
let can = Self::inner_can(&mut twai).await;
let mut moistures = Moistures::default();
let _ = Self::wait_for_can_measurements(&mut twai, &mut moistures).with_timeout(Duration::from_millis(5000)).await;
can_power.set_low();
let config = twai.stop();
let config = twai.stop().into_blocking();
twai_config.replace(config);
let value = can?;
Ok(value)
Ok(moistures)
}
}
}
@@ -160,9 +161,8 @@ impl SensorImpl {
}
}
let mut result = DetectionResult::default();
// Wait for messages to arrive
let _ = Self::wait_for_can_measurements(&mut as_async, &mut result)
let mut moistures = Moistures::default();
let _ = Self::wait_for_can_measurements(&mut as_async, &mut moistures)
.with_timeout(Duration::from_millis(5000))
.await;
@@ -170,6 +170,8 @@ impl SensorImpl {
can_power.set_low();
twai_config.replace(config);
let result= moistures.into();
info!("Autodetection result: {result:?}");
Ok(result)
}
@@ -178,8 +180,9 @@ impl SensorImpl {
async fn wait_for_can_measurements(
as_async: &mut Twai<'_, Async>,
result: &mut DetectionResult,
) {
moistures: &mut Moistures,
) -> FatResult<()> {
loop {
match as_async.receive_async().await {
Ok(can_frame) => match can_frame.id() {
@@ -196,12 +199,14 @@ impl SensorImpl {
if msg.0 == MessageKind::MoistureData {
let plant = msg.1 as usize;
let sensor = msg.2;
let data = can_frame.data();
match sensor {
SensorSlot::A => {
result.plant[plant].sensor_a = true;
moistures.sensor_a_hz[plant] = data[0] as f32;
}
SensorSlot::B => {
result.plant[plant].sensor_b = true;
moistures.sensor_b_hz[plant] = data[0] as f32;
}
}
}
@@ -214,7 +219,6 @@ impl SensorImpl {
},
Err(err) => {
error!("Error receiving CAN message: {err:?}");
break;
}
}
}
@@ -302,31 +306,17 @@ impl SensorImpl {
let median = results[mid];
Ok(median)
}
async fn inner_can(twai: &mut Twai<'static, Blocking>) -> FatResult<Moistures> {
config::standard();
let timeout = Instant::now()
.checked_add(embassy_time::Duration::from_millis(100))
.context("Timeout")?;
loop {
let answer = twai.receive();
match answer {
Ok(answer) => {
info!("Received CAN message: {answer:?}");
}
Err(error) => match error {
nb::Error::Other(error) => {
return Err(FatError::CanBusError { error });
}
nb::Error::WouldBlock => {
if Instant::now() > timeout {
bail!("Timeout waiting for CAN answer");
}
Timer::after_millis(10).await;
}
},
}
}
}
}
impl From<Moistures> for DetectionResult {
fn from(value: Moistures) -> Self {
let mut result = DetectionResult::default();
for (plant, sensor) in value.sensor_a_hz.iter().enumerate() {
result.plant[plant].sensor_a = *sensor > 1.0_f32;
}
for (plant, sensor) in value.sensor_b_hz.iter().enumerate() {
result.plant[plant].sensor_b = *sensor > 1.0_f32;
}
result
}
}

View File

@@ -7,7 +7,7 @@ use deranged::RangedU8;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::mutex::Mutex;
use esp_hal::Persistable;
use log::info;
use log::{info, warn};
use serde::Serialize;
use strum_macros::IntoStaticStr;
use unit_enum::UnitEnum;
@@ -159,24 +159,41 @@ impl LogArray {
}
}
fn limit_length<const LIMIT: usize>(input: &str, target: &mut heapless::String<LIMIT>) {
for char in input.chars() {
match target.push(char) {
Ok(_) => {} //continue adding chars
Err(_) => {
//clear space for two asci chars
info!("pushing char {char} to limit {LIMIT} current value {target} input {input}");
while target.len() + 2 >= LIMIT {
target.pop().unwrap();
target.pop();
}
//add .. to shortened strings
target.push('.').unwrap();
target.push('.').unwrap();
match target.push('.'){
Ok(_) => {}
Err(_) => {
warn!("Error pushin . to limit {LIMIT} current value {target} input {input}")
}
}
match target.push('.'){
Ok(_) => {}
Err(_) => {
warn!("Error pushin . to limit {LIMIT} current value {target} input {input}")
}
}
return;
}
}
}
while target.len() < LIMIT {
target.push(' ').unwrap();
match target.push(' ') {
Ok(_) => {}
Err(_) => {
warn!("Error pushing space to limit {LIMIT} current value {target} input {input}")
}
}
}
}

View File

@@ -832,7 +832,7 @@ macro_rules! mk_static {
($t:ty,$val:expr) => {{
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
#[deny(unused_attributes)]
let x = STATIC_CELL.uninit().write(($val));
let x = STATIC_CELL.uninit().write($val);
x
}};
}

View File

@@ -120,8 +120,8 @@ impl PlantState {
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,
board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency.map(|a| a as f32),
board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency.map(|b| b as f32),
) {
Ok(moisture_percent) => MoistureSensorState::MoistureValue {
raw_hz: raw,
@@ -137,8 +137,8 @@ impl PlantState {
let raw = moistures.sensor_b_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,
board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency.map(|a| a as f32),
board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency.map(|b| b as f32)
) {
Ok(moisture_percent) => MoistureSensorState::MoistureValue {
raw_hz: raw,
@@ -186,7 +186,7 @@ impl PlantState {
pub fn plant_moisture(
&self,
) -> (
Option<f32>,
Option<u8>,
(Option<&MoistureSensorError>, Option<&MoistureSensorError>),
) {
match (
@@ -194,10 +194,10 @@ impl PlantState {
self.sensor_b.moisture_percent(),
) {
(Some(moisture_a), Some(moisture_b)) => {
(Some((moisture_a + moisture_b) / 2.), (None, None))
(Some(((moisture_a + moisture_b) / 2.) as u8), (None, None))
}
(Some(moisture_percent), _) => (Some(moisture_percent), (None, self.sensor_b.is_err())),
(_, Some(moisture_percent)) => (Some(moisture_percent), (self.sensor_a.is_err(), None)),
(Some(moisture_percent), _) => (Some(moisture_percent as u8), (None, self.sensor_b.is_err())),
(_, Some(moisture_percent)) => (Some(moisture_percent as u8), (self.sensor_a.is_err(), None)),
_ => (None, (self.sensor_a.is_err(), self.sensor_b.is_err())),
}
}