update: refactor and enhance CAN sensor initialization, reorganize GPIO assignments, improve error detection and logging, and streamline TWAI handling

This commit is contained in:
2026-01-30 22:01:37 +01:00
parent 0c0b62e2ed
commit 355388aa62
3 changed files with 234 additions and 167 deletions

View File

@@ -130,15 +130,14 @@ pub struct V4<'a> {
pump_ina: Option<
SyncIna219<I2cDevice<'a, CriticalSectionRawMutex, I2c<'static, Blocking>>, UnCalibrated>,
>,
twai_peripheral: Option<esp_hal::peripherals::TWAI0<'static>>,
twai_rx_pin: Option<esp_hal::peripherals::GPIO2<'static>>,
twai_tx_pin: Option<esp_hal::peripherals::GPIO0<'static>>,
can_power: Output<'static>,
extra1: Output<'a>,
extra2: Output<'a>,
can_mutex: embassy_sync::mutex::Mutex<CriticalSectionRawMutex, ()>,
}
pub(crate) async fn create_v4(
peripherals: FreePeripherals<'static>,
esp: Esp<'static>,
@@ -257,35 +256,41 @@ pub(crate) async fn create_v4(
config,
battery_monitor,
pump_ina,
twai_peripheral,
twai_rx_pin,
twai_tx_pin,
charger,
extra1,
extra2,
can_power,
can_mutex: embassy_sync::mutex::Mutex::new(()),
};
Ok(Box::new(v))
}
impl<'a> V4<'a> {
fn teardown_twai(&mut self, old: TwaiConfiguration<Blocking>) {
drop(old);
// Re-acquire the peripheral and pins
let twai = unsafe { peripherals::TWAI0::steal() };
let rx_pin = unsafe { peripherals::GPIO2::steal() };
let tx_pin = unsafe { peripherals::GPIO0::steal() };
fn teardown_twai(old: Twai<Async>) {
let config = old.stop();
drop(config);
// Re-acquire the peripheral and pins
let rx_pin = unsafe { peripherals::GPIO2::steal() };
let tx_pin = unsafe { peripherals::GPIO0::steal() };
// Set pins to low to avoid parasitic powering
let mut rx = Input::new(rx_pin, InputConfig::default().with_pull(Pull::None));
let mut tx = Input::new(tx_pin, InputConfig::default().with_pull(Pull::None));
// Set pins to low to avoid parasitic powering
let _ = Input::new(rx_pin, InputConfig::default().with_pull(Pull::None));
let _ = Input::new(tx_pin, InputConfig::default().with_pull(Pull::None));
}
// Release the pins from Output back to raw pins and store everything
self.twai_peripheral = Some(twai);
self.twai_rx_pin = Some(unsafe { peripherals::GPIO2::steal() });
self.twai_tx_pin = Some(unsafe { peripherals::GPIO0::steal() });
self.can_power.set_low();
}
fn create_twai<'a>() -> Twai<'a, Async> {
// Release the pins from Output back to raw pins and store everything
let twai = unsafe { peripherals::TWAI0::steal() };
let twai_rx_pin = unsafe { peripherals::GPIO2::steal() };
let twai_tx_pin = unsafe { peripherals::GPIO0::steal() };
let twai_config = TwaiConfiguration::new(
twai,
twai_rx_pin,
twai_tx_pin,
TWAI_BAUDRATE,
TwaiMode::Normal,
);
twai_config.into_async().start()
}
#[async_trait(?Send)]
@@ -379,14 +384,8 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
}
async fn measure_moisture_hz(&mut self) -> FatResult<Moistures> {
self.can_power.set_high();
let twai_config = TwaiConfiguration::new(
self.twai_peripheral.take().unwrap(),
self.twai_rx_pin.take().unwrap(),
self.twai_tx_pin.take().unwrap(),
TWAI_BAUDRATE,
TwaiMode::Normal,
);
let mut twai = twai_config.into_async().start();
let mut twai = create_twai();
loop {
let rec = twai.receive();
@@ -405,11 +404,64 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
let _ = wait_for_can_measurements(&mut twai, &mut moistures)
.with_timeout(Duration::from_millis(2000))
.await;
self.teardown_twai(twai.stop().into_blocking());
teardown_twai(twai);
self.can_power.set_low();
Ok(moistures)
}
async fn detect_sensors(&mut self) -> FatResult<DetectionResult> {
self.can_power.set_high();
let mut twai = create_twai();
// 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 = twai
.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 twai, &mut moistures)
.with_timeout(Duration::from_millis(1000))
.await;
teardown_twai(twai);
self.can_power.set_low();
let result = moistures.into();
info!("Autodetection result: {result:?}");
Ok(result)
}
async fn general_fault(&mut self, enable: bool) {
hold_disable(23);
self.general_fault.set_level(enable.into());
@@ -470,69 +522,9 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
async fn get_mptt_current(&mut self) -> FatResult<Current> {
self.charger.get_mppt_current()
}
async fn detect_sensors(&mut self) -> FatResult<DetectionResult> {
// Power on CAN transceiver and start controller
self.can_power.set_high();
let twai_config = TwaiConfiguration::new(
self.twai_peripheral.take().unwrap(),
self.twai_rx_pin.take().unwrap(),
self.twai_tx_pin.take().unwrap(),
TWAI_BAUDRATE,
TwaiMode::Normal,
);
info!("convert can");
let mut as_async = twai_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.teardown_twai(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,