Compare commits
7 Commits
0c0b62e2ed
...
3128e32fb2
| Author | SHA1 | Date | |
|---|---|---|---|
| 3128e32fb2 | |||
| 6bba9b1f27 | |||
| c909b33af0 | |||
| 979f982565 | |||
| e6f8e34f7d | |||
| ce10d084f8 | |||
| 355388aa62 |
204763
Hardware/Sensor/sensor.step
204763
Hardware/Sensor/sensor.step
File diff suppressed because it is too large
Load Diff
BIN
Hardware/Sensor_Case/case_body.3mf
Normal file
BIN
Hardware/Sensor_Case/case_body.3mf
Normal file
Binary file not shown.
BIN
Hardware/Sensor_Case/case_top.3mf
Normal file
BIN
Hardware/Sensor_Case/case_top.3mf
Normal file
Binary file not shown.
@@ -3,7 +3,7 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use crate::hal::peripherals::CAN1;
|
use crate::hal::peripherals::CAN1;
|
||||||
use canapi::id::{plant_id, MessageKind, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
|
use canapi::id::{plant_id, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
|
||||||
use canapi::SensorSlot;
|
use canapi::SensorSlot;
|
||||||
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
|
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
|
||||||
use ch32_hal::can;
|
use ch32_hal::can;
|
||||||
@@ -16,15 +16,15 @@ use embassy_executor::{task, Spawner};
|
|||||||
use embassy_futures::yield_now;
|
use embassy_futures::yield_now;
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::channel::Channel;
|
use embassy_sync::channel::Channel;
|
||||||
use embassy_time::{Delay, Duration, Instant, Timer};
|
use embassy_time::{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) => {{
|
||||||
@@ -42,6 +42,8 @@ bind_interrupts!(struct Irqs {
|
|||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static HEAP: Heap = Heap::empty();
|
static HEAP: Heap = Heap::empty();
|
||||||
static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new();
|
static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new();
|
||||||
|
static CAN_RX_CH: Channel<CriticalSectionRawMutex, CanFrame, 4> = Channel::new();
|
||||||
|
static CAN_TX_CH: Channel<CriticalSectionRawMutex, CanFrame, 4> = Channel::new();
|
||||||
|
|
||||||
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
@@ -60,49 +62,78 @@ 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 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 standard_identify_id = StandardId::new(identify_id).unwrap();
|
||||||
|
|
||||||
let invalid_config = res_pb3.is_none() || res_pb4.is_none() || res_pb5.is_none() || res_pb6.is_none() || res_pb7.is_none();
|
//is any floating, or invalid addr (only 1-8 are valid)
|
||||||
|
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()
|
||||||
|
|| addr == 0
|
||||||
|
|| addr > 8;
|
||||||
|
|
||||||
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 +162,68 @@ 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 if sensor_ab_config.is_none() {
|
||||||
|
5
|
||||||
|
} else {
|
||||||
|
6 // Invalid address (0 or > 8)
|
||||||
};
|
};
|
||||||
|
let which = match code {
|
||||||
|
1 => "PB4 (bit 1)",
|
||||||
|
2 => "PB5 (bit 2)",
|
||||||
|
3 => "PB6 (bit 3)",
|
||||||
|
4 => "PB7 (bit 4)",
|
||||||
|
5 => "PB3 (A/B)",
|
||||||
|
_ => "Address (0 or > 8)",
|
||||||
|
};
|
||||||
|
if code == 6 {
|
||||||
|
let _ = core::fmt::Write::write_fmt(
|
||||||
|
&mut msg,
|
||||||
|
format_args!(
|
||||||
|
"Invalid address {} (only 1-8 allowed) -> blinking code {}. Fix jumpers.\r\n",
|
||||||
|
addr, code
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
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 +243,36 @@ 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();
|
can.add_filter(CanFilter::accept_all());
|
||||||
spawner.spawn(usb_writer(class)).unwrap();
|
// let mut filter = CanFilter::new_id_list();
|
||||||
// move Q output, LED, ADC and analog input into worker task
|
// filter.get(0).unwrap().set(Id::Standard(standard_identify_id), Default::default());
|
||||||
spawner.spawn(worker(probe_gnd, q_out, info, adc, ain, can, StandardId::new(moisture_id).unwrap(), StandardId::new(identify_id).unwrap())).unwrap();
|
// can.add_filter(filter);
|
||||||
|
spawner.spawn(can_task(can, warn, standard_identify_id)).unwrap();
|
||||||
|
|
||||||
|
// move Q output, LED, ADC and analog input into worker task
|
||||||
|
spawner
|
||||||
|
.spawn(worker(
|
||||||
|
probe_gnd,
|
||||||
|
q_out,
|
||||||
|
info,
|
||||||
|
adc,
|
||||||
|
ain,
|
||||||
|
StandardId::new(moisture_id).unwrap(),
|
||||||
|
standard_identify_id,
|
||||||
|
))
|
||||||
|
.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 +282,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,6 +303,79 @@ async fn blink_error(mut info_led: &mut Output<'static>, code: u8) -> !{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
async fn can_task(
|
||||||
|
mut can: Can<'static, CAN1, NonBlocking>,
|
||||||
|
warn: &'static mut Output<'static>,
|
||||||
|
identify_id: StandardId,
|
||||||
|
) {
|
||||||
|
loop {
|
||||||
|
match can.receive() {
|
||||||
|
Ok(frame) => {
|
||||||
|
match frame.id() {
|
||||||
|
Id::Standard(s_frame) => {
|
||||||
|
let mut msg: heapless::String<128> = heapless::String::new();
|
||||||
|
let _ = write!(
|
||||||
|
&mut msg,
|
||||||
|
"Received from canbus: {:?} ident is {:?} \r\n",
|
||||||
|
s_frame.as_raw(),
|
||||||
|
identify_id.as_raw()
|
||||||
|
);
|
||||||
|
log(msg);
|
||||||
|
if s_frame.as_raw() == identify_id.as_raw() {
|
||||||
|
CAN_RX_CH.send(frame).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Id::Extended(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(nb::Error::WouldBlock) => {
|
||||||
|
// No frame available
|
||||||
|
}
|
||||||
|
Err(nb::Error::Other(err)) => {
|
||||||
|
for _ in 0..3 {
|
||||||
|
warn.set_high();
|
||||||
|
Timer::after_millis(100).await;
|
||||||
|
warn.set_low();
|
||||||
|
Timer::after_millis(100).await;
|
||||||
|
}
|
||||||
|
let mut msg: heapless::String<128> = heapless::String::new();
|
||||||
|
let _ = write!(&mut msg, "rx err {:?}", err);
|
||||||
|
log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Ok(mut frame) = CAN_TX_CH.try_receive() {
|
||||||
|
match can.transmit(&mut frame) {
|
||||||
|
Ok(..) => {
|
||||||
|
}
|
||||||
|
Err(nb::Error::WouldBlock) => {
|
||||||
|
for _ in 0..2 {
|
||||||
|
warn.set_high();
|
||||||
|
Timer::after_millis(100).await;
|
||||||
|
warn.set_low();
|
||||||
|
Timer::after_millis(100).await;
|
||||||
|
}
|
||||||
|
let mut msg: heapless::String<128> = heapless::String::new();
|
||||||
|
let _ = write!(&mut msg, "canbus out buffer full");
|
||||||
|
log(msg);
|
||||||
|
}
|
||||||
|
Err(nb::Error::Other(err)) => {
|
||||||
|
for _ in 0..3 {
|
||||||
|
warn.set_high();
|
||||||
|
Timer::after_millis(100).await;
|
||||||
|
warn.set_low();
|
||||||
|
Timer::after_millis(100).await;
|
||||||
|
}
|
||||||
|
let mut msg: heapless::String<128> = heapless::String::new();
|
||||||
|
let _ = write!(&mut msg, "tx err {:?}", err);
|
||||||
|
log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield_now().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
async fn worker(
|
async fn worker(
|
||||||
@@ -214,25 +384,14 @@ async fn worker(
|
|||||||
mut info: Output<'static>,
|
mut info: 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>,
|
|
||||||
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;
|
||||||
let low_th: u16 = (ADC_MAX as u16) / 3; // ~1/3 Vref
|
let low_th: u16 = (ADC_MAX as u16) / 3; // ~1/3 Vref
|
||||||
let high_th: u16 = ((ADC_MAX as u32 * 2) / 3) as u16; // ~2/3 Vref
|
let high_th: u16 = ((ADC_MAX as u32 * 2) / 3) as u16; // ~2/3 Vref
|
||||||
|
|
||||||
let mut filter = CanFilter::new_id_list();
|
|
||||||
|
|
||||||
filter
|
|
||||||
.get(0)
|
|
||||||
.unwrap()
|
|
||||||
.set(identify_id.into(), Default::default());
|
|
||||||
|
|
||||||
//can.add_filter(filter);
|
|
||||||
can.add_filter(CanFilter::accept_all());
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Count rising edges of Q in a 100 ms window
|
// Count rising edges of Q in a 100 ms window
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
@@ -241,7 +400,7 @@ async fn worker(
|
|||||||
|
|
||||||
probe_gnd.set_as_output(Speed::Low);
|
probe_gnd.set_as_output(Speed::Low);
|
||||||
probe_gnd.set_low();
|
probe_gnd.set_low();
|
||||||
let probe_duration = Duration::from_millis(1000);
|
let probe_duration = Duration::from_millis(100);
|
||||||
while Instant::now()
|
while Instant::now()
|
||||||
.checked_duration_since(start)
|
.checked_duration_since(start)
|
||||||
.unwrap_or(Duration::from_millis(0))
|
.unwrap_or(Duration::from_millis(0))
|
||||||
@@ -278,60 +437,23 @@ async fn worker(
|
|||||||
}
|
}
|
||||||
probe_gnd.set_as_input(Pull::None);
|
probe_gnd.set_as_input(Pull::None);
|
||||||
|
|
||||||
// Compute frequency from 100 ms window
|
let freq_hz: u32 = pulses * (1000 / probe_duration.as_millis()) as u32; // pulses per 0.1s => Hz
|
||||||
let freq_hz = pulses; // pulses per 0.1s => Hz
|
|
||||||
|
|
||||||
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) id={:?}\r\n",
|
||||||
pulses, freq_hz
|
probe_duration.as_millis(),
|
||||||
);
|
pulses,
|
||||||
log(msg);
|
freq_hz,
|
||||||
|
|
||||||
let mut moisture = CanFrame::new(moisture_id, &(freq_hz as u16).to_be_bytes()).unwrap();
|
|
||||||
match can.transmit(&mut moisture) {
|
|
||||||
Ok(..) => {
|
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
|
||||||
let _ = write!(&mut msg, "Send to canbus");
|
|
||||||
log(msg);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
for _ in 0..3 {
|
|
||||||
info.set_high();
|
|
||||||
Timer::after_millis(100).await;
|
|
||||||
info.set_low();
|
|
||||||
Timer::after_millis(100).await;
|
|
||||||
}
|
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
|
||||||
let _ = write!(&mut msg, "err {:?}", err);
|
|
||||||
log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
|
||||||
let _ = write!(
|
|
||||||
&mut msg,
|
|
||||||
"Check identity addr received: {:#x} \r\n",
|
|
||||||
identify_id.as_raw()
|
identify_id.as_raw()
|
||||||
);
|
);
|
||||||
log(msg);
|
log(msg);
|
||||||
|
|
||||||
|
let moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap();
|
||||||
|
CAN_TX_CH.send(moisture).await;
|
||||||
|
|
||||||
yield_now().await;
|
while let Ok(_frame) = CAN_RX_CH.try_receive() {
|
||||||
match can.receive() {
|
|
||||||
Ok(frame) => match frame.id() {
|
|
||||||
Id::Standard(s_frame) => {
|
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
|
||||||
let _ = write!(
|
|
||||||
&mut msg,
|
|
||||||
"Received from canbus: {:?} ident is {:?} \r\n",
|
|
||||||
s_frame.as_raw(),
|
|
||||||
identify_id.as_raw()
|
|
||||||
);
|
|
||||||
log(msg);
|
|
||||||
if s_frame.as_raw() == identify_id.as_raw() {
|
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
Timer::after_millis(250).await;
|
Timer::after_millis(250).await;
|
||||||
info.toggle();
|
info.toggle();
|
||||||
@@ -339,15 +461,6 @@ async fn worker(
|
|||||||
info.set_low();
|
info.set_low();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Id::Extended(_) => {}
|
|
||||||
},
|
|
||||||
|
|
||||||
Err(err) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(message: heapless::String<128>) {
|
fn log(message: heapless::String<128>) {
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ impl Esp<'_> {
|
|||||||
loop {
|
loop {
|
||||||
match self.uart0.read_buffered(&mut buf) {
|
match self.uart0.read_buffered(&mut buf) {
|
||||||
Ok(read) => {
|
Ok(read) => {
|
||||||
if (read == 0) {
|
if read == 0 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let c = buf[0] as char;
|
let c = buf[0] as char;
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
use crate::alloc::boxed::Box;
|
|
||||||
use crate::fat_error::{FatError, FatResult};
|
|
||||||
use crate::hal::esp::Esp;
|
|
||||||
use crate::hal::rtc::{BackupHeader, RTCModuleInteraction};
|
|
||||||
use crate::hal::water::TankSensor;
|
|
||||||
use crate::hal::{BoardInteraction, FreePeripherals, Moistures, TIME_ACCESS};
|
|
||||||
use crate::{
|
|
||||||
bail,
|
|
||||||
config::PlantControllerConfig,
|
|
||||||
hal::battery::{BatteryInteraction, NoBatteryMonitor},
|
|
||||||
};
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use esp_hal::gpio::{Level, Output, OutputConfig};
|
|
||||||
use measurements::{Current, Voltage};
|
|
||||||
|
|
||||||
pub struct Initial<'a> {
|
|
||||||
pub(crate) general_fault: Output<'a>,
|
|
||||||
pub(crate) esp: Esp<'a>,
|
|
||||||
pub(crate) config: PlantControllerConfig,
|
|
||||||
pub(crate) battery: Box<dyn BatteryInteraction + Send>,
|
|
||||||
pub rtc: Box<dyn RTCModuleInteraction + Send>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct NoRTC {}
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl RTCModuleInteraction for NoRTC {
|
|
||||||
async fn get_backup_info(&mut self) -> Result<BackupHeader, FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_backup_config(&mut self, _chunk: usize) -> FatResult<([u8; 32], usize, u16)> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn backup_config(&mut self, _offset: usize, _bytes: &[u8]) -> FatResult<()> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn backup_config_finalize(&mut self, _crc: u16, _length: usize) -> FatResult<()> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_rtc_time(&mut self) -> Result<DateTime<Utc>, FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_rtc_time(&mut self, _time: &DateTime<Utc>) -> Result<(), FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_initial_board(
|
|
||||||
free_pins: FreePeripherals<'static>,
|
|
||||||
config: PlantControllerConfig,
|
|
||||||
esp: Esp<'static>,
|
|
||||||
) -> Result<Box<dyn BoardInteraction<'static> + Send>, FatError> {
|
|
||||||
log::info!("Start initial");
|
|
||||||
let general_fault = Output::new(free_pins.gpio23, Level::Low, OutputConfig::default());
|
|
||||||
let v = Initial {
|
|
||||||
general_fault,
|
|
||||||
config,
|
|
||||||
esp,
|
|
||||||
battery: Box::new(NoBatteryMonitor {}),
|
|
||||||
rtc: Box::new(NoRTC {}),
|
|
||||||
};
|
|
||||||
Ok(Box::new(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl<'a> BoardInteraction<'a> for Initial<'a> {
|
|
||||||
fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
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<dyn BatteryInteraction + Send> {
|
|
||||||
&mut self.battery
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_rtc_module(&mut self) -> &mut Box<dyn RTCModuleInteraction + Send> {
|
|
||||||
&mut self.rtc
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_charge_indicator(&mut self, _charging: bool) -> Result<(), FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
|
|
||||||
let rtc = TIME_ACCESS.get().await.lock().await;
|
|
||||||
self.esp.deep_sleep(duration_in_ms, rtc);
|
|
||||||
}
|
|
||||||
fn is_day(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
async fn light(&mut self, _enable: bool) -> Result<(), FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn pump(&mut self, _plant: usize, _enable: bool) -> Result<(), FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn pump_current(&mut self, _plant: usize) -> Result<Current, FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fault(&mut self, _plant: usize, _enable: bool) -> Result<(), FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn measure_moisture_hz(&mut self) -> Result<Moistures, FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn general_fault(&mut self, enable: bool) {
|
|
||||||
self.general_fault.set_level(enable.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn test(&mut self) -> Result<(), FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_config(&mut self, config: PlantControllerConfig) {
|
|
||||||
self.config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_mptt_voltage(&mut self) -> Result<Voltage, FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_mptt_current(&mut self) -> Result<Current, FatError> {
|
|
||||||
bail!("Please configure board revision")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ use esp_hal::uart::{Config as UartConfig};
|
|||||||
pub(crate) mod battery;
|
pub(crate) mod battery;
|
||||||
// mod can_api; // replaced by external canapi crate
|
// mod can_api; // replaced by external canapi crate
|
||||||
pub mod esp;
|
pub mod esp;
|
||||||
mod initial_hal;
|
|
||||||
mod little_fs2storage_adapter;
|
mod little_fs2storage_adapter;
|
||||||
pub(crate) mod rtc;
|
pub(crate) mod rtc;
|
||||||
mod shared_flash;
|
mod shared_flash;
|
||||||
@@ -40,7 +39,7 @@ use esp_hal::peripherals::TWAI0;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bail,
|
bail,
|
||||||
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
|
config::{BatteryBoardVersion, PlantControllerConfig},
|
||||||
hal::{
|
hal::{
|
||||||
battery::{BatteryInteraction, NoBatteryMonitor},
|
battery::{BatteryInteraction, NoBatteryMonitor},
|
||||||
esp::Esp,
|
esp::Esp,
|
||||||
@@ -50,7 +49,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use alloc::string::String;
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bincode::{Decode, Encode};
|
use bincode::{Decode, Encode};
|
||||||
@@ -162,6 +160,7 @@ pub trait BoardInteraction<'a> {
|
|||||||
fn set_config(&mut self, config: PlantControllerConfig);
|
fn set_config(&mut self, config: PlantControllerConfig);
|
||||||
async fn get_mptt_voltage(&mut self) -> FatResult<Voltage>;
|
async fn get_mptt_voltage(&mut self) -> FatResult<Voltage>;
|
||||||
async fn get_mptt_current(&mut self) -> FatResult<Current>;
|
async fn get_mptt_current(&mut self) -> FatResult<Current>;
|
||||||
|
async fn can_power(&mut self, state: bool) -> FatResult<()>;
|
||||||
|
|
||||||
// Return JSON string with autodetected sensors per plant. Default: not supported.
|
// Return JSON string with autodetected sensors per plant. Default: not supported.
|
||||||
async fn detect_sensors(&mut self) -> FatResult<DetectionResult> {
|
async fn detect_sensors(&mut self) -> FatResult<DetectionResult> {
|
||||||
@@ -681,8 +680,8 @@ pub async fn esp_set_time(time: DateTime<FixedOffset>) -> FatResult<()> {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize)]
|
||||||
pub struct Moistures {
|
pub struct Moistures {
|
||||||
pub sensor_a_hz: [f32; PLANT_COUNT],
|
pub sensor_a_hz: [Option<f32>; PLANT_COUNT],
|
||||||
pub sensor_b_hz: [f32; PLANT_COUNT],
|
pub sensor_b_hz: [Option<f32>; PLANT_COUNT],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ use crate::hal::{
|
|||||||
};
|
};
|
||||||
use crate::log::{LogMessage, LOG_ACCESS};
|
use crate::log::{LogMessage, LOG_ACCESS};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{ToString};
|
||||||
use alloc::vec;
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET};
|
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET};
|
||||||
use canapi::SensorSlot;
|
use canapi::SensorSlot;
|
||||||
@@ -22,9 +21,9 @@ use embassy_time::{Duration, Timer, WithTimeout};
|
|||||||
use embedded_can::{Frame, Id};
|
use embedded_can::{Frame, Id};
|
||||||
use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull};
|
use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull};
|
||||||
use esp_hal::i2c::master::I2c;
|
use esp_hal::i2c::master::I2c;
|
||||||
use esp_hal::peripherals;
|
|
||||||
use esp_hal::twai::{EspTwaiError, EspTwaiFrame, StandardId, Twai, TwaiConfiguration, TwaiMode};
|
use esp_hal::twai::{EspTwaiError, EspTwaiFrame, StandardId, Twai, TwaiConfiguration, TwaiMode};
|
||||||
use esp_hal::{twai, Async, Blocking};
|
use esp_hal::{twai, Async, Blocking};
|
||||||
|
use esp_println::println;
|
||||||
use ina219::address::{Address, Pin};
|
use ina219::address::{Address, Pin};
|
||||||
use ina219::calibration::UnCalibrated;
|
use ina219::calibration::UnCalibrated;
|
||||||
use ina219::configuration::{Configuration, OperatingMode, Resolution};
|
use ina219::configuration::{Configuration, OperatingMode, Resolution};
|
||||||
@@ -130,15 +129,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>,
|
||||||
|
twai_config: Option<TwaiConfiguration<'static, Blocking>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) async fn create_v4(
|
pub(crate) async fn create_v4(
|
||||||
peripherals: FreePeripherals<'static>,
|
peripherals: FreePeripherals<'static>,
|
||||||
esp: Esp<'static>,
|
esp: Esp<'static>,
|
||||||
@@ -153,11 +151,13 @@ pub(crate) async fn create_v4(
|
|||||||
let mut general_fault = Output::new(peripherals.gpio23, Level::Low, OutputConfig::default());
|
let mut general_fault = Output::new(peripherals.gpio23, Level::Low, OutputConfig::default());
|
||||||
general_fault.set_low();
|
general_fault.set_low();
|
||||||
|
|
||||||
let twai_peripheral = Some(peripherals.twai);
|
let twai_config = Some(TwaiConfiguration::new(
|
||||||
|
peripherals.twai,
|
||||||
|
peripherals.gpio0,
|
||||||
let twai_rx_pin = Some(peripherals.gpio2);
|
peripherals.gpio2,
|
||||||
let twai_tx_pin = Some(peripherals.gpio0);
|
TWAI_BAUDRATE,
|
||||||
|
TwaiMode::Normal,
|
||||||
|
));
|
||||||
|
|
||||||
let extra1 = Output::new(peripherals.gpio6, Level::Low, OutputConfig::default());
|
let extra1 = Output::new(peripherals.gpio6, Level::Low, OutputConfig::default());
|
||||||
let extra2 = Output::new(peripherals.gpio15, Level::Low, OutputConfig::default());
|
let extra2 = Output::new(peripherals.gpio15, Level::Low, OutputConfig::default());
|
||||||
@@ -257,36 +257,15 @@ 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,
|
||||||
|
twai_config
|
||||||
};
|
};
|
||||||
Ok(Box::new(v))
|
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() };
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl<'a> BoardInteraction<'a> for V4<'a> {
|
impl<'a> BoardInteraction<'a> for V4<'a> {
|
||||||
@@ -379,37 +358,78 @@ 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 config = self.twai_config.take().expect("twai config not set");
|
||||||
self.twai_peripheral.take().unwrap(),
|
let mut twai = config.into_async().start();
|
||||||
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 {
|
|
||||||
let rec = twai.receive();
|
|
||||||
match rec {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => {
|
|
||||||
info!("Error receiving CAN message: {err:?}");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer::after_millis(10).await;
|
Timer::after_millis(10).await;
|
||||||
|
|
||||||
let mut moistures = Moistures::default();
|
let mut moistures = Moistures::default();
|
||||||
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(5000))
|
||||||
.await;
|
.await;
|
||||||
self.teardown_twai(twai.stop().into_blocking());
|
|
||||||
|
|
||||||
|
let config = twai.stop().into_blocking();
|
||||||
|
self.twai_config.replace(config);
|
||||||
|
|
||||||
|
self.can_power.set_low();
|
||||||
Ok(moistures)
|
Ok(moistures)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn detect_sensors(&mut self) -> FatResult<DetectionResult> {
|
||||||
|
self.can_power.set_high();
|
||||||
|
let config = self.twai_config.take().expect("twai config not set");
|
||||||
|
let mut twai = config.into_async().start();
|
||||||
|
|
||||||
|
Timer::after_millis(1000).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 +1) as u16))
|
||||||
|
.context(">> Could not create address for sensor! (plant: {}) <<")?;
|
||||||
|
let can_buffer = [0_u8; 0];
|
||||||
|
info!("Sending test message to plant {} sensor {sensor:?} with id {}", plant +1, target.as_raw());
|
||||||
|
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(3000))
|
||||||
|
.await;
|
||||||
|
match resu {
|
||||||
|
Ok(_) => {
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
info!(
|
||||||
|
"Error sending test message to plant {} sensor {sensor:?}: {err:?}", plant +1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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(3000))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
|
||||||
|
let config = twai.stop().into_blocking();
|
||||||
|
self.twai_config.replace(config);
|
||||||
|
|
||||||
|
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());
|
||||||
@@ -447,12 +467,12 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
}
|
}
|
||||||
let moisture = self.measure_moisture_hz().await?;
|
let moisture = self.measure_moisture_hz().await?;
|
||||||
for plant in 0..PLANT_COUNT {
|
for plant in 0..PLANT_COUNT {
|
||||||
let a = moisture.sensor_a_hz[plant] as u32;
|
let a = moisture.sensor_a_hz[plant].unwrap_or(0.0) as u32;
|
||||||
let b = moisture.sensor_b_hz[plant] as u32;
|
let b = moisture.sensor_b_hz[plant].unwrap_or(0.0) as u32;
|
||||||
LOG_ACCESS
|
LOG_ACCESS
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.log(LogMessage::TestSensor, a, b, &plant.to_string(), "")
|
.log(LogMessage::TestSensor, a, b, &(plant+1).to_string(), "")
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Timer::after_millis(10).await;
|
Timer::after_millis(10).await;
|
||||||
@@ -471,67 +491,16 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
self.charger.get_mppt_current()
|
self.charger.get_mppt_current()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn detect_sensors(&mut self) -> FatResult<DetectionResult> {
|
async fn can_power(&mut self, state: bool) -> FatResult<()> {
|
||||||
// Power on CAN transceiver and start controller
|
if state && self.can_power.is_set_low() {
|
||||||
self.can_power.set_high();
|
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 {
|
} else {
|
||||||
info!("Error building CAN frame");
|
self.can_power.set_low();
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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>,
|
||||||
@@ -554,16 +523,19 @@ async fn wait_for_can_measurements(
|
|||||||
let plant = msg.1 as usize;
|
let plant = msg.1 as usize;
|
||||||
let sensor = msg.2;
|
let sensor = msg.2;
|
||||||
let data = can_frame.data();
|
let data = can_frame.data();
|
||||||
if data.len() == 2 {
|
info!("Received moisture data: {:?}", data);
|
||||||
let frequency = u16::from_be_bytes([data[0], data[1]]);
|
if let Ok(bytes) = data.try_into() {
|
||||||
|
let frequency = u32::from_be_bytes(bytes);
|
||||||
match sensor {
|
match sensor {
|
||||||
SensorSlot::A => {
|
SensorSlot::A => {
|
||||||
moistures.sensor_a_hz[plant] = frequency as f32;
|
moistures.sensor_a_hz[plant-1] = Some(frequency as f32);
|
||||||
}
|
}
|
||||||
SensorSlot::B => {
|
SensorSlot::B => {
|
||||||
moistures.sensor_b_hz[plant] = frequency as f32;
|
moistures.sensor_b_hz[plant-1] = Some(frequency as f32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
error!("Received moisture data with invalid length: {} (expected 4)", data.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -591,10 +563,10 @@ impl From<Moistures> for DetectionResult {
|
|||||||
fn from(value: Moistures) -> Self {
|
fn from(value: Moistures) -> Self {
|
||||||
let mut result = DetectionResult::default();
|
let mut result = DetectionResult::default();
|
||||||
for (plant, sensor) in value.sensor_a_hz.iter().enumerate() {
|
for (plant, sensor) in value.sensor_a_hz.iter().enumerate() {
|
||||||
result.plant[plant].sensor_a = *sensor > 1.0_f32;
|
result.plant[plant].sensor_a = sensor.is_some();
|
||||||
}
|
}
|
||||||
for (plant, sensor) in value.sensor_b_hz.iter().enumerate() {
|
for (plant, sensor) in value.sensor_b_hz.iter().enumerate() {
|
||||||
result.plant[plant].sensor_b = *sensor > 1.0_f32;
|
result.plant[plant].sensor_b = sensor.is_some();
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, thi
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
pub enum MoistureSensorError {
|
pub enum MoistureSensorError {
|
||||||
|
NoMessage,
|
||||||
ShortCircuit { hz: f32, max: f32 },
|
ShortCircuit { hz: f32, max: f32 },
|
||||||
OpenLoop { hz: f32, min: f32 },
|
OpenLoop { hz: f32, min: f32 },
|
||||||
}
|
}
|
||||||
@@ -118,6 +119,11 @@ impl PlantState {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let sensor_a = if board.board_hal.get_config().plants[plant_id].sensor_a {
|
let sensor_a = if board.board_hal.get_config().plants[plant_id].sensor_a {
|
||||||
let raw = moistures.sensor_a_hz[plant_id];
|
let raw = moistures.sensor_a_hz[plant_id];
|
||||||
|
match raw {
|
||||||
|
None => {
|
||||||
|
MoistureSensorState::SensorError(MoistureSensorError::NoMessage)
|
||||||
|
}
|
||||||
|
Some(raw) => {
|
||||||
match map_range_moisture(
|
match map_range_moisture(
|
||||||
raw,
|
raw,
|
||||||
board.board_hal.get_config().plants[plant_id]
|
board.board_hal.get_config().plants[plant_id]
|
||||||
@@ -133,12 +139,20 @@ impl PlantState {
|
|||||||
},
|
},
|
||||||
Err(err) => MoistureSensorState::SensorError(err),
|
Err(err) => MoistureSensorState::SensorError(err),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
MoistureSensorState::Disabled
|
MoistureSensorState::Disabled
|
||||||
};
|
};
|
||||||
|
|
||||||
let sensor_b = if board.board_hal.get_config().plants[plant_id].sensor_b {
|
let sensor_b = if board.board_hal.get_config().plants[plant_id].sensor_b {
|
||||||
let raw = moistures.sensor_b_hz[plant_id];
|
let raw = moistures.sensor_b_hz[plant_id];
|
||||||
|
match raw {
|
||||||
|
None => {
|
||||||
|
MoistureSensorState::SensorError(MoistureSensorError::NoMessage)
|
||||||
|
}
|
||||||
|
Some(raw) => {
|
||||||
match map_range_moisture(
|
match map_range_moisture(
|
||||||
raw,
|
raw,
|
||||||
board.board_hal.get_config().plants[plant_id]
|
board.board_hal.get_config().plants[plant_id]
|
||||||
@@ -154,6 +168,8 @@ impl PlantState {
|
|||||||
},
|
},
|
||||||
Err(err) => MoistureSensorState::SensorError(err),
|
Err(err) => MoistureSensorState::SensorError(err),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
MoistureSensorState::Disabled
|
MoistureSensorState::Disabled
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ use crate::webserver::get_json::{
|
|||||||
use crate::webserver::get_log::get_log;
|
use crate::webserver::get_log::get_log;
|
||||||
use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index};
|
use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index};
|
||||||
use crate::webserver::ota::ota_operations;
|
use crate::webserver::ota::ota_operations;
|
||||||
use crate::webserver::post_json::{
|
use crate::webserver::post_json::{board_test, can_power, detect_sensors, night_lamp_test, pump_test, set_config, wifi_scan, write_time};
|
||||||
board_test, detect_sensors, night_lamp_test, pump_test, set_config, wifi_scan, write_time,
|
|
||||||
};
|
|
||||||
use crate::{bail, BOARD_ACCESS};
|
use crate::{bail, BOARD_ACCESS};
|
||||||
use alloc::borrow::ToOwned;
|
use alloc::borrow::ToOwned;
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
@@ -103,6 +101,7 @@ impl Handler for HTTPRequestRouter {
|
|||||||
"/time" => Some(write_time(conn).await),
|
"/time" => Some(write_time(conn).await),
|
||||||
"/backup_config" => Some(backup_config(conn).await),
|
"/backup_config" => Some(backup_config(conn).await),
|
||||||
"/pumptest" => Some(pump_test(conn).await),
|
"/pumptest" => Some(pump_test(conn).await),
|
||||||
|
"/can_power" => Some(can_power(conn).await),
|
||||||
"/lamptest" => Some(night_lamp_test(conn).await),
|
"/lamptest" => Some(night_lamp_test(conn).await),
|
||||||
"/boardtest" => Some(board_test().await),
|
"/boardtest" => Some(board_test().await),
|
||||||
"/detect_sensors" => Some(detect_sensors().await),
|
"/detect_sensors" => Some(detect_sensors().await),
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ pub struct TestPump {
|
|||||||
pump: usize,
|
pump: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
|
pub struct CanPower {
|
||||||
|
state: bool,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn wifi_scan<T, const N: usize>(
|
pub(crate) async fn wifi_scan<T, const N: usize>(
|
||||||
_request: &mut Connection<'_, T, N>,
|
_request: &mut Connection<'_, T, N>,
|
||||||
) -> FatResult<Option<String>> {
|
) -> FatResult<Option<String>> {
|
||||||
@@ -117,3 +122,22 @@ where
|
|||||||
board.board_hal.set_config(config);
|
board.board_hal.set_config(config);
|
||||||
Ok(Some("Ok".to_string()))
|
Ok(Some("Ok".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn can_power<T, const N: usize>(
|
||||||
|
request: &mut Connection<'_, T, N>,
|
||||||
|
) -> FatResult<Option<String>>
|
||||||
|
where
|
||||||
|
T: Read + Write,
|
||||||
|
{
|
||||||
|
let actual_data = read_up_to_bytes_from_request(request, None).await?;
|
||||||
|
let can_power_request: CanPower = serde_json::from_slice(&actual_data)?;
|
||||||
|
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||||
|
|
||||||
|
board.board_hal.can_power(can_power_request.state).await?;
|
||||||
|
let enable = can_power_request.state;
|
||||||
|
info!(
|
||||||
|
"set can power to {enable}"
|
||||||
|
);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,10 @@ export interface TestPump {
|
|||||||
pump: number
|
pump: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CanPower {
|
||||||
|
state: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface SetTime {
|
export interface SetTime {
|
||||||
time: string
|
time: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,7 @@
|
|||||||
<h3>Plants:</h3>
|
<h3>Plants:</h3>
|
||||||
<button id="measure_moisture">Measure Moisture</button>
|
<button id="measure_moisture">Measure Moisture</button>
|
||||||
<button id="detect_sensors" style="display:none">Detect/Test Sensors</button>
|
<button id="detect_sensors" style="display:none">Detect/Test Sensors</button>
|
||||||
|
<input id="can_power" type="checkbox">Power CAN</input>
|
||||||
<div id="plants" class="plantlist"></div>
|
<div id="plants" class="plantlist"></div>
|
||||||
|
|
||||||
<div class="flexcontainer-rev">
|
<div class="flexcontainer-rev">
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
SetTime, SSIDList, TankInfo,
|
SetTime, SSIDList, TankInfo,
|
||||||
TestPump,
|
TestPump,
|
||||||
VersionInfo,
|
VersionInfo,
|
||||||
FileList, SolarState, PumpTestResult, DetectionResult
|
FileList, SolarState, PumpTestResult, DetectionResult, CanPower
|
||||||
} from "./api";
|
} from "./api";
|
||||||
import {SolarView} from "./solarview";
|
import {SolarView} from "./solarview";
|
||||||
import {toast} from "./toast";
|
import {toast} from "./toast";
|
||||||
@@ -527,6 +527,18 @@ export class Controller {
|
|||||||
setTimeout(this.waitForReboot, 1000)
|
setTimeout(this.waitForReboot, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setCanPower(checked: boolean) {
|
||||||
|
var body: CanPower = {
|
||||||
|
state : checked
|
||||||
|
}
|
||||||
|
var pretty = JSON.stringify(body, undefined, 1);
|
||||||
|
|
||||||
|
fetch(PUBLIC_URL + "/can_power", {
|
||||||
|
method: "POST",
|
||||||
|
body: pretty
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
initialConfig: PlantControllerConfig | null = null
|
initialConfig: PlantControllerConfig | null = null
|
||||||
readonly rebootBtn: HTMLButtonElement
|
readonly rebootBtn: HTMLButtonElement
|
||||||
readonly exitBtn: HTMLButtonElement
|
readonly exitBtn: HTMLButtonElement
|
||||||
@@ -544,6 +556,7 @@ export class Controller {
|
|||||||
readonly fileview: FileView;
|
readonly fileview: FileView;
|
||||||
readonly logView: LogView
|
readonly logView: LogView
|
||||||
readonly detectBtn: HTMLButtonElement
|
readonly detectBtn: HTMLButtonElement
|
||||||
|
readonly can_power: HTMLInputElement;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.timeView = new TimeView(this)
|
this.timeView = new TimeView(this)
|
||||||
@@ -569,9 +582,15 @@ export class Controller {
|
|||||||
this.exitBtn.onclick = () => {
|
this.exitBtn.onclick = () => {
|
||||||
controller.exit();
|
controller.exit();
|
||||||
}
|
}
|
||||||
|
this.can_power = document.getElementById("can_power") as HTMLInputElement
|
||||||
|
this.can_power.onchange = () => {
|
||||||
|
controller.setCanPower(this.can_power.checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const controller = new Controller();
|
const controller = new Controller();
|
||||||
controller.progressview.removeProgress("rebooting");
|
controller.progressview.removeProgress("rebooting");
|
||||||
|
|
||||||
|
|||||||
@@ -227,47 +227,47 @@ export class PlantView {
|
|||||||
let showTarget = plantConfig.mode === "TargetMoisture"
|
let showTarget = plantConfig.mode === "TargetMoisture"
|
||||||
let showMin = plantConfig.mode === "MinMoisture"
|
let showMin = plantConfig.mode === "MinMoisture"
|
||||||
|
|
||||||
if(this.showDisabled || plantConfig.sensor_a || plantConfig.sensor_b) {
|
// if(this.showDisabled || plantConfig.sensor_a || plantConfig.sensor_b) {
|
||||||
console.log("Showing plant " + this.plantId);
|
// console.log("Showing plant " + this.plantId);
|
||||||
this.plantDiv.style.display = "block";
|
// this.plantDiv.style.display = "block";
|
||||||
} else {
|
// } else {
|
||||||
console.log("Hiding plant " + this.plantId);
|
// console.log("Hiding plant " + this.plantId);
|
||||||
this.plantDiv.style.display = "none";
|
// this.plantDiv.style.display = "none";
|
||||||
}
|
// }
|
||||||
|
|
||||||
console.log("updateVisibility showsensor: " + showSensor + " pump " + showPump + " target " +showTarget + " min " + showMin)
|
console.log("updateVisibility showsensor: " + showSensor + " pump " + showPump + " target " +showTarget + " min " + showMin)
|
||||||
|
|
||||||
for (const element of Array.from(sensorOnly)) {
|
// for (const element of Array.from(sensorOnly)) {
|
||||||
if (showSensor) {
|
// if (showSensor) {
|
||||||
element.classList.remove("plantHidden_" + this.plantId)
|
// element.classList.remove("plantHidden_" + this.plantId)
|
||||||
} else {
|
// } else {
|
||||||
element.classList.add("plantHidden_" + this.plantId)
|
// element.classList.add("plantHidden_" + this.plantId)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
for (const element of Array.from(pumpOnly)) {
|
// for (const element of Array.from(pumpOnly)) {
|
||||||
if (showPump) {
|
// if (showPump) {
|
||||||
element.classList.remove("plantHidden_" + this.plantId)
|
// element.classList.remove("plantHidden_" + this.plantId)
|
||||||
} else {
|
// } else {
|
||||||
element.classList.add("plantHidden_" + this.plantId)
|
// element.classList.add("plantHidden_" + this.plantId)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
for (const element of Array.from(targetOnly)) {
|
// for (const element of Array.from(targetOnly)) {
|
||||||
if (showTarget) {
|
// if (showTarget) {
|
||||||
element.classList.remove("plantHidden_" + this.plantId)
|
// element.classList.remove("plantHidden_" + this.plantId)
|
||||||
} else {
|
// } else {
|
||||||
element.classList.add("plantHidden_" + this.plantId)
|
// element.classList.add("plantHidden_" + this.plantId)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
for (const element of Array.from(minOnly)) {
|
// for (const element of Array.from(minOnly)) {
|
||||||
if (showMin) {
|
// if (showMin) {
|
||||||
element.classList.remove("plantHidden_" + this.plantId)
|
// element.classList.remove("plantHidden_" + this.plantId)
|
||||||
} else {
|
// } else {
|
||||||
element.classList.add("plantHidden_" + this.plantId)
|
// element.classList.add("plantHidden_" + this.plantId)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
setTestResult(result: PumpTestResult) {
|
setTestResult(result: PumpTestResult) {
|
||||||
@@ -335,8 +335,9 @@ export class PlantView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setDetectionResult(plantResult: DetectionPlant) {
|
setDetectionResult(plantResult: DetectionPlant) {
|
||||||
|
console.log("setDetectionResult plantResult: " + plantResult.sensor_a + " " + plantResult.sensor_b)
|
||||||
var changed = false;
|
var changed = false;
|
||||||
if (this.sensorAInstalled.checked != plantResult.sensor_b){
|
if (this.sensorAInstalled.checked != plantResult.sensor_a){
|
||||||
changed = true;
|
changed = true;
|
||||||
this.sensorAInstalled.checked = plantResult.sensor_a;
|
this.sensorAInstalled.checked = plantResult.sensor_a;
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule website/themes/blowfish updated: 26d1205439...f9eb1d4e81
Reference in New Issue
Block a user