Compare commits
3 Commits
3128e32fb2
...
125b3efad3
| Author | SHA1 | Date | |
|---|---|---|---|
| 125b3efad3 | |||
| 9b21d505e6 | |||
| c575fc2c36 |
@@ -12,6 +12,7 @@ use ch32_hal::gpio::{Flex, Level, Output, Pull, Speed};
|
|||||||
use ch32_hal::mode::NonBlocking;
|
use ch32_hal::mode::NonBlocking;
|
||||||
use ch32_hal::peripherals::USBD;
|
use ch32_hal::peripherals::USBD;
|
||||||
use core::fmt::Write as _;
|
use core::fmt::Write as _;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use embassy_executor::{task, Spawner};
|
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;
|
||||||
@@ -42,9 +43,10 @@ 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();
|
static CAN_TX_CH: Channel<CriticalSectionRawMutex, CanFrame, 4> = Channel::new();
|
||||||
|
|
||||||
|
static BEACON: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
||||||
@@ -67,7 +69,7 @@ async fn main(spawner: Spawner) {
|
|||||||
|
|
||||||
// Create GPIO for 555 Q output (PB0)
|
// Create GPIO for 555 Q output (PB0)
|
||||||
let q_out = Output::new(p.PA0, Level::Low, Speed::Low);
|
let q_out = Output::new(p.PA0, Level::Low, Speed::Low);
|
||||||
let info = Output::new(p.PA10, Level::Low, Speed::Low);
|
let info = mk_static!(Output, Output::new(p.PA10, Level::Low, Speed::Low));
|
||||||
let warn = mk_static!(Output, Output::new(p.PA9, 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
|
||||||
@@ -207,7 +209,7 @@ async fn main(spawner: Spawner) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
log(msg);
|
log(msg);
|
||||||
spawner.spawn(blink_error(warn, code)).unwrap();
|
spawner.spawn(blink_error_task(warn, info, 2, code)).unwrap();
|
||||||
} else {
|
} else {
|
||||||
// Log startup configuration and derived CAN IDs
|
// Log startup configuration and derived CAN IDs
|
||||||
|
|
||||||
@@ -247,17 +249,19 @@ async fn main(spawner: Spawner) {
|
|||||||
// let mut filter = CanFilter::new_id_list();
|
// let mut filter = CanFilter::new_id_list();
|
||||||
// filter.get(0).unwrap().set(Id::Standard(standard_identify_id), Default::default());
|
// filter.get(0).unwrap().set(Id::Standard(standard_identify_id), Default::default());
|
||||||
// can.add_filter(filter);
|
// can.add_filter(filter);
|
||||||
spawner.spawn(can_task(can, warn, standard_identify_id)).unwrap();
|
let standard_moisture_id = StandardId::new(moisture_id).unwrap();
|
||||||
|
spawner
|
||||||
|
.spawn(can_task(can,info, warn, standard_identify_id, standard_moisture_id))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// move Q output, LED, ADC and analog input into worker task
|
// move Q output, LED, ADC and analog input into worker task
|
||||||
spawner
|
spawner
|
||||||
.spawn(worker(
|
.spawn(worker(
|
||||||
probe_gnd,
|
probe_gnd,
|
||||||
q_out,
|
q_out,
|
||||||
info,
|
|
||||||
adc,
|
adc,
|
||||||
ain,
|
ain,
|
||||||
StandardId::new(moisture_id).unwrap(),
|
standard_moisture_id,
|
||||||
standard_identify_id,
|
standard_identify_id,
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -288,27 +292,44 @@ async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option<bool> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[task]
|
async fn blink_error_loop(info_led: &mut Output<'static>, warn_led: &mut Output<'static>, c_i: u8, c_w: u8) -> ! {
|
||||||
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), 7 for CAN address collision
|
||||||
for _ in 0..code {
|
for _ in 0..c_i {
|
||||||
info_led.set_high();
|
info_led.set_high();
|
||||||
Timer::after_millis(200).await;
|
Timer::after_millis(200).await;
|
||||||
info_led.set_low();
|
info_led.set_low();
|
||||||
Timer::after_millis(200).await;
|
Timer::after_millis(200).await;
|
||||||
}
|
}
|
||||||
|
for _ in 0..c_w {
|
||||||
|
warn_led.set_high();
|
||||||
|
Timer::after_millis(200).await;
|
||||||
|
warn_led.set_low();
|
||||||
|
Timer::after_millis(200).await;
|
||||||
|
}
|
||||||
// Pause between sequences
|
// Pause between sequences
|
||||||
Timer::after_secs(2).await;
|
Timer::after_millis(400).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
async fn blink_error_task(info_led: &'static mut Output<'static>, warn_led: &'static mut Output<'static>, c_i: u8, c_w: u8) -> ! {
|
||||||
|
blink_error_loop(info_led, warn_led, c_i, c_w).await
|
||||||
|
}
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
async fn can_task(
|
async fn can_task(
|
||||||
mut can: Can<'static, CAN1, NonBlocking>,
|
mut can: Can<'static, CAN1, NonBlocking>,
|
||||||
|
info: &'static mut Output<'static>,
|
||||||
warn: &'static mut Output<'static>,
|
warn: &'static mut Output<'static>,
|
||||||
identify_id: StandardId,
|
identify_id: StandardId,
|
||||||
|
moisture_id: StandardId,
|
||||||
) {
|
) {
|
||||||
|
// Non-blocking beacon blink timing.
|
||||||
|
// We keep this inside the CAN task so it can't stall other tasks (like `worker`) with `await`s.
|
||||||
|
let mut next_beacon_toggle = Instant::now();
|
||||||
|
let beacon_period = Duration::from_millis(50);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match can.receive() {
|
match can.receive() {
|
||||||
Ok(frame) => {
|
Ok(frame) => {
|
||||||
@@ -322,8 +343,15 @@ async fn can_task(
|
|||||||
identify_id.as_raw()
|
identify_id.as_raw()
|
||||||
);
|
);
|
||||||
log(msg);
|
log(msg);
|
||||||
|
if s_frame.as_raw() == moisture_id.as_raw() {
|
||||||
|
//trigger collision detection on other side as well
|
||||||
|
let _ = can.transmit(&frame);
|
||||||
|
// We should never receive moisture packets addressed to ourselves.
|
||||||
|
// If we do, another node likely uses the same jumper configuration.
|
||||||
|
blink_error_loop(info, warn, 1,2).await;
|
||||||
|
}
|
||||||
if s_frame.as_raw() == identify_id.as_raw() {
|
if s_frame.as_raw() == identify_id.as_raw() {
|
||||||
CAN_RX_CH.send(frame).await;
|
BEACON.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Id::Extended(_) => {}
|
Id::Extended(_) => {}
|
||||||
@@ -345,6 +373,15 @@ async fn can_task(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if BEACON.load(Ordering::Relaxed) {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now >= next_beacon_toggle {
|
||||||
|
info.toggle();
|
||||||
|
// Move the schedule forward; if we fell behind, resync to "now".
|
||||||
|
next_beacon_toggle = now + beacon_period;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while let Ok(mut frame) = CAN_TX_CH.try_receive() {
|
while let Ok(mut frame) = CAN_TX_CH.try_receive() {
|
||||||
match can.transmit(&mut frame) {
|
match can.transmit(&mut frame) {
|
||||||
Ok(..) => {
|
Ok(..) => {
|
||||||
@@ -381,7 +418,6 @@ async fn can_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 adc: Adc<'static, hal::peripherals::ADC1>,
|
mut adc: Adc<'static, hal::peripherals::ADC1>,
|
||||||
mut ain: hal::peripherals::PA1,
|
mut ain: hal::peripherals::PA1,
|
||||||
moisture_id: StandardId,
|
moisture_id: StandardId,
|
||||||
@@ -452,14 +488,6 @@ async fn worker(
|
|||||||
|
|
||||||
let moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap();
|
let moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap();
|
||||||
CAN_TX_CH.send(moisture).await;
|
CAN_TX_CH.send(moisture).await;
|
||||||
|
|
||||||
while let Ok(_frame) = CAN_RX_CH.try_receive() {
|
|
||||||
for _ in 0..10 {
|
|
||||||
Timer::after_millis(250).await;
|
|
||||||
info.toggle();
|
|
||||||
}
|
|
||||||
info.set_low();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
|
|||||||
use littlefs2::object_safe::DynStorage;
|
use littlefs2::object_safe::DynStorage;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use portable_atomic::AtomicBool;
|
use portable_atomic::AtomicBool;
|
||||||
use serde::Serialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use shared_flash::MutexFlashStorage;
|
use shared_flash::MutexFlashStorage;
|
||||||
|
|
||||||
pub static TIME_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, Rtc>> = OnceLock::new();
|
pub static TIME_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, Rtc>> = OnceLock::new();
|
||||||
@@ -138,6 +138,12 @@ pub struct HAL<'a> {
|
|||||||
pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
|
pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DetectionRequest {
|
||||||
|
pub sensorsa: [Sensor; PLANT_COUNT],
|
||||||
|
pub sensorsb: [Sensor; PLANT_COUNT],
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait BoardInteraction<'a> {
|
pub trait BoardInteraction<'a> {
|
||||||
fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError>;
|
fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError>;
|
||||||
@@ -163,7 +169,7 @@ pub trait BoardInteraction<'a> {
|
|||||||
async fn can_power(&mut self, state: bool) -> FatResult<()>;
|
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, request: Detection) -> FatResult<Detection> {
|
||||||
bail!("Autodetection is only available on v4 HAL with CAN bus");
|
bail!("Autodetection is only available on v4 HAL with CAN bus");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,12 +690,13 @@ pub struct Moistures {
|
|||||||
pub sensor_b_hz: [Option<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, Deserialize)]
|
||||||
pub struct DetectionResult {
|
pub struct Detection {
|
||||||
plant: [DetectionSensorResult; PLANT_COUNT],
|
plant: [DetectionSensorResult; PLANT_COUNT],
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||||
pub struct DetectionSensorResult {
|
pub struct DetectionSensorResult {
|
||||||
sensor_a: bool,
|
sensor_a: bool,
|
||||||
sensor_b: bool,
|
sensor_b: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::hal::esp::{hold_disable, hold_enable, Esp};
|
|||||||
use crate::hal::rtc::RTCModuleInteraction;
|
use crate::hal::rtc::RTCModuleInteraction;
|
||||||
use crate::hal::water::TankSensor;
|
use crate::hal::water::TankSensor;
|
||||||
use crate::hal::{
|
use crate::hal::{
|
||||||
BoardInteraction, DetectionResult, FreePeripherals, Moistures, Sensor, I2C_DRIVER, PLANT_COUNT,
|
BoardInteraction, Detection, FreePeripherals, Moistures, Sensor, I2C_DRIVER, PLANT_COUNT,
|
||||||
TIME_ACCESS,
|
TIME_ACCESS,
|
||||||
};
|
};
|
||||||
use crate::log::{LogMessage, LOG_ACCESS};
|
use crate::log::{LogMessage, LOG_ACCESS};
|
||||||
@@ -375,7 +375,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
Ok(moistures)
|
Ok(moistures)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn detect_sensors(&mut self) -> FatResult<DetectionResult> {
|
async fn detect_sensors(&mut self, request: Detection) -> FatResult<Detection> {
|
||||||
self.can_power.set_high();
|
self.can_power.set_high();
|
||||||
let config = self.twai_config.take().expect("twai config not set");
|
let config = self.twai_config.take().expect("twai config not set");
|
||||||
let mut twai = config.into_async().start();
|
let mut twai = config.into_async().start();
|
||||||
@@ -385,6 +385,14 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
// Send a few test messages per potential sensor node
|
// Send a few test messages per potential sensor node
|
||||||
for plant in 0..PLANT_COUNT {
|
for plant in 0..PLANT_COUNT {
|
||||||
for sensor in [Sensor::A, Sensor::B] {
|
for sensor in [Sensor::A, Sensor::B] {
|
||||||
|
let detect = if sensor == Sensor::A {
|
||||||
|
request.plant[plant].sensor_a
|
||||||
|
} else {
|
||||||
|
request.plant[plant].sensor_b
|
||||||
|
};
|
||||||
|
if !detect {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let target =
|
let target =
|
||||||
StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, sensor.into(), (plant +1) as u16))
|
StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, sensor.into(), (plant +1) as u16))
|
||||||
.context(">> Could not create address for sensor! (plant: {}) <<")?;
|
.context(">> Could not create address for sensor! (plant: {}) <<")?;
|
||||||
@@ -559,9 +567,9 @@ async fn wait_for_can_measurements(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Moistures> for DetectionResult {
|
impl From<Moistures> for Detection {
|
||||||
fn from(value: Moistures) -> Self {
|
fn from(value: Moistures) -> Self {
|
||||||
let mut result = DetectionResult::default();
|
let mut result = Detection::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.is_some();
|
result.plant[plant].sensor_a = sensor.is_some();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ impl PlantState {
|
|||||||
plant_id: usize,
|
plant_id: usize,
|
||||||
board: &mut HAL<'_>,
|
board: &mut HAL<'_>,
|
||||||
) -> 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 {
|
match raw {
|
||||||
None => {
|
None => {
|
||||||
@@ -142,11 +142,11 @@ impl PlantState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} 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 {
|
match raw {
|
||||||
None => {
|
None => {
|
||||||
@@ -170,9 +170,9 @@ impl PlantState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}; // else {
|
||||||
MoistureSensorState::Disabled
|
// MoistureSensorState::Disabled
|
||||||
};
|
//};
|
||||||
|
|
||||||
let previous_pump = board.board_hal.get_esp().last_pump_time(plant_id);
|
let previous_pump = board.board_hal.get_esp().last_pump_time(plant_id);
|
||||||
let consecutive_pump_count = board.board_hal.get_esp().consecutive_pump_count(plant_id);
|
let consecutive_pump_count = board.board_hal.get_esp().consecutive_pump_count(plant_id);
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ impl Handler for HTTPRequestRouter {
|
|||||||
"/can_power" => Some(can_power(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(conn).await),
|
||||||
"/reboot" => {
|
"/reboot" => {
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||||
board.board_hal.get_esp().set_restart_to_conf(true);
|
board.board_hal.get_esp().set_restart_to_conf(true);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::config::PlantControllerConfig;
|
use crate::config::PlantControllerConfig;
|
||||||
use crate::fat_error::FatResult;
|
use crate::fat_error::FatResult;
|
||||||
use crate::hal::esp_set_time;
|
use crate::hal::{esp_set_time, Detection, DetectionRequest};
|
||||||
use crate::webserver::read_up_to_bytes_from_request;
|
use crate::webserver::read_up_to_bytes_from_request;
|
||||||
use crate::{do_secure_pump, BOARD_ACCESS};
|
use crate::{do_secure_pump, BOARD_ACCESS};
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
@@ -55,9 +55,16 @@ pub(crate) async fn board_test() -> FatResult<Option<String>> {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn detect_sensors() -> FatResult<Option<String>> {
|
pub(crate) async fn detect_sensors<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 detect: Detection = serde_json::from_slice(&actual_data)?;
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||||
let result = board.board_hal.detect_sensors().await?;
|
let result = board.board_hal.detect_sensors(detect).await?;
|
||||||
let json = serde_json::to_string(&result)?;
|
let json = serde_json::to_string(&result)?;
|
||||||
Ok(Some(json))
|
Ok(Some(json))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ export interface DetectionPlant {
|
|||||||
sensor_b: boolean
|
sensor_b: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DetectionResult {
|
export interface Detection {
|
||||||
plant: DetectionPlant[]
|
plant: DetectionPlant[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ document.body.innerHTML = require('./main.html') as string;
|
|||||||
|
|
||||||
|
|
||||||
import {TimeView} from "./timeview";
|
import {TimeView} from "./timeview";
|
||||||
import {PlantViews} from "./plant";
|
import {PlantViews, PLANT_COUNT} from "./plant";
|
||||||
import {NetworkConfigView} from "./network";
|
import {NetworkConfigView} from "./network";
|
||||||
import {NightLampView} from "./nightlightview";
|
import {NightLampView} from "./nightlightview";
|
||||||
import {TankConfigView} from "./tankview";
|
import {TankConfigView} from "./tankview";
|
||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
SetTime, SSIDList, TankInfo,
|
SetTime, SSIDList, TankInfo,
|
||||||
TestPump,
|
TestPump,
|
||||||
VersionInfo,
|
VersionInfo,
|
||||||
FileList, SolarState, PumpTestResult, DetectionResult, CanPower
|
FileList, SolarState, PumpTestResult, Detection, CanPower
|
||||||
} from "./api";
|
} from "./api";
|
||||||
import {SolarView} from "./solarview";
|
import {SolarView} from "./solarview";
|
||||||
import {toast} from "./toast";
|
import {toast} from "./toast";
|
||||||
@@ -361,7 +361,7 @@ export class Controller {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async detectSensors() {
|
async detectSensors(detection: Detection) {
|
||||||
let counter = 0
|
let counter = 0
|
||||||
let limit = 5
|
let limit = 5
|
||||||
controller.progressview.addProgress("detect_sensors", counter / limit * 100, "Detecting sensors " + (limit - counter) + "s")
|
controller.progressview.addProgress("detect_sensors", counter / limit * 100, "Detecting sensors " + (limit - counter) + "s")
|
||||||
@@ -376,9 +376,11 @@ export class Controller {
|
|||||||
|
|
||||||
timerId = setTimeout(updateProgress, 1000);
|
timerId = setTimeout(updateProgress, 1000);
|
||||||
|
|
||||||
fetch(PUBLIC_URL + "/detect_sensors", { method: "POST" })
|
var pretty = JSON.stringify(detection, undefined, 1);
|
||||||
|
|
||||||
|
fetch(PUBLIC_URL + "/detect_sensors", { method: "POST", body: pretty })
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then (json => json as DetectionResult)
|
.then (json => json as Detection)
|
||||||
.then(json => {
|
.then(json => {
|
||||||
clearTimeout(timerId);
|
clearTimeout(timerId);
|
||||||
controller.progressview.removeProgress("detect_sensors");
|
controller.progressview.removeProgress("detect_sensors");
|
||||||
@@ -573,7 +575,15 @@ export class Controller {
|
|||||||
this.logView = new LogView(this)
|
this.logView = new LogView(this)
|
||||||
this.hardwareView = new HardwareConfigView(this)
|
this.hardwareView = new HardwareConfigView(this)
|
||||||
this.detectBtn = document.getElementById("detect_sensors") as HTMLButtonElement
|
this.detectBtn = document.getElementById("detect_sensors") as HTMLButtonElement
|
||||||
this.detectBtn.onclick = () => { controller.detectSensors(); }
|
this.detectBtn.onclick = () => {
|
||||||
|
const detection: Detection = {
|
||||||
|
plant: Array.from({length: PLANT_COUNT}, () => ({
|
||||||
|
sensor_a: true,
|
||||||
|
sensor_b: true,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
controller.detectSensors(detection);
|
||||||
|
}
|
||||||
this.rebootBtn = document.getElementById("reboot") as HTMLButtonElement
|
this.rebootBtn = document.getElementById("reboot") as HTMLButtonElement
|
||||||
this.rebootBtn.onclick = () => {
|
this.rebootBtn.onclick = () => {
|
||||||
controller.reboot();
|
controller.reboot();
|
||||||
|
|||||||
@@ -125,6 +125,10 @@
|
|||||||
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
|
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
|
||||||
<div class="subtitle">Live:</div>
|
<div class="subtitle">Live:</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
|
||||||
|
<button class="subtitle" id="plant_${plantId}_test_sensor_a">Test Sensor A</button>
|
||||||
|
<button class="subtitle" id="plant_${plantId}_test_sensor_b">Test Sensor B</button>
|
||||||
|
</div>
|
||||||
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
|
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
|
||||||
<span class="plantsensorkey">Sensor A:</span>
|
<span class="plantsensorkey">Sensor A:</span>
|
||||||
<span class="plantsensorvalue" id="plant_${plantId}_moisture_a">not measured</span>
|
<span class="plantsensorvalue" id="plant_${plantId}_moisture_a">not measured</span>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {DetectionPlant, DetectionResult, PlantConfig, PumpTestResult} from "./api";
|
import {DetectionPlant, Detection, PlantConfig, PumpTestResult} from "./api";
|
||||||
|
|
||||||
const PLANT_COUNT = 8;
|
export const PLANT_COUNT = 8;
|
||||||
|
|
||||||
|
|
||||||
import {Controller} from "./main";
|
import {Controller} from "./main";
|
||||||
@@ -48,7 +48,7 @@ export class PlantViews {
|
|||||||
plantView.setTestResult(response)
|
plantView.setTestResult(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyDetectionResult(json: DetectionResult) {
|
applyDetectionResult(json: Detection) {
|
||||||
for (let i = 0; i < PLANT_COUNT; i++) {
|
for (let i = 0; i < PLANT_COUNT; i++) {
|
||||||
var plantResult = json.plant[i];
|
var plantResult = json.plant[i];
|
||||||
this.plants[i].setDetectionResult(plantResult);
|
this.plants[i].setDetectionResult(plantResult);
|
||||||
@@ -65,6 +65,8 @@ export class PlantView {
|
|||||||
private readonly plantDiv: HTMLDivElement;
|
private readonly plantDiv: HTMLDivElement;
|
||||||
private readonly header: HTMLElement;
|
private readonly header: HTMLElement;
|
||||||
private readonly testButton: HTMLButtonElement;
|
private readonly testButton: HTMLButtonElement;
|
||||||
|
private readonly testSensorAButton: HTMLButtonElement;
|
||||||
|
private readonly testSensorBButton: HTMLButtonElement;
|
||||||
private readonly targetMoisture: HTMLInputElement;
|
private readonly targetMoisture: HTMLInputElement;
|
||||||
private readonly minMoisture: HTMLInputElement;
|
private readonly minMoisture: HTMLInputElement;
|
||||||
private readonly pumpTimeS: HTMLInputElement;
|
private readonly pumpTimeS: HTMLInputElement;
|
||||||
@@ -119,6 +121,28 @@ export class PlantView {
|
|||||||
controller.testPlant(plantId)
|
controller.testPlant(plantId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.testSensorAButton = document.getElementById("plant_" + plantId + "_test_sensor_a")! as HTMLButtonElement;
|
||||||
|
this.testSensorAButton.onclick = () => {
|
||||||
|
const detection: Detection = {
|
||||||
|
plant: Array.from({length: PLANT_COUNT}, (_v, idx) => ({
|
||||||
|
sensor_a: idx === plantId,
|
||||||
|
sensor_b: false,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
controller.detectSensors(detection);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.testSensorBButton = document.getElementById("plant_" + plantId + "_test_sensor_b")! as HTMLButtonElement;
|
||||||
|
this.testSensorBButton.onclick = () => {
|
||||||
|
const detection: Detection = {
|
||||||
|
plant: Array.from({length: PLANT_COUNT}, (_v, idx) => ({
|
||||||
|
sensor_a: false,
|
||||||
|
sensor_b: idx === plantId,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
controller.detectSensors(detection);
|
||||||
|
};
|
||||||
|
|
||||||
this.mode = document.getElementById("plant_" + plantId + "_mode") as HTMLSelectElement
|
this.mode = document.getElementById("plant_" + plantId + "_mode") as HTMLSelectElement
|
||||||
this.mode.onchange = function () {
|
this.mode.onchange = function () {
|
||||||
controller.configChanged()
|
controller.configChanged()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Controller } from "./main";
|
import { Controller } from "./main";
|
||||||
import {DetectionResult, TankConfig, TankInfo} from "./api";
|
import {Detection, TankConfig, TankInfo} from "./api";
|
||||||
|
|
||||||
export class TankConfigView {
|
export class TankConfigView {
|
||||||
private readonly tank_useable_ml: HTMLInputElement;
|
private readonly tank_useable_ml: HTMLInputElement;
|
||||||
|
|||||||
Reference in New Issue
Block a user