diff --git a/Software/MainBoard/rust/.idea/vcs.xml b/Software/MainBoard/rust/.idea/vcs.xml
index c654893..c2365ab 100644
--- a/Software/MainBoard/rust/.idea/vcs.xml
+++ b/Software/MainBoard/rust/.idea/vcs.xml
@@ -2,7 +2,5 @@
-
-
\ No newline at end of file
diff --git a/Software/MainBoard/rust/src/config.rs b/Software/MainBoard/rust/src/config.rs
index 947e0c0..f437d44 100644
--- a/Software/MainBoard/rust/src/config.rs
+++ b/Software/MainBoard/rust/src/config.rs
@@ -88,9 +88,8 @@ pub enum BatteryBoardVersion {
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
pub enum BoardVersion {
- #[default]
Initial,
- V3,
+ #[default]
V4,
}
diff --git a/Software/MainBoard/rust/src/fat_error.rs b/Software/MainBoard/rust/src/fat_error.rs
index f5b3306..96bbe11 100644
--- a/Software/MainBoard/rust/src/fat_error.rs
+++ b/Software/MainBoard/rust/src/fat_error.rs
@@ -194,7 +194,7 @@ impl From for FatError {
}
}
-impl From> for FatError {
+impl From> for FatError {
fn from(value: edge_http::io::Error) -> Self {
FatError::String {
error: format!("{value:?}"),
@@ -202,7 +202,7 @@ impl From> for FatError {
}
}
-impl From> for FatError {
+impl From> for FatError {
fn from(value: ds323x::Error) -> Self {
FatError::DS323 {
error: format!("{value:?}"),
@@ -210,7 +210,7 @@ impl From> for FatError {
}
}
-impl From> for FatError {
+impl From> for FatError {
fn from(value: eeprom24x::Error) -> Self {
FatError::Eeprom24x {
error: format!("{value:?}"),
@@ -218,7 +218,7 @@ impl From> for FatError {
}
}
-impl From>> for FatError {
+impl From>> for FatError {
fn from(value: ExpanderError>) -> Self {
FatError::ExpanderError {
error: format!("{value:?}"),
@@ -248,7 +248,7 @@ impl From for FatError {
}
}
-impl From> for FatError {
+impl From> for FatError {
fn from(value: I2cDeviceError) -> Self {
FatError::String {
error: format!("{value:?}"),
@@ -256,14 +256,14 @@ impl From> for FatError {
}
}
-impl From>> for FatError {
+impl From>> for FatError {
fn from(value: BusVoltageReadError>) -> Self {
FatError::String {
error: format!("{value:?}"),
}
}
}
-impl From>> for FatError {
+impl From>> for FatError {
fn from(value: ShuntVoltageReadError>) -> Self {
FatError::String {
error: format!("{value:?}"),
diff --git a/Software/MainBoard/rust/src/hal/esp.rs b/Software/MainBoard/rust/src/hal/esp.rs
index 4985e1a..25e006b 100644
--- a/Software/MainBoard/rust/src/hal/esp.rs
+++ b/Software/MainBoard/rust/src/hal/esp.rs
@@ -560,14 +560,11 @@ impl Esp<'_> {
duration_in_ms: u64,
mut rtc: MutexGuard,
) -> ! {
- // Configure and enter deep sleep using esp-hal. Also keep prior behavior where
- // duration_in_ms == 0 triggers an immediate reset.
-
// Mark the current OTA image as valid if we reached here while in pending verify.
if let Ok(cur) = self.ota.current_ota_state() {
if cur == OtaImageState::PendingVerify {
self.ota
- .set_current_ota_state(OtaImageState::Valid)
+ .set_current_ota_state(Valid)
.expect("Could not set image to valid");
}
}
@@ -673,12 +670,12 @@ impl Esp<'_> {
// 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}");
+ info!("LAST_WATERING_TIMESTAMP[{i}] = UTC {time}");
}
// 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}");
+ info!("CONSECUTIVE_WATERING_PLANT[{i}] = {item}");
}
}
}
@@ -963,7 +960,7 @@ async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) {
&mut buf,
)
.await
- .inspect_err(|e| log::warn!("DHCP server error: {e:?}"));
+ .inspect_err(|e| warn!("DHCP server error: {e:?}"));
Timer::after(Duration::from_millis(500)).await;
}
}
diff --git a/Software/MainBoard/rust/src/hal/little_fs2storage_adapter.rs b/Software/MainBoard/rust/src/hal/little_fs2storage_adapter.rs
index 987a7b7..c3b7a80 100644
--- a/Software/MainBoard/rust/src/hal/little_fs2storage_adapter.rs
+++ b/Software/MainBoard/rust/src/hal/little_fs2storage_adapter.rs
@@ -63,25 +63,25 @@ impl lfs2Storage for LittleFs2Filesystem {
let block_size: usize = Self::BLOCK_SIZE;
if off % block_size != 0 {
error!("Littlefs2Filesystem erase error: offset not aligned to block size offset: {off} block_size: {block_size}");
- return lfs2Result::Err(lfs2Error::IO);
+ return Err(lfs2Error::IO);
}
if len % block_size != 0 {
error!("Littlefs2Filesystem erase error: length not aligned to block size length: {len} block_size: {block_size}");
- return lfs2Result::Err(lfs2Error::IO);
+ return Err(lfs2Error::IO);
}
match check_erase(self.storage, off as u32, (off + len) as u32) {
Ok(_) => {}
Err(err) => {
error!("Littlefs2Filesystem check erase error: {err:?}");
- return lfs2Result::Err(lfs2Error::IO);
+ return Err(lfs2Error::IO);
}
}
match self.storage.erase(off as u32, (off + len) as u32) {
- Ok(..) => lfs2Result::Ok(len),
+ Ok(..) => Ok(len),
Err(err) => {
error!("Littlefs2Filesystem erase error: {err:?}");
- lfs2Result::Err(lfs2Error::IO)
+ Err(lfs2Error::IO)
}
}
}
diff --git a/Software/MainBoard/rust/src/hal/mod.rs b/Software/MainBoard/rust/src/hal/mod.rs
index c995dcf..04e2175 100644
--- a/Software/MainBoard/rust/src/hal/mod.rs
+++ b/Software/MainBoard/rust/src/hal/mod.rs
@@ -5,10 +5,7 @@ mod initial_hal;
mod little_fs2storage_adapter;
pub(crate) mod rtc;
mod shared_flash;
-mod v3_hal;
-mod v3_shift_register;
mod v4_hal;
-pub(crate) mod v4_sensor;
mod water;
use crate::alloc::string::ToString;
@@ -169,7 +166,7 @@ pub trait BoardInteraction<'a> {
async fn progress(&mut self, counter: u32) {
// Indicate progress is active to suppress default wait_infinity blinking
- crate::hal::PROGRESS_ACTIVE.store(true, core::sync::atomic::Ordering::Relaxed);
+ PROGRESS_ACTIVE.store(true, core::sync::atomic::Ordering::Relaxed);
let current = counter % PLANT_COUNT as u32;
for led in 0..PLANT_COUNT {
@@ -190,7 +187,7 @@ pub trait BoardInteraction<'a> {
let _ = self.general_fault(false).await;
// Reset progress active flag so wait_infinity can resume blinking
- crate::hal::PROGRESS_ACTIVE.store(false, core::sync::atomic::Ordering::Relaxed);
+ PROGRESS_ACTIVE.store(false, core::sync::atomic::Ordering::Relaxed);
}
}
@@ -345,7 +342,7 @@ impl PlantHal {
info!("Slot0 state: {state_0:?}");
info!("Slot1 state: {state_1:?}");
- //obtain current_state and next_state here!
+ //get current_state and next_state here!
let ota_target = match target {
AppPartitionSubType::Ota0 => pt
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App(
@@ -382,11 +379,11 @@ impl PlantHal {
let lfs2filesystem = mk_static!(LittleFs2Filesystem, LittleFs2Filesystem { storage: data });
let alloc = mk_static!(Allocation, lfs2Filesystem::allocate());
if lfs2filesystem.is_mountable() {
- log::info!("Littlefs2 filesystem is mountable");
+ info!("Littlefs2 filesystem is mountable");
} else {
match lfs2filesystem.format() {
- Result::Ok(..) => {
- log::info!("Littlefs2 filesystem is formatted");
+ Ok(..) => {
+ info!("Littlefs2 filesystem is formatted");
}
Err(err) => {
error!("Littlefs2 filesystem could not be formatted: {err:?}");
@@ -466,7 +463,7 @@ impl PlantHal {
let config = esp.load_config().await;
- log::info!("Init rtc driver");
+ info!("Init rtc driver");
let sda = peripherals.GPIO20;
let scl = peripherals.GPIO19;
@@ -500,10 +497,10 @@ impl PlantHal {
let rtc_time = rtc.datetime();
match rtc_time {
Ok(tt) => {
- log::info!("Rtc Module reports time at UTC {tt}");
+ info!("Rtc Module reports time at UTC {tt}");
}
Err(err) => {
- log::info!("Rtc Module could not be read {err:?}");
+ info!("Rtc Module could not be read {err:?}");
}
}
@@ -518,7 +515,7 @@ impl PlantHal {
Box::new(DS3231Module { rtc, storage }) as Box;
let hal = match config {
- Result::Ok(config) => {
+ Ok(config) => {
let battery_interaction: Box =
match config.hardware.battery {
BatteryBoardVersion::Disabled => Box::new(NoBatteryMonitor {}),
@@ -558,9 +555,6 @@ impl PlantHal {
BoardVersion::Initial => {
initial_hal::create_initial_board(free_pins, config, esp)?
}
- BoardVersion::V3 => {
- 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)
.await?
@@ -705,7 +699,7 @@ pub struct Moistures {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
pub struct DetectionResult {
- plant: [DetectionSensorResult; crate::hal::PLANT_COUNT],
+ plant: [DetectionSensorResult; PLANT_COUNT],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
pub struct DetectionSensorResult {
diff --git a/Software/MainBoard/rust/src/hal/shared_flash.rs b/Software/MainBoard/rust/src/hal/shared_flash.rs
index 69bd76e..b206ea5 100644
--- a/Software/MainBoard/rust/src/hal/shared_flash.rs
+++ b/Software/MainBoard/rust/src/hal/shared_flash.rs
@@ -36,7 +36,7 @@ impl ErrorType for MutexFlashStorage {
}
impl ReadNorFlash for MutexFlashStorage {
- const READ_SIZE: usize = 0;
+ const READ_SIZE: usize = 1;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
ReadStorage::read(self, offset, bytes)
@@ -48,8 +48,8 @@ impl ReadNorFlash for MutexFlashStorage {
}
impl NorFlash for MutexFlashStorage {
- const WRITE_SIZE: usize = 0;
- const ERASE_SIZE: usize = 0;
+ const WRITE_SIZE: usize = 1;
+ const ERASE_SIZE: usize = 4096;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.inner
diff --git a/Software/MainBoard/rust/src/hal/v3_hal.rs b/Software/MainBoard/rust/src/hal/v3_hal.rs
deleted file mode 100644
index 244bcbe..0000000
--- a/Software/MainBoard/rust/src/hal/v3_hal.rs
+++ /dev/null
@@ -1,458 +0,0 @@
-use crate::bail;
-use crate::fat_error::FatError;
-use crate::hal::esp::{hold_disable, hold_enable};
-use crate::hal::rtc::RTCModuleInteraction;
-use crate::hal::v3_shift_register::ShiftRegister40;
-use crate::hal::water::TankSensor;
-use crate::hal::{BoardInteraction, FreePeripherals, Moistures, Sensor, PLANT_COUNT, TIME_ACCESS};
-use crate::log::{LogMessage, LOG_ACCESS};
-use crate::{
- config::PlantControllerConfig,
- hal::{battery::BatteryInteraction, esp::Esp},
-};
-use alloc::boxed::Box;
-use alloc::format;
-use alloc::string::ToString;
-use async_trait::async_trait;
-use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
-use embassy_sync::mutex::Mutex;
-use embassy_time::Timer;
-use embedded_hal::digital::OutputPin as _;
-use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull};
-use esp_hal::pcnt::channel::CtrlMode::Keep;
-use esp_hal::pcnt::channel::EdgeMode::{Hold, Increment};
-use esp_hal::pcnt::unit::Unit;
-use measurements::{Current, Voltage};
-
-const PUMP8_BIT: usize = 0;
-const PUMP1_BIT: usize = 1;
-const PUMP2_BIT: usize = 2;
-const PUMP3_BIT: usize = 3;
-const PUMP4_BIT: usize = 4;
-const PUMP5_BIT: usize = 5;
-const PUMP6_BIT: usize = 6;
-const PUMP7_BIT: usize = 7;
-const MS_0: usize = 8;
-const MS_4: usize = 9;
-const MS_2: usize = 10;
-const MS_3: usize = 11;
-const MS_1: usize = 13;
-const SENSOR_ON: usize = 12;
-
-const SENSOR_A_1: u8 = 7;
-const SENSOR_A_2: u8 = 6;
-const SENSOR_A_3: u8 = 5;
-const SENSOR_A_4: u8 = 4;
-const SENSOR_A_5: u8 = 3;
-const SENSOR_A_6: u8 = 2;
-const SENSOR_A_7: u8 = 1;
-const SENSOR_A_8: u8 = 0;
-
-const SENSOR_B_1: u8 = 8;
-const SENSOR_B_2: u8 = 9;
-const SENSOR_B_3: u8 = 10;
-const SENSOR_B_4: u8 = 11;
-const SENSOR_B_5: u8 = 12;
-const SENSOR_B_6: u8 = 13;
-const SENSOR_B_7: u8 = 14;
-const SENSOR_B_8: u8 = 15;
-
-const CHARGING: usize = 14;
-const AWAKE: usize = 15;
-
-const FAULT_3: usize = 16;
-const FAULT_8: usize = 17;
-const FAULT_7: usize = 18;
-const FAULT_6: usize = 19;
-const FAULT_5: usize = 20;
-const FAULT_4: usize = 21;
-const FAULT_1: usize = 22;
-const FAULT_2: usize = 23;
-
-const REPEAT_MOIST_MEASURE: usize = 1;
-
-pub struct V3<'a> {
- config: PlantControllerConfig,
- battery_monitor: Box,
- rtc_module: Box,
- esp: Esp<'a>,
- shift_register:
- Mutex, Output<'a>, Output<'a>>>,
- _shift_register_enable_invert: Output<'a>,
- tank_sensor: TankSensor<'a>,
- solar_is_day: Input<'a>,
- light: Output<'a>,
- main_pump: Output<'a>,
- general_fault: Output<'a>,
- pub signal_counter: Unit<'static, 0>,
-}
-
-pub(crate) fn create_v3(
- peripherals: FreePeripherals<'static>,
- esp: Esp<'static>,
- config: PlantControllerConfig,
- battery_monitor: Box,
- rtc_module: Box,
-) -> Result + Send + 'static>, FatError> {
- log::info!("Start v3");
- let clock = Output::new(peripherals.gpio15, Level::Low, OutputConfig::default());
- let latch = Output::new(peripherals.gpio3, Level::Low, OutputConfig::default());
- let data = Output::new(peripherals.gpio23, Level::Low, OutputConfig::default());
- let shift_register = ShiftRegister40::new(clock, latch, data);
- //disable all
- for mut pin in shift_register.decompose() {
- let _ = pin.set_low();
- }
-
- // Set always-on status bits
- let _ = shift_register.decompose()[AWAKE].set_high();
- let _ = shift_register.decompose()[CHARGING].set_high();
-
- // Multiplexer defaults: ms0..ms3 low, ms4 high (disabled)
- let _ = shift_register.decompose()[MS_0].set_low();
- let _ = shift_register.decompose()[MS_1].set_low();
- let _ = shift_register.decompose()[MS_2].set_low();
- let _ = shift_register.decompose()[MS_3].set_low();
- let _ = shift_register.decompose()[MS_4].set_high();
-
- let one_wire_pin = Flex::new(peripherals.gpio18);
- let tank_power_pin = Output::new(peripherals.gpio11, Level::Low, OutputConfig::default());
-
- let flow_sensor_pin = Input::new(
- peripherals.gpio4,
- InputConfig::default().with_pull(Pull::Up),
- );
-
- let tank_sensor = TankSensor::create(
- one_wire_pin,
- peripherals.adc1,
- peripherals.gpio5,
- tank_power_pin,
- flow_sensor_pin,
- peripherals.pcnt1,
- )?;
-
- let solar_is_day = Input::new(peripherals.gpio7, InputConfig::default());
- let light = Output::new(peripherals.gpio10, Level::Low, OutputConfig::default());
- let mut main_pump = Output::new(peripherals.gpio2, Level::Low, OutputConfig::default());
- main_pump.set_low();
- let mut general_fault = Output::new(peripherals.gpio6, Level::Low, OutputConfig::default());
- general_fault.set_low();
-
- let mut shift_register_enable_invert =
- Output::new(peripherals.gpio21, Level::Low, OutputConfig::default());
- shift_register_enable_invert.set_low();
-
- let signal_counter = peripherals.pcnt0;
-
- signal_counter.set_high_limit(Some(i16::MAX))?;
-
- let ch0 = &signal_counter.channel0;
- let edge_pin = Input::new(peripherals.gpio22, InputConfig::default());
- ch0.set_edge_signal(edge_pin.peripheral_input());
- ch0.set_input_mode(Hold, Increment);
- ch0.set_ctrl_mode(Keep, Keep);
- signal_counter.listen();
-
- Ok(Box::new(V3 {
- config,
- battery_monitor,
- rtc_module,
- esp,
- shift_register: Mutex::new(shift_register),
- _shift_register_enable_invert: shift_register_enable_invert,
- tank_sensor,
- solar_is_day,
- light,
- main_pump,
- general_fault,
- signal_counter,
- }))
-}
-
-impl V3<'_> {
- async fn inner_measure_moisture_hz(
- &mut self,
- plant: usize,
- sensor: Sensor,
- ) -> Result {
- let mut results = [0_f32; REPEAT_MOIST_MEASURE];
- for sample in results.iter_mut() {
- self.signal_counter.pause();
- self.signal_counter.clear();
- //Disable all
- {
- let shift_register = self.shift_register.lock().await;
- shift_register.decompose()[MS_4].set_high()?;
- }
-
- let sensor_channel = match sensor {
- Sensor::A => match plant {
- 0 => SENSOR_A_1,
- 1 => SENSOR_A_2,
- 2 => SENSOR_A_3,
- 3 => SENSOR_A_4,
- 4 => SENSOR_A_5,
- 5 => SENSOR_A_6,
- 6 => SENSOR_A_7,
- 7 => SENSOR_A_8,
- _ => bail!("Invalid plant id {}", plant),
- },
- Sensor::B => match plant {
- 0 => SENSOR_B_1,
- 1 => SENSOR_B_2,
- 2 => SENSOR_B_3,
- 3 => SENSOR_B_4,
- 4 => SENSOR_B_5,
- 5 => SENSOR_B_6,
- 6 => SENSOR_B_7,
- 7 => SENSOR_B_8,
- _ => bail!("Invalid plant id {}", plant),
- },
- };
-
- let is_bit_set = |b: u8| -> bool { sensor_channel & (1 << b) != 0 };
- {
- let shift_register = self.shift_register.lock().await;
- let pin_0 = &mut shift_register.decompose()[MS_0];
- let pin_1 = &mut shift_register.decompose()[MS_1];
- let pin_2 = &mut shift_register.decompose()[MS_2];
- let pin_3 = &mut shift_register.decompose()[MS_3];
- if is_bit_set(0) {
- pin_0.set_high()?;
- } else {
- pin_0.set_low()?;
- }
- if is_bit_set(1) {
- pin_1.set_high()?;
- } else {
- pin_1.set_low()?;
- }
- if is_bit_set(2) {
- pin_2.set_high()?;
- } else {
- pin_2.set_low()?;
- }
- if is_bit_set(3) {
- pin_3.set_high()?;
- } else {
- pin_3.set_low()?;
- }
-
- shift_register.decompose()[MS_4].set_low()?;
- shift_register.decompose()[SENSOR_ON].set_high()?;
- }
- let measurement = 100; //how long to measure and then extrapolate to hz
- let factor = 1000f32 / measurement as f32; //scale raw cound by this number to get hz
-
- //give some time to stabilize
- Timer::after_millis(10).await;
- self.signal_counter.resume();
- Timer::after_millis(measurement).await;
- self.signal_counter.pause();
- {
- let shift_register = self.shift_register.lock().await;
- shift_register.decompose()[MS_4].set_high()?;
- shift_register.decompose()[SENSOR_ON].set_low()?;
- }
- Timer::after_millis(10).await;
- let unscaled = self.signal_counter.value();
- 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)
- }
-}
-
-#[async_trait(?Send)]
-impl<'a> BoardInteraction<'a> for V3<'a> {
- fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError> {
- Ok(&mut self.tank_sensor)
- }
-
- fn get_esp(&mut self) -> &mut Esp<'a> {
- &mut self.esp
- }
-
- fn get_config(&mut self) -> &PlantControllerConfig {
- &self.config
- }
-
- fn get_battery_monitor(&mut self) -> &mut Box {
- &mut self.battery_monitor
- }
-
- fn get_rtc_module(&mut self) -> &mut Box {
- &mut self.rtc_module
- }
- async fn set_charge_indicator(&mut self, charging: bool) -> Result<(), FatError> {
- let shift_register = self.shift_register.lock().await;
- if charging {
- let _ = shift_register.decompose()[CHARGING].set_high();
- } else {
- let _ = shift_register.decompose()[CHARGING].set_low();
- }
- Ok(())
- }
-
- async fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
- let _ = self.shift_register.lock().await.decompose()[AWAKE].set_low();
- let guard = TIME_ACCESS.get().await.lock().await;
- self.esp.deep_sleep(duration_in_ms, guard)
- }
-
- fn is_day(&self) -> bool {
- self.solar_is_day.is_high()
- }
-
- async fn light(&mut self, enable: bool) -> Result<(), FatError> {
- hold_disable(10);
- if enable {
- self.light.set_high();
- } else {
- self.light.set_low();
- }
- hold_enable(10);
- Ok(())
- }
- async fn pump(&mut self, plant: usize, enable: bool) -> Result<(), FatError> {
- if enable {
- self.main_pump.set_high();
- }
-
- let index = match plant {
- 0 => PUMP1_BIT,
- 1 => PUMP2_BIT,
- 2 => PUMP3_BIT,
- 3 => PUMP4_BIT,
- 4 => PUMP5_BIT,
- 5 => PUMP6_BIT,
- 6 => PUMP7_BIT,
- 7 => PUMP8_BIT,
- _ => bail!("Invalid pump {plant}"),
- };
- let shift_register = self.shift_register.lock().await;
- if enable {
- let _ = shift_register.decompose()[index].set_high();
- } else {
- let _ = shift_register.decompose()[index].set_low();
- }
-
- if !enable {
- self.main_pump.set_low();
- }
- Ok(())
- }
-
- async fn pump_current(&mut self, _plant: usize) -> Result {
- bail!("Not implemented in v3")
- }
-
- async fn fault(&mut self, plant: usize, enable: bool) -> Result<(), FatError> {
- let index = match plant {
- 0 => FAULT_1,
- 1 => FAULT_2,
- 2 => FAULT_3,
- 3 => FAULT_4,
- 4 => FAULT_5,
- 5 => FAULT_6,
- 6 => FAULT_7,
- 7 => FAULT_8,
- _ => panic!("Invalid plant id {}", plant),
- };
- let shift_register = self.shift_register.lock().await;
- if enable {
- let _ = shift_register.decompose()[index].set_high();
- } else {
- let _ = shift_register.decompose()[index].set_low();
- }
- Ok(())
- }
-
- async fn measure_moisture_hz(&mut self) -> Result {
- let mut result = Moistures::default();
- 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(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(),
- "",
- )
- .await;
- result.sensor_a_hz[plant] = aa;
- result.sensor_b_hz[plant] = bb;
- }
- Ok(result)
- }
-
- async fn general_fault(&mut self, enable: bool) {
- hold_disable(6);
- if enable {
- self.general_fault.set_high();
- } else {
- self.general_fault.set_low();
- }
- hold_enable(6);
- }
-
- async fn test(&mut self) -> Result<(), FatError> {
- self.general_fault(true).await;
- Timer::after_millis(100).await;
- self.general_fault(false).await;
- Timer::after_millis(100).await;
- self.light(true).await?;
- Timer::after_millis(500).await;
-
- self.light(false).await?;
- Timer::after_millis(500).await;
- for i in 0..PLANT_COUNT {
- self.fault(i, true).await?;
- Timer::after_millis(500).await;
- self.fault(i, false).await?;
- Timer::after_millis(500).await;
- }
- for i in 0..PLANT_COUNT {
- self.pump(i, true).await?;
- Timer::after_millis(100).await;
- self.pump(i, false).await?;
- Timer::after_millis(100).await;
- }
- self.measure_moisture_hz().await?;
- Timer::after_millis(10).await;
- Ok(())
- }
-
- fn set_config(&mut self, config: PlantControllerConfig) {
- self.config = config;
- }
-
- async fn get_mptt_voltage(&mut self) -> Result {
- bail!("Not implemented in v3")
- }
- async fn get_mptt_current(&mut self) -> Result {
- bail!("Not implemented in v3")
- }
-}
diff --git a/Software/MainBoard/rust/src/hal/v3_shift_register.rs b/Software/MainBoard/rust/src/hal/v3_shift_register.rs
deleted file mode 100644
index f931e4e..0000000
--- a/Software/MainBoard/rust/src/hal/v3_shift_register.rs
+++ /dev/null
@@ -1,154 +0,0 @@
-//! Serial-in parallel-out shift register
-#![allow(warnings)]
-use core::cell::RefCell;
-use core::convert::Infallible;
-use core::iter::Iterator;
-use core::mem::{self, MaybeUninit};
-use core::result::{Result, Result::Ok};
-use embedded_hal::digital::OutputPin;
-
-trait ShiftRegisterInternal: Send {
- fn update(&self, index: usize, command: bool) -> Result<(), ()>;
-}
-
-/// Output pin of the shift register
-pub struct ShiftRegisterPin<'a> {
- shift_register: &'a dyn ShiftRegisterInternal,
- index: usize,
-}
-
-impl<'a> ShiftRegisterPin<'a> {
- fn new(shift_register: &'a dyn ShiftRegisterInternal, index: usize) -> Self {
- ShiftRegisterPin {
- shift_register,
- index,
- }
- }
-}
-
-impl embedded_hal::digital::ErrorType for ShiftRegisterPin<'_> {
- type Error = Infallible;
-}
-
-impl OutputPin for ShiftRegisterPin<'_> {
- fn set_low(&mut self) -> Result<(), Infallible> {
- self.shift_register.update(self.index, false).unwrap();
- Ok(())
- }
-
- fn set_high(&mut self) -> Result<(), Infallible> {
- self.shift_register.update(self.index, true).unwrap();
- Ok(())
- }
-}
-
-macro_rules! ShiftRegisterBuilder {
- ($name: ident, $size: expr) => {
- /// Serial-in parallel-out shift register
- pub struct $name
- where
- Pin1: OutputPin + Send,
- Pin2: OutputPin + Send,
- Pin3: OutputPin + Send,
- {
- clock: RefCell,
- latch: RefCell,
- data: RefCell,
- output_state: RefCell<[bool; $size]>,
- }
-
- impl ShiftRegisterInternal for $name
- where
- Pin1: OutputPin + Send,
- Pin2: OutputPin + Send,
- Pin3: OutputPin + Send,
- {
- /// Sets the value of the shift register output at `index` to value `command`
- fn update(&self, index: usize, command: bool) -> Result<(), ()> {
- self.output_state.borrow_mut()[index] = command;
- let output_state = self.output_state.borrow();
- self.latch.borrow_mut().set_low().map_err(|_e| ())?;
-
- for i in 1..=output_state.len() {
- if output_state[output_state.len() - i] {
- self.data.borrow_mut().set_high().map_err(|_e| ())?;
- } else {
- self.data.borrow_mut().set_low().map_err(|_e| ())?;
- }
- self.clock.borrow_mut().set_high().map_err(|_e| ())?;
- self.clock.borrow_mut().set_low().map_err(|_e| ())?;
- }
-
- self.latch.borrow_mut().set_high().map_err(|_e| ())?;
- Ok(())
- }
- }
-
- impl $name
- where
- Pin1: OutputPin + Send,
- Pin2: OutputPin + Send,
- Pin3: OutputPin + Send,
- {
- /// Creates a new SIPO shift register from clock, latch, and data output pins
- pub fn new(clock: Pin1, latch: Pin2, data: Pin3) -> Self {
- $name {
- clock: RefCell::new(clock),
- latch: RefCell::new(latch),
- data: RefCell::new(data),
- output_state: RefCell::new([false; $size]),
- }
- }
-
- /// Get embedded-hal output pins to control the shift register outputs
- pub fn decompose(&self) -> [ShiftRegisterPin<'_>; $size] {
- // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
- // safe because the type we are claiming to have initialized here is a
- // bunch of `MaybeUninit`s, which do not require initialization.
- let mut pins: [MaybeUninit; $size] =
- unsafe { MaybeUninit::uninit().assume_init() };
-
- // Dropping a `MaybeUninit` does nothing, so if there is a panic during this loop,
- // we have a memory leak, but there is no memory safety issue.
- for (index, elem) in pins.iter_mut().enumerate() {
- elem.write(ShiftRegisterPin::new(self, index));
- }
-
- // Everything is initialized. Transmute the array to the
- // initialized type.
- unsafe { mem::transmute::<_, [ShiftRegisterPin; $size]>(pins) }
- }
-
- /// Consume the shift register and return the original clock, latch, and data output pins
- pub fn release(self) -> (Pin1, Pin2, Pin3) {
- let Self {
- clock,
- latch,
- data,
- output_state: _,
- } = self;
- (clock.into_inner(), latch.into_inner(), data.into_inner())
- }
- }
- };
-}
-
-ShiftRegisterBuilder!(ShiftRegister8, 8);
-ShiftRegisterBuilder!(ShiftRegister16, 16);
-ShiftRegisterBuilder!(ShiftRegister24, 24);
-ShiftRegisterBuilder!(ShiftRegister32, 32);
-ShiftRegisterBuilder!(ShiftRegister40, 40);
-ShiftRegisterBuilder!(ShiftRegister48, 48);
-ShiftRegisterBuilder!(ShiftRegister56, 56);
-ShiftRegisterBuilder!(ShiftRegister64, 64);
-ShiftRegisterBuilder!(ShiftRegister72, 72);
-ShiftRegisterBuilder!(ShiftRegister80, 80);
-ShiftRegisterBuilder!(ShiftRegister88, 88);
-ShiftRegisterBuilder!(ShiftRegister96, 96);
-ShiftRegisterBuilder!(ShiftRegister104, 104);
-ShiftRegisterBuilder!(ShiftRegister112, 112);
-ShiftRegisterBuilder!(ShiftRegister120, 120);
-ShiftRegisterBuilder!(ShiftRegister128, 128);
-
-/// 8 output serial-in parallel-out shift register
-pub type ShiftRegister = ShiftRegister8;
diff --git a/Software/MainBoard/rust/src/hal/v4_hal.rs b/Software/MainBoard/rust/src/hal/v4_hal.rs
index 30b507f..08807a4 100644
--- a/Software/MainBoard/rust/src/hal/v4_hal.rs
+++ b/Software/MainBoard/rust/src/hal/v4_hal.rs
@@ -1,32 +1,33 @@
use crate::bail;
use crate::config::PlantControllerConfig;
-use crate::fat_error::{FatError, FatResult};
+use crate::fat_error::{ContextExt, FatError, FatResult};
use crate::hal::battery::BatteryInteraction;
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,
+ BoardInteraction, DetectionResult, FreePeripherals, Moistures, Sensor, I2C_DRIVER, PLANT_COUNT,
TIME_ACCESS,
};
use crate::log::{LogMessage, LOG_ACCESS};
use alloc::boxed::Box;
use alloc::string::ToString;
use async_trait::async_trait;
+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::Timer;
+use embassy_time::{Duration, Timer, WithTimeout};
+use embedded_can::{Frame, Id};
use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull};
use esp_hal::i2c::master::I2c;
-use esp_hal::pcnt::channel::CtrlMode::Keep;
-use esp_hal::pcnt::channel::EdgeMode::{Hold, Increment};
-use esp_hal::twai::TwaiMode;
-use esp_hal::{twai, Blocking};
+use esp_hal::twai::{EspTwaiError, EspTwaiFrame, StandardId, Twai, TwaiConfiguration, TwaiMode};
+use esp_hal::{twai, Async, Blocking};
use ina219::address::{Address, Pin};
use ina219::calibration::UnCalibrated;
use ina219::configuration::{Configuration, OperatingMode, Resolution};
use ina219::SyncIna219;
+use log::{error, info, warn};
use measurements::Resistance;
use measurements::{Current, Voltage};
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
@@ -88,7 +89,7 @@ impl Charger<'_> {
operating_mode: OperatingMode::PowerDown,
})
.map_err(|e| {
- log::info!(
+ info!(
"Error setting ina mppt configuration during deep sleep preparation{e:?}"
);
});
@@ -127,7 +128,8 @@ pub struct V4<'a> {
pump_ina: Option<
SyncIna219>, UnCalibrated>,
>,
- sensor: SensorImpl,
+ twai_config: Option>,
+ can_power: Output<'static>,
extra1: Output<'a>,
extra2: Output<'a>,
}
@@ -139,7 +141,7 @@ pub(crate) async fn create_v4(
battery_monitor: Box,
rtc_module: Box,
) -> Result + Send + 'static>, FatError> {
- log::info!("Start v4");
+ info!("Start v4");
let mut awake = Output::new(peripherals.gpio21, Level::High, OutputConfig::default());
awake.set_high();
@@ -165,53 +167,14 @@ pub(crate) async fn create_v4(
peripherals.pcnt1,
)?;
- let sensor_expander_device = I2cDevice::new(I2C_DRIVER.get().await);
- let mut sensor_expander = Pca9535Immediate::new(sensor_expander_device, 34);
- let sensor = match sensor_expander.pin_into_output(GPIOBank::Bank0, 0) {
- Ok(_) => {
- log::info!("SensorExpander answered");
-
- let signal_counter = peripherals.pcnt0;
-
- signal_counter.set_high_limit(Some(i16::MAX))?;
-
- let ch0 = &signal_counter.channel0;
- let edge_pin = Input::new(peripherals.gpio22, InputConfig::default());
- ch0.set_edge_signal(edge_pin.peripheral_input());
- ch0.set_input_mode(Hold, Increment);
- ch0.set_ctrl_mode(Keep, Keep);
- signal_counter.listen();
-
- for pin in 0..8 {
- let _ = sensor_expander.pin_into_output(GPIOBank::Bank0, pin);
- let _ = sensor_expander.pin_into_output(GPIOBank::Bank1, pin);
- let _ = sensor_expander.pin_set_low(GPIOBank::Bank0, pin);
- let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
- }
-
- SensorImpl::PulseCounter {
- signal_counter,
- sensor_expander,
- }
- }
- Err(_) => {
- log::info!("Can bus mode ");
- let twai_config = Some(twai::TwaiConfiguration::new(
- peripherals.twai,
- peripherals.gpio2,
- peripherals.gpio0,
- TWAI_BAUDRATE,
- TwaiMode::Normal,
- ));
- let can_power = Output::new(peripherals.gpio22, Level::Low, OutputConfig::default());
-
- //can bus version
- SensorImpl::CanBus {
- twai_config,
- can_power,
- }
- }
- };
+ let twai_config = Some(TwaiConfiguration::new(
+ peripherals.twai,
+ peripherals.gpio2,
+ peripherals.gpio0,
+ TWAI_BAUDRATE,
+ TwaiMode::Normal,
+ ));
+ let can_power = Output::new(peripherals.gpio22, Level::Low, OutputConfig::default());
let solar_is_day = Input::new(peripherals.gpio7, InputConfig::default());
let light = Output::new(peripherals.gpio10, Level::Low, Default::default());
@@ -241,7 +204,7 @@ pub(crate) async fn create_v4(
Some(ina)
}
Err(err) => {
- log::info!("Error creating mppt ina: {err:?}");
+ info!("Error creating mppt ina: {err:?}");
None
}
};
@@ -250,7 +213,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:?}");
+ info!("Error creating pump ina: {err:?}");
None
}
};
@@ -262,7 +225,7 @@ pub(crate) async fn create_v4(
bus_voltage_range: Default::default(),
shunt_voltage_range: Default::default(),
bus_resolution: Default::default(),
- shunt_resolution: ina219::configuration::Resolution::Avg128,
+ shunt_resolution: Resolution::Avg128,
operating_mode: Default::default(),
})?;
@@ -275,6 +238,7 @@ pub(crate) async fn create_v4(
None => Charger::ErrorInit {},
};
+ info!("Assembling final v4 board interaction object");
let v = V4 {
rtc_module,
esp,
@@ -286,10 +250,11 @@ pub(crate) async fn create_v4(
config,
battery_monitor,
pump_ina,
+ twai_config,
charger,
extra1,
extra2,
- sensor,
+ can_power,
};
Ok(Box::new(v))
}
@@ -383,9 +348,35 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
}
Ok(())
}
+ async fn measure_moisture_hz(&mut self) -> FatResult {
+ self.can_power.set_high();
+ let config = self.twai_config.take().expect("twai config not set");
+ let mut twai = config.into_async().start();
- async fn measure_moisture_hz(&mut self) -> Result {
- self.sensor.measure_moisture_hz().await
+ loop {
+ let rec = twai.receive();
+ match rec {
+ Ok(_) => {}
+ Err(err) => {
+ info!("Error receiving CAN message: {err:?}");
+ break;
+ }
+ }
+ }
+
+ Timer::after_millis(10).await;
+
+ let mut moistures = Moistures::default();
+ let _ = wait_for_can_measurements(&mut twai, &mut moistures)
+ .with_timeout(Duration::from_millis(2000))
+ .await;
+
+ self.can_power.set_low();
+
+ let config = twai.stop().into_blocking();
+ self.twai_config.replace(config);
+
+ Ok(moistures)
}
async fn general_fault(&mut self, enable: bool) {
@@ -450,6 +441,123 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
}
async fn detect_sensors(&mut self) -> FatResult {
- self.sensor.autodetect().await
+ // Power on CAN transceiver and start controller
+ self.can_power.set_high();
+ let config = self.twai_config.take().expect("twai config not set");
+ info!("convert can");
+ let mut as_async = config.into_async().start();
+ // Give CAN some time to stabilize
+ Timer::after_millis(10).await;
+
+ info!("Sending info messages now");
+ // Send a few test messages per potential sensor node
+ for plant in 0..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 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)
+ .with_timeout(Duration::from_millis(1000))
+ .await;
+ match resu {
+ Ok(_) => {
+ info!("Sent test message to plant {plant} sensor {sensor:?}");
+ }
+ Err(err) => {
+ info!(
+ "Error sending test message to plant {plant} sensor {sensor:?}: {err:?}"
+ );
+ }
+ }
+ } else {
+ info!("Error building CAN frame");
+ }
+ }
+ }
+
+ let mut moistures = Moistures::default();
+ let _ = wait_for_can_measurements(&mut as_async, &mut moistures)
+ .with_timeout(Duration::from_millis(1000))
+ .await;
+
+ let config = as_async.stop().into_blocking();
+ self.can_power.set_low();
+ self.twai_config.replace(config);
+
+ let result = moistures.into();
+
+ info!("Autodetection result: {result:?}");
+ Ok(result)
+ }
+}
+
+async fn wait_for_can_measurements(
+ as_async: &mut Twai<'_, Async>,
+ moistures: &mut Moistures,
+) -> FatResult<()> {
+ 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;
+ let data = can_frame.data();
+ if data.len() == 2 {
+ let frequency = u16::from_be_bytes([data[0], data[1]]);
+ match sensor {
+ SensorSlot::A => {
+ moistures.sensor_a_hz[plant] = frequency as f32;
+ }
+ SensorSlot::B => {
+ moistures.sensor_b_hz[plant] = frequency as f32;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ Id::Extended(ext) => {
+ warn!("Received extended ID: {ext:?}");
+ }
+ },
+ Err(err) => {
+ match err {
+ EspTwaiError::BusOff => {
+ bail!("Bus offline")
+ }
+ EspTwaiError::NonCompliantDlc(_) => {}
+ EspTwaiError::EmbeddedHAL(_) => {}
+ }
+ error!("Error receiving CAN message: {err:?}");
+ }
+ }
+ }
+}
+
+impl From 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
}
}
diff --git a/Software/MainBoard/rust/src/hal/v4_sensor.rs b/Software/MainBoard/rust/src/hal/v4_sensor.rs
deleted file mode 100644
index 2bc1ccd..0000000
--- a/Software/MainBoard/rust/src/hal/v4_sensor.rs
+++ /dev/null
@@ -1,334 +0,0 @@
-use crate::bail;
-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 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 embedded_can::{Frame, Id};
-use esp_hal::gpio::Output;
-use esp_hal::i2c::master::I2c;
-use esp_hal::pcnt::unit::Unit;
-use esp_hal::twai::{EspTwaiError, EspTwaiFrame, StandardId, Twai, TwaiConfiguration};
-use esp_hal::{Async, Blocking};
-use log::{error, info, warn};
-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;
-}
-
-const MS0: u8 = 1_u8;
-const MS1: u8 = 0_u8;
-const MS2: u8 = 3_u8;
-const MS3: u8 = 4_u8;
-const MS4: u8 = 2_u8;
-const SENSOR_ON: u8 = 5_u8;
-
-pub enum SensorImpl {
- PulseCounter {
- signal_counter: Unit<'static, 0>,
- sensor_expander:
- Pca9535Immediate>>,
- },
- CanBus {
- twai_config: Option>,
- can_power: Output<'static>,
- },
-}
-
-#[async_trait(?Send)]
-impl SensorInteraction for SensorImpl {
- async fn measure_moisture_hz(&mut self) -> FatResult {
- match self {
- SensorImpl::PulseCounter {
- signal_counter,
- sensor_expander,
- ..
- } => {
- 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]
- );
- }
- Ok(result)
- }
-
- SensorImpl::CanBus {
- twai_config,
- can_power,
- } => {
- can_power.set_high();
- let config = twai_config.take().expect("twai config not set");
- let mut twai = config.into_async().start();
-
- loop {
- let rec = twai.receive();
- match rec {
- Ok(_) => {}
- Err(err) => {
- info!("Error receiving CAN message: {err:?}");
- break;
- }
- }
- }
-
- Timer::after_millis(10).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().into_blocking();
- twai_config.replace(config);
-
- Ok(moistures)
- }
- }
- }
-}
-
-impl SensorImpl {
- pub async fn autodetect(&mut self) -> FatResult {
- match self {
- SensorImpl::PulseCounter { .. } => {
- bail!("Only CAN bus implementation supports autodetection")
- }
- SensorImpl::CanBus {
- twai_config,
- can_power,
- } => {
- // Power on CAN transceiver and start controller
- can_power.set_high();
- let config = twai_config.take().expect("twai config not set");
- info!("convert can");
- let mut as_async = config.into_async().start();
- // Give CAN some time to stabilize
- Timer::after_millis(10).await;
-
- info!("Sending info messages now");
- // 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 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).with_timeout(Duration::from_millis(1000)).await;
- match resu {
- Ok(_) => {
- info!("Sent test message to plant {plant} sensor {sensor:?}");
- }
- Err(err) => {
- info!(
- "Error sending test message to plant {plant} sensor {sensor:?}: {err:?}"
- );
- }
- }
- } else {
- info!("Error building CAN frame");
- }
- }
- }
-
- let mut moistures = Moistures::default();
- let _ = Self::wait_for_can_measurements(&mut as_async, &mut moistures)
- .with_timeout(Duration::from_millis(1000))
- .await;
-
- let config = as_async.stop().into_blocking();
- can_power.set_low();
- twai_config.replace(config);
-
- let result = moistures.into();
-
- info!("Autodetection result: {result:?}");
- Ok(result)
- }
- }
- }
-
- async fn wait_for_can_measurements(
- as_async: &mut Twai<'_, Async>,
- moistures: &mut Moistures,
- ) -> FatResult<()> {
- 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;
- let data = can_frame.data();
- if data.len() == 2 {
- let frequency = u16::from_be_bytes([data[0], data[1]]);
- match sensor {
- SensorSlot::A => {
- moistures.sensor_a_hz[plant] = frequency as f32;
- }
- SensorSlot::B => {
- moistures.sensor_b_hz[plant] = frequency as f32;
- }
- }
- }
-
- }
- }
- }
- }
- Id::Extended(ext) => {
- warn!("Received extended ID: {ext:?}");
- }
- },
- Err(err) => {
- match err {
- EspTwaiError::BusOff => {
- bail!("Bus offline")
- }
- EspTwaiError::NonCompliantDlc(_) => {}
- EspTwaiError::EmbeddedHAL(_) => {}
- }
- error!("Error receiving CAN message: {err:?}");
- }
- }
- }
- }
-
- 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 {
- let mut results = [0_f32; REPEAT_MOIST_MEASURE];
- for sample in results.iter_mut() {
- signal_counter.pause();
- signal_counter.clear();
-
- //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 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)
- }
-}
-
-impl From 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
- }
-}
diff --git a/Software/MainBoard/rust/src/log/mod.rs b/Software/MainBoard/rust/src/log/mod.rs
index 881fb4b..a340ace 100644
--- a/Software/MainBoard/rust/src/log/mod.rs
+++ b/Software/MainBoard/rust/src/log/mod.rs
@@ -27,7 +27,7 @@ static mut LOG_ARRAY: LogArray = LogArray {
head: 0,
};
-// this is the only reference that is created for LOG_ARRAY and the only way to access it
+// this is the only reference created for LOG_ARRAY and the only way to access it
#[allow(static_mut_refs)]
pub static LOG_ACCESS: Mutex =
unsafe { Mutex::new(&mut LOG_ARRAY) };
@@ -298,7 +298,7 @@ impl From<&LogMessage> for MessageTranslation {
}
impl LogMessage {
- pub fn to_log_localisation_config() -> Vec {
+ pub fn log_localisation_config() -> Vec {
Vec::from_iter((0..LogMessage::len()).map(|i| {
let msg_type = LogMessage::from_ordinal(i).unwrap();
(&msg_type).into()
diff --git a/Software/MainBoard/rust/src/main.rs b/Software/MainBoard/rust/src/main.rs
index f558886..19c311a 100644
--- a/Software/MainBoard/rust/src/main.rs
+++ b/Software/MainBoard/rust/src/main.rs
@@ -38,7 +38,7 @@ use embassy_net::Stack;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::mutex::{Mutex, MutexGuard};
use embassy_sync::once_lock::OnceLock;
-use embassy_time::Timer;
+use embassy_time::{Duration, Timer, WithTimeout};
use esp_hal::rom::ets_delay_us;
use esp_hal::system::software_reset;
use esp_println::{logger, println};
@@ -1063,7 +1063,13 @@ async fn main(spawner: Spawner) -> ! {
// intialize embassy
logger::init_logger_from_env();
//force init here!
- match BOARD_ACCESS.init(PlantHal::create().await.unwrap()) {
+ match BOARD_ACCESS.init(
+ PlantHal::create()
+ .with_timeout(Duration::from_secs(10))
+ .await
+ .unwrap()
+ .unwrap(),
+ ) {
Ok(_) => {}
Err(_) => {
panic!("Could not set hal to static")
diff --git a/Software/MainBoard/rust/src/webserver/get_json.rs b/Software/MainBoard/rust/src/webserver/get_json.rs
index 553e9e4..186449f 100644
--- a/Software/MainBoard/rust/src/webserver/get_json.rs
+++ b/Software/MainBoard/rust/src/webserver/get_json.rs
@@ -175,6 +175,6 @@ pub(crate) async fn get_log_localization_config(
_request: &mut Connection<'_, T, N>,
) -> FatResult