allow single sensor detection, get rid of sensor disabled hardware state == nomessage

This commit is contained in:
2026-02-27 23:12:40 +01:00
parent c575fc2c36
commit 9b21d505e6
10 changed files with 92 additions and 32 deletions

View File

@@ -101,7 +101,7 @@ use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
use littlefs2::object_safe::DynStorage;
use log::{error, info, warn};
use portable_atomic::AtomicBool;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use shared_flash::MutexFlashStorage;
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 struct DetectionRequest {
pub sensorsa: [Sensor; PLANT_COUNT],
pub sensorsb: [Sensor; PLANT_COUNT],
}
#[async_trait(?Send)]
pub trait BoardInteraction<'a> {
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<()>;
// 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");
}
@@ -684,12 +690,13 @@ pub struct Moistures {
pub sensor_b_hz: [Option<f32>; PLANT_COUNT],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
pub struct DetectionResult {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct Detection {
plant: [DetectionSensorResult; PLANT_COUNT],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct DetectionSensorResult {
sensor_a: bool,
sensor_b: bool,
}

View File

@@ -6,7 +6,7 @@ use crate::hal::esp::{hold_disable, hold_enable, Esp};
use crate::hal::rtc::RTCModuleInteraction;
use crate::hal::water::TankSensor;
use crate::hal::{
BoardInteraction, DetectionResult, FreePeripherals, Moistures, Sensor, I2C_DRIVER, PLANT_COUNT,
BoardInteraction, Detection, FreePeripherals, Moistures, Sensor, I2C_DRIVER, PLANT_COUNT,
TIME_ACCESS,
};
use crate::log::{LogMessage, LOG_ACCESS};
@@ -375,7 +375,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
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();
let config = self.twai_config.take().expect("twai config not set");
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
for plant in 0..PLANT_COUNT {
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 =
StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, sensor.into(), (plant +1) as u16))
.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 {
let mut result = DetectionResult::default();
let mut result = Detection::default();
for (plant, sensor) in value.sensor_a_hz.iter().enumerate() {
result.plant[plant].sensor_a = sensor.is_some();
}

View File

@@ -117,7 +117,7 @@ impl PlantState {
plant_id: usize,
board: &mut HAL<'_>,
) -> 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];
match raw {
None => {
@@ -142,11 +142,11 @@ impl PlantState {
}
}
} else {
MoistureSensorState::Disabled
};
}; // else {
// 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];
match raw {
None => {
@@ -170,9 +170,9 @@ impl PlantState {
}
}
}
} else {
MoistureSensorState::Disabled
};
}; // else {
// MoistureSensorState::Disabled
//};
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);

View File

@@ -104,7 +104,7 @@ impl Handler for HTTPRequestRouter {
"/can_power" => Some(can_power(conn).await),
"/lamptest" => Some(night_lamp_test(conn).await),
"/boardtest" => Some(board_test().await),
"/detect_sensors" => Some(detect_sensors().await),
"/detect_sensors" => Some(detect_sensors(conn).await),
"/reboot" => {
let mut board = BOARD_ACCESS.get().await.lock().await;
board.board_hal.get_esp().set_restart_to_conf(true);

View File

@@ -1,6 +1,6 @@
use crate::config::PlantControllerConfig;
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::{do_secure_pump, BOARD_ACCESS};
use alloc::string::{String, ToString};
@@ -55,9 +55,16 @@ pub(crate) async fn board_test() -> FatResult<Option<String>> {
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 result = board.board_hal.detect_sensors().await?;
let result = board.board_hal.detect_sensors(detect).await?;
let json = serde_json::to_string(&result)?;
Ok(Some(json))
}

View File

@@ -182,7 +182,7 @@ export interface DetectionPlant {
sensor_b: boolean
}
export interface DetectionResult {
export interface Detection {
plant: DetectionPlant[]
}

View File

@@ -8,7 +8,7 @@ document.body.innerHTML = require('./main.html') as string;
import {TimeView} from "./timeview";
import {PlantViews} from "./plant";
import {PlantViews, PLANT_COUNT} from "./plant";
import {NetworkConfigView} from "./network";
import {NightLampView} from "./nightlightview";
import {TankConfigView} from "./tankview";
@@ -29,7 +29,7 @@ import {
SetTime, SSIDList, TankInfo,
TestPump,
VersionInfo,
FileList, SolarState, PumpTestResult, DetectionResult, CanPower
FileList, SolarState, PumpTestResult, Detection, CanPower
} from "./api";
import {SolarView} from "./solarview";
import {toast} from "./toast";
@@ -361,7 +361,7 @@ export class Controller {
)
}
async detectSensors() {
async detectSensors(detection: Detection) {
let counter = 0
let limit = 5
controller.progressview.addProgress("detect_sensors", counter / limit * 100, "Detecting sensors " + (limit - counter) + "s")
@@ -376,9 +376,11 @@ export class Controller {
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 (json => json as DetectionResult)
.then (json => json as Detection)
.then(json => {
clearTimeout(timerId);
controller.progressview.removeProgress("detect_sensors");
@@ -573,7 +575,15 @@ export class Controller {
this.logView = new LogView(this)
this.hardwareView = new HardwareConfigView(this)
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.onclick = () => {
controller.reboot();

View File

@@ -125,6 +125,10 @@
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
<div class="subtitle">Live:</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}">
<span class="plantsensorkey">Sensor A:</span>
<span class="plantsensorvalue" id="plant_${plantId}_moisture_a">not measured</span>

View File

@@ -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";
@@ -48,7 +48,7 @@ export class PlantViews {
plantView.setTestResult(response)
}
applyDetectionResult(json: DetectionResult) {
applyDetectionResult(json: Detection) {
for (let i = 0; i < PLANT_COUNT; i++) {
var plantResult = json.plant[i];
this.plants[i].setDetectionResult(plantResult);
@@ -65,6 +65,8 @@ export class PlantView {
private readonly plantDiv: HTMLDivElement;
private readonly header: HTMLElement;
private readonly testButton: HTMLButtonElement;
private readonly testSensorAButton: HTMLButtonElement;
private readonly testSensorBButton: HTMLButtonElement;
private readonly targetMoisture: HTMLInputElement;
private readonly minMoisture: HTMLInputElement;
private readonly pumpTimeS: HTMLInputElement;
@@ -119,6 +121,28 @@ export class PlantView {
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.onchange = function () {
controller.configChanged()

View File

@@ -1,5 +1,5 @@
import { Controller } from "./main";
import {DetectionResult, TankConfig, TankInfo} from "./api";
import {Detection, TankConfig, TankInfo} from "./api";
export class TankConfigView {
private readonly tank_useable_ml: HTMLInputElement;