update: refactor and enhance CAN sensor initialization, reorganize GPIO assignments, improve error detection and logging, and streamline TWAI handling
This commit is contained in:
@@ -19,12 +19,12 @@ use embassy_sync::channel::Channel;
|
|||||||
use embassy_time::{Delay, Duration, Instant, Timer};
|
use embassy_time::{Delay, Duration, Instant, Timer};
|
||||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||||
use embassy_usb::{Builder, UsbDevice};
|
use embassy_usb::{Builder, UsbDevice};
|
||||||
|
use embedded_alloc::LlffHeap as Heap;
|
||||||
|
use embedded_can::nb::Can as nb_can;
|
||||||
use embedded_can::{Id, StandardId};
|
use embedded_can::{Id, StandardId};
|
||||||
use hal::bind_interrupts;
|
use hal::bind_interrupts;
|
||||||
use hal::usbd::Driver;
|
use hal::usbd::Driver;
|
||||||
use {ch32_hal as hal, panic_halt as _};
|
use {ch32_hal as hal, panic_halt as _};
|
||||||
use embedded_alloc::LlffHeap as Heap;
|
|
||||||
use embedded_can::nb::Can as nb_can;
|
|
||||||
|
|
||||||
macro_rules! mk_static {
|
macro_rules! mk_static {
|
||||||
($t:ty,$val:expr) => {{
|
($t:ty,$val:expr) => {{
|
||||||
@@ -60,49 +60,74 @@ async fn main(spawner: Spawner) {
|
|||||||
// Build driver and USB stack using 'static buffers
|
// Build driver and USB stack using 'static buffers
|
||||||
let driver = Driver::new(p.USBD, Irqs, p.PA12, p.PA11);
|
let driver = Driver::new(p.USBD, Irqs, p.PA12, p.PA11);
|
||||||
|
|
||||||
|
let mut probe_gnd = Flex::new(p.PA2);
|
||||||
|
|
||||||
let mut probe_gnd = Flex::new(p.PB1);
|
|
||||||
probe_gnd.set_as_input(Pull::None);
|
probe_gnd.set_as_input(Pull::None);
|
||||||
|
|
||||||
|
|
||||||
// Create GPIO for 555 Q output (PB0)
|
// Create GPIO for 555 Q output (PB0)
|
||||||
let q_out = Output::new(p.PB0, Level::Low, Speed::Low);
|
let q_out = Output::new(p.PA0, Level::Low, Speed::Low);
|
||||||
// Built-in LED on PB2 mirrors Q state
|
let info = Output::new(p.PA10, Level::Low, Speed::Low);
|
||||||
let mut info = Output::new(p.PB2, Level::Low, Speed::Low);
|
let mut warn = mk_static!(Output, Output::new(p.PA9, Level::Low, Speed::Low));
|
||||||
|
|
||||||
// Read configuration switches on PB3..PB7 at startup with floating detection
|
// Read configuration switches on PB3..PB7 at startup with floating detection
|
||||||
// PB3: Sensor A/B selector (Low=A, High=B)
|
// PB3: Sensor A/B selector (Low=A, High=B)
|
||||||
// PB4..PB7: address bits (1,2,4,8)
|
// PB4..PB7: address bits (1,2,4,8)
|
||||||
let mut pb3 = Flex::new(p.PB3);
|
let mut sensor_ab_pin = Flex::new(p.PA3);
|
||||||
let mut pb4 = Flex::new(p.PB4);
|
let mut sensor_address_bit_1_pin = Flex::new(p.PA4);
|
||||||
let mut pb5 = Flex::new(p.PB5);
|
let mut sensor_address_bit_2_pin = Flex::new(p.PA5);
|
||||||
let mut pb6 = Flex::new(p.PB6);
|
let mut sensor_address_bit_3_pin = Flex::new(p.PA6);
|
||||||
let mut pb7 = Flex::new(p.PB7);
|
let mut sensor_address_bit_4_pin = Flex::new(p.PA7);
|
||||||
|
|
||||||
// Validate all config pins; if any is floating, stay in an error loop until fixed
|
// Validate all config pins; if any is floating, stay in an error loop until fixed
|
||||||
// Try read PB3..PB7
|
// Try read PB3..PB7
|
||||||
let res_pb3 = detect_stable_pin(&mut pb3).await;
|
let sensor_ab_config = detect_stable_pin(&mut sensor_ab_pin).await;
|
||||||
let res_pb4 = detect_stable_pin(&mut pb4).await;
|
let sensor_address_bit_1_config = detect_stable_pin(&mut sensor_address_bit_1_pin).await;
|
||||||
let res_pb5 = detect_stable_pin(&mut pb5).await;
|
let sensor_address_bit_2_config = detect_stable_pin(&mut sensor_address_bit_2_pin).await;
|
||||||
let res_pb6 = detect_stable_pin(&mut pb6).await;
|
let sensor_address_bit_3_config = detect_stable_pin(&mut sensor_address_bit_3_pin).await;
|
||||||
let res_pb7 = detect_stable_pin(&mut pb7).await;
|
let sensor_address_bit_4_config = detect_stable_pin(&mut sensor_address_bit_4_pin).await;
|
||||||
|
|
||||||
let slot = if res_pb3.unwrap_or(false) { SensorSlot::B } else { SensorSlot::A };
|
let slot = if sensor_ab_config.unwrap_or(false) {
|
||||||
|
SensorSlot::B
|
||||||
|
} else {
|
||||||
|
SensorSlot::A
|
||||||
|
};
|
||||||
let mut addr: u8 = 0;
|
let mut addr: u8 = 0;
|
||||||
if res_pb4.unwrap_or(false) { addr |= 1; }
|
if sensor_address_bit_1_config.unwrap_or(false) {
|
||||||
if res_pb5.unwrap_or(false) { addr |= 2; }
|
addr |= 1;
|
||||||
if res_pb6.unwrap_or(false) { addr |= 4; }
|
}
|
||||||
if res_pb7.unwrap_or(false) { addr |= 8; }
|
if sensor_address_bit_2_config.unwrap_or(false) {
|
||||||
|
addr |= 2;
|
||||||
|
}
|
||||||
|
if sensor_address_bit_3_config.unwrap_or(false) {
|
||||||
|
addr |= 4;
|
||||||
|
}
|
||||||
|
if sensor_address_bit_4_config.unwrap_or(false) {
|
||||||
|
addr |= 8;
|
||||||
|
}
|
||||||
let moisture_id = plant_id(MOISTURE_DATA_OFFSET, slot, addr as u16);
|
let moisture_id = plant_id(MOISTURE_DATA_OFFSET, slot, addr as u16);
|
||||||
let identify_id = plant_id(IDENTIFY_CMD_OFFSET, slot, addr as u16);
|
let identify_id = plant_id(IDENTIFY_CMD_OFFSET, slot, addr as u16);
|
||||||
|
|
||||||
let invalid_config = res_pb3.is_none() || res_pb4.is_none() || res_pb5.is_none() || res_pb6.is_none() || res_pb7.is_none();
|
let invalid_config = sensor_ab_config.is_none()
|
||||||
|
|| sensor_address_bit_1_config.is_none()
|
||||||
|
|| sensor_address_bit_2_config.is_none()
|
||||||
|
|| sensor_address_bit_3_config.is_none()
|
||||||
|
|| sensor_address_bit_4_config.is_none();
|
||||||
|
|
||||||
let mut config = embassy_usb::Config::new(0xC0DE, 0xCAFE);
|
let mut config = embassy_usb::Config::new(0xC0DE, 0xCAFE);
|
||||||
config.manufacturer = Some("Can Sensor v0.2");
|
config.manufacturer = Some("Can Sensor v0.2");
|
||||||
let msg = mk_static!(heapless::String<128>, heapless::String::new());;
|
let msg = mk_static!(heapless::String<128>, heapless::String::new());
|
||||||
|
if invalid_config {
|
||||||
|
let _ = core::fmt::Write::write_fmt(
|
||||||
|
msg,
|
||||||
|
format_args!(
|
||||||
|
"CFG err: {:?} {:?} {:?} {:?} {:?}",
|
||||||
|
to_info(sensor_ab_config), to_info(sensor_address_bit_1_config), to_info(sensor_address_bit_2_config), to_info(sensor_address_bit_3_config), to_info(sensor_address_bit_4_config)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
let _ = core::fmt::Write::write_fmt(msg, format_args!("Sensor {:?} plant {}", slot, addr));
|
let _ = core::fmt::Write::write_fmt(msg, format_args!("Sensor {:?} plant {}", slot, addr));
|
||||||
|
}
|
||||||
|
|
||||||
config.product = Some(msg.as_str());
|
config.product = Some(msg.as_str());
|
||||||
config.serial_number = Some("12345678");
|
config.serial_number = Some("12345678");
|
||||||
config.max_power = 100;
|
config.max_power = 100;
|
||||||
@@ -131,28 +156,55 @@ async fn main(spawner: Spawner) {
|
|||||||
// Build USB device
|
// Build USB device
|
||||||
let usb = mk_static!(UsbDevice<Driver<USBD>>, builder.build());
|
let usb = mk_static!(UsbDevice<Driver<USBD>>, builder.build());
|
||||||
|
|
||||||
|
spawner.spawn(usb_task(usb)).unwrap();
|
||||||
|
spawner.spawn(usb_writer(class)).unwrap();
|
||||||
|
|
||||||
if invalid_config {
|
if invalid_config {
|
||||||
// At least one floating: report and blink code for the first one found.
|
// At least one floating: report and blink code for the first one found.
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
let mut msg: heapless::String<128> = heapless::String::new();
|
||||||
let code = if res_pb4.is_none() { 1 } else if res_pb5.is_none() { 2 } else if res_pb6.is_none() { 3 } else if res_pb7.is_none() { 4 } else { 5 }; // PB3 -> 5
|
let code = if sensor_address_bit_1_config.is_none() {
|
||||||
let which = match code { 1 => "PB4", 2 => "PB5", 3 => "PB6", 4 => "PB7", _ => "PB3 (A/B)" };
|
1
|
||||||
let _ = core::fmt::Write::write_fmt(&mut msg, format_args!("Config pin floating detected on {} -> blinking code {}. Fix jumpers.\r\n", which, code));
|
} else if sensor_address_bit_2_config.is_none() {
|
||||||
log(msg);
|
2
|
||||||
blink_error(&mut info, code).await;
|
} else if sensor_address_bit_3_config.is_none() {
|
||||||
|
3
|
||||||
|
} else if sensor_address_bit_4_config.is_none() {
|
||||||
|
4
|
||||||
|
} else {
|
||||||
|
5
|
||||||
|
}; // PB3 -> 5
|
||||||
|
let which = match code {
|
||||||
|
1 => "PB4",
|
||||||
|
2 => "PB5",
|
||||||
|
3 => "PB6",
|
||||||
|
4 => "PB7",
|
||||||
|
_ => "PB3 (A/B)",
|
||||||
};
|
};
|
||||||
|
let _ = core::fmt::Write::write_fmt(
|
||||||
|
&mut msg,
|
||||||
|
format_args!(
|
||||||
|
"Config pin floating detected on {} -> blinking code {}. Fix jumpers.\r\n",
|
||||||
|
which, code
|
||||||
|
),
|
||||||
|
);
|
||||||
|
log(msg);
|
||||||
|
spawner.spawn(blink_error(warn, code)).unwrap();
|
||||||
|
} else {
|
||||||
// Log startup configuration and derived CAN IDs
|
// Log startup configuration and derived CAN IDs
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
let mut msg: heapless::String<128> = heapless::String::new();
|
||||||
let slot_chr = match slot { SensorSlot::A => 'a', SensorSlot::B => 'b' };
|
let slot_chr = match slot {
|
||||||
let _ = core::fmt::Write::write_fmt(&mut msg, format_args!(
|
SensorSlot::A => 'a',
|
||||||
|
SensorSlot::B => 'b',
|
||||||
|
};
|
||||||
|
let _ = core::fmt::Write::write_fmt(
|
||||||
|
&mut msg,
|
||||||
|
format_args!(
|
||||||
"Startup: slot={} addr={} moisture_id=0x{:03X} identity_id=0x{:03X}\r\n",
|
"Startup: slot={} addr={} moisture_id=0x{:03X} identity_id=0x{:03X}\r\n",
|
||||||
slot_chr, addr, moisture_id, identify_id
|
slot_chr, addr, moisture_id, identify_id
|
||||||
));
|
),
|
||||||
|
);
|
||||||
log(msg);
|
log(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,15 +224,32 @@ async fn main(spawner: Spawner) {
|
|||||||
.expect("Valid");
|
.expect("Valid");
|
||||||
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
||||||
|
|
||||||
spawner.spawn(usb_task(usb)).unwrap();
|
|
||||||
spawner.spawn(usb_writer(class)).unwrap();
|
|
||||||
// move Q output, LED, ADC and analog input into worker task
|
// move Q output, LED, ADC and analog input into worker task
|
||||||
spawner.spawn(worker(probe_gnd, q_out, info, adc, ain, can, StandardId::new(moisture_id).unwrap(), StandardId::new(identify_id).unwrap())).unwrap();
|
spawner
|
||||||
|
.spawn(worker(
|
||||||
|
probe_gnd,
|
||||||
|
q_out,
|
||||||
|
info,
|
||||||
|
warn,
|
||||||
|
adc,
|
||||||
|
ain,
|
||||||
|
can,
|
||||||
|
StandardId::new(moisture_id).unwrap(),
|
||||||
|
StandardId::new(identify_id).unwrap(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
// Prevent main from exiting
|
// Prevent main from exiting
|
||||||
core::future::pending::<()>().await;
|
core::future::pending::<()>().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_info(res: Option<bool>) -> i8 {
|
||||||
|
match res {
|
||||||
|
Some(true) => 1,
|
||||||
|
Some(false) => -1,
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper closure: detect stable pin by comparing readings under Pull::Down and Pull::Up
|
// Helper closure: detect stable pin by comparing readings under Pull::Down and Pull::Up
|
||||||
async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option<bool> {
|
async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option<bool> {
|
||||||
@@ -190,9 +259,14 @@ async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option<bool> {
|
|||||||
pin.set_as_input(Pull::Up);
|
pin.set_as_input(Pull::Up);
|
||||||
Timer::after_millis(2).await;
|
Timer::after_millis(2).await;
|
||||||
let high_read = pin.is_high();
|
let high_read = pin.is_high();
|
||||||
if low_read == high_read { Some(high_read) } else { None }
|
if low_read == high_read {
|
||||||
|
Some(high_read)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
async fn blink_error(mut info_led: &mut Output<'static>, code: u8) -> !{
|
}
|
||||||
|
#[task]
|
||||||
|
async fn blink_error(info_led: &'static mut Output<'static>, code: u8) -> ! {
|
||||||
loop {
|
loop {
|
||||||
// code: 1-4 for PB4..PB7, 5 for PB3 (A/B)
|
// code: 1-4 for PB4..PB7, 5 for PB3 (A/B)
|
||||||
for _ in 0..code {
|
for _ in 0..code {
|
||||||
@@ -206,17 +280,17 @@ async fn blink_error(mut info_led: &mut Output<'static>, code: u8) -> !{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
async fn worker(
|
async fn worker(
|
||||||
mut probe_gnd: Flex<'static>,
|
mut probe_gnd: Flex<'static>,
|
||||||
mut q: Output<'static>,
|
mut q: Output<'static>,
|
||||||
mut info: Output<'static>,
|
mut info: Output<'static>,
|
||||||
|
mut warn: &'static mut Output<'static>,
|
||||||
mut adc: Adc<'static, hal::peripherals::ADC1>,
|
mut adc: Adc<'static, hal::peripherals::ADC1>,
|
||||||
mut ain: hal::peripherals::PA1,
|
mut ain: hal::peripherals::PA1,
|
||||||
mut can: Can<'static, CAN1, NonBlocking>,
|
mut can: Can<'static, CAN1, NonBlocking>,
|
||||||
moisture_id: StandardId,
|
moisture_id: StandardId,
|
||||||
identify_id: StandardId
|
identify_id: StandardId,
|
||||||
) {
|
) {
|
||||||
// 555 emulation state: Q initially Low
|
// 555 emulation state: Q initially Low
|
||||||
let mut q_high = false;
|
let mut q_high = false;
|
||||||
@@ -284,8 +358,10 @@ async fn worker(
|
|||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
let mut msg: heapless::String<128> = heapless::String::new();
|
||||||
let _ = write!(
|
let _ = write!(
|
||||||
&mut msg,
|
&mut msg,
|
||||||
"555 window={}ms pulses={} freq={} Hz (A1->Q on PB0)\r\n", probe_duration.as_millis(),
|
"555 window={}ms pulses={} freq={} Hz (A1->Q on PB0)\r\n",
|
||||||
pulses, freq_hz
|
probe_duration.as_millis(),
|
||||||
|
pulses,
|
||||||
|
freq_hz
|
||||||
);
|
);
|
||||||
log(msg);
|
log(msg);
|
||||||
|
|
||||||
@@ -298,9 +374,9 @@ async fn worker(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
info.set_high();
|
warn.set_high();
|
||||||
Timer::after_millis(100).await;
|
Timer::after_millis(100).await;
|
||||||
info.set_low();
|
warn.set_low();
|
||||||
Timer::after_millis(100).await;
|
Timer::after_millis(100).await;
|
||||||
}
|
}
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
let mut msg: heapless::String<128> = heapless::String::new();
|
||||||
@@ -318,7 +394,6 @@ async fn worker(
|
|||||||
);
|
);
|
||||||
log(msg);
|
log(msg);
|
||||||
|
|
||||||
|
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
match can.receive() {
|
match can.receive() {
|
||||||
Ok(frame) => match frame.id() {
|
Ok(frame) => match frame.id() {
|
||||||
|
|||||||
@@ -130,15 +130,14 @@ pub struct V4<'a> {
|
|||||||
pump_ina: Option<
|
pump_ina: Option<
|
||||||
SyncIna219<I2cDevice<'a, CriticalSectionRawMutex, I2c<'static, Blocking>>, UnCalibrated>,
|
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>,
|
can_power: Output<'static>,
|
||||||
|
|
||||||
extra1: Output<'a>,
|
extra1: Output<'a>,
|
||||||
extra2: Output<'a>,
|
extra2: Output<'a>,
|
||||||
|
can_mutex: embassy_sync::mutex::Mutex<CriticalSectionRawMutex, ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) async fn create_v4(
|
pub(crate) async fn create_v4(
|
||||||
peripherals: FreePeripherals<'static>,
|
peripherals: FreePeripherals<'static>,
|
||||||
esp: Esp<'static>,
|
esp: Esp<'static>,
|
||||||
@@ -257,35 +256,41 @@ pub(crate) async fn create_v4(
|
|||||||
config,
|
config,
|
||||||
battery_monitor,
|
battery_monitor,
|
||||||
pump_ina,
|
pump_ina,
|
||||||
twai_peripheral,
|
|
||||||
twai_rx_pin,
|
|
||||||
twai_tx_pin,
|
|
||||||
charger,
|
charger,
|
||||||
extra1,
|
extra1,
|
||||||
extra2,
|
extra2,
|
||||||
can_power,
|
can_power,
|
||||||
|
can_mutex: embassy_sync::mutex::Mutex::new(()),
|
||||||
};
|
};
|
||||||
Ok(Box::new(v))
|
Ok(Box::new(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> V4<'a> {
|
fn teardown_twai(old: Twai<Async>) {
|
||||||
fn teardown_twai(&mut self, old: TwaiConfiguration<Blocking>) {
|
let config = old.stop();
|
||||||
drop(old);
|
drop(config);
|
||||||
// Re-acquire the peripheral and pins
|
// Re-acquire the peripheral and pins
|
||||||
let twai = unsafe { peripherals::TWAI0::steal() };
|
|
||||||
let rx_pin = unsafe { peripherals::GPIO2::steal() };
|
let rx_pin = unsafe { peripherals::GPIO2::steal() };
|
||||||
let tx_pin = unsafe { peripherals::GPIO0::steal() };
|
let tx_pin = unsafe { peripherals::GPIO0::steal() };
|
||||||
|
|
||||||
// Set pins to low to avoid parasitic powering
|
// Set pins to low to avoid parasitic powering
|
||||||
let mut rx = Input::new(rx_pin, InputConfig::default().with_pull(Pull::None));
|
let _ = Input::new(rx_pin, InputConfig::default().with_pull(Pull::None));
|
||||||
let mut tx = Input::new(tx_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)]
|
#[async_trait(?Send)]
|
||||||
@@ -379,14 +384,8 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
}
|
}
|
||||||
async fn measure_moisture_hz(&mut self) -> FatResult<Moistures> {
|
async fn measure_moisture_hz(&mut self) -> FatResult<Moistures> {
|
||||||
self.can_power.set_high();
|
self.can_power.set_high();
|
||||||
let twai_config = TwaiConfiguration::new(
|
let mut twai = create_twai();
|
||||||
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();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let rec = twai.receive();
|
let rec = twai.receive();
|
||||||
@@ -405,11 +404,64 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
let _ = wait_for_can_measurements(&mut twai, &mut moistures)
|
let _ = wait_for_can_measurements(&mut twai, &mut moistures)
|
||||||
.with_timeout(Duration::from_millis(2000))
|
.with_timeout(Duration::from_millis(2000))
|
||||||
.await;
|
.await;
|
||||||
self.teardown_twai(twai.stop().into_blocking());
|
teardown_twai(twai);
|
||||||
|
self.can_power.set_low();
|
||||||
Ok(moistures)
|
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) {
|
async fn general_fault(&mut self, enable: bool) {
|
||||||
hold_disable(23);
|
hold_disable(23);
|
||||||
self.general_fault.set_level(enable.into());
|
self.general_fault.set_level(enable.into());
|
||||||
@@ -470,68 +522,8 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
async fn get_mptt_current(&mut self) -> FatResult<Current> {
|
async fn get_mptt_current(&mut self) -> FatResult<Current> {
|
||||||
self.charger.get_mppt_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(
|
async fn wait_for_can_measurements(
|
||||||
as_async: &mut Twai<'_, Async>,
|
as_async: &mut Twai<'_, Async>,
|
||||||
|
|||||||
Submodule website/themes/blowfish updated: 26d1205439...f9eb1d4e81
Reference in New Issue
Block a user