From 7121dd0fae96cd4fac734cea9ce3fed183aa25d2 Mon Sep 17 00:00:00 2001 From: Empire Phoenix Date: Sun, 29 Mar 2026 14:21:12 +0200 Subject: [PATCH] Add silent mode for sensor detection and moisture measurement - Introduced the `silent` parameter to prevent UI progress updates during automatic operations. - Enhanced CAN robustness with improved bus-off management, retransmission settings, and jitter tolerance. - Added auto-refresh functionality for plant moisture and sensor detection with configurable enablement. --- Software/CAN_Sensor/src/main.rs | 17 + .../MainBoard/rust/src_webpack/src/main.html | 333 +++++++++--------- .../MainBoard/rust/src_webpack/src/main.ts | 85 ++++- .../MainBoard/rust/src_webpack/src/plant.ts | 33 +- 4 files changed, 274 insertions(+), 194 deletions(-) diff --git a/Software/CAN_Sensor/src/main.rs b/Software/CAN_Sensor/src/main.rs index 1baa813..e3349fc 100644 --- a/Software/CAN_Sensor/src/main.rs +++ b/Software/CAN_Sensor/src/main.rs @@ -248,6 +248,23 @@ async fn main(spawner: Spawner) { ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2)); can.add_filter(CanFilter::accept_all()); + + // Improve CAN robustness for longer cables: + // 1. Enable Automatic Bus-Off Management (ABOM) + // 2. Ensure No Automatic Retransmission (NART) is DISABLED (i.e. we WANT retransmission) + // 3. Enable Receive FIFO Overwrite Mode (RFLM = 0, default) + // 4. Increase Resync Jump Width (SJW) if possible by patching BTIMR + hal::pac::CAN1.ctlr().modify(|w| { + w.set_abom(true); + w.set_nart(false); // HAL default is usually false, but let's be explicit + }); + + // SJW is bits 24-25 of BTIMR. HAL sets it to 0 (SJW=1). + // Let's try to set it to 3 (SJW=4) for better jitter tolerance. + hal::pac::CAN1.btimr().modify(|w| { + w.set_sjw(3); // 3 means 4TQ + }); + // let mut filter = CanFilter::new_id_list(); // filter.get(0).unwrap().set(Id::Standard(standard_identify_id), Default::default()); // can.add_filter(filter); diff --git a/Software/MainBoard/rust/src_webpack/src/main.html b/Software/MainBoard/rust/src_webpack/src/main.html index 5821fd9..f8c2730 100644 --- a/Software/MainBoard/rust/src_webpack/src/main.html +++ b/Software/MainBoard/rust/src_webpack/src/main.html @@ -1,195 +1,202 @@
-
-
-
- -
-
+
+
-
+ +
+
+
+
+
+
+
+
+
-
+ +
+
+
+
+
+
-
+ +

Plants:

+ + + + +
+ +
+
+
+
+
-
-
-
-
+ + + + +
+
-
-
-
-

Plants:

- - - Power CAN -
- -
-
-
-
-
-
- - - - - - -
-
-
- - +
-
> -
- -
-
> +
+ > +
+ +
+
+ >
\ No newline at end of file diff --git a/Software/MainBoard/rust/src_webpack/src/main.ts b/Software/MainBoard/rust/src_webpack/src/main.ts index eb90312..5469923 100644 --- a/Software/MainBoard/rust/src_webpack/src/main.ts +++ b/Software/MainBoard/rust/src_webpack/src/main.ts @@ -362,16 +362,21 @@ export class Controller { ) } - async detectSensors(detection: Detection) { + async detectSensors(detection: Detection, silent: boolean = false) { let counter = 0 let limit = 5 - controller.progressview.addProgress("detect_sensors", counter / limit * 100, "Detecting sensors " + (limit - counter) + "s") + if (!silent) { + controller.progressview.addProgress("detect_sensors", counter / limit * 100, "Detecting sensors " + (limit - counter) + "s") + } + let timerId: string | number | NodeJS.Timeout | undefined function updateProgress() { counter++; - controller.progressview.addProgress("detect_sensors", counter / limit * 100, "Detecting sensors " + (limit - counter) + "s") + if (!silent) { + controller.progressview.addProgress("detect_sensors", counter / limit * 100, "Detecting sensors " + (limit - counter) + "s") + } timerId = setTimeout(updateProgress, 1000); } @@ -379,12 +384,15 @@ export class Controller { var pretty = JSON.stringify(detection, undefined, 1); - fetch(PUBLIC_URL + "/detect_sensors", {method: "POST", body: pretty}) + return fetch(PUBLIC_URL + "/detect_sensors", {method: "POST", body: pretty}) .then(response => response.json()) .then(json => json as Detection) .then(json => { clearTimeout(timerId); - controller.progressview.removeProgress("detect_sensors"); + if (!silent) { + controller.progressview.removeProgress("detect_sensors"); + } + const pretty = JSON.stringify(json); toast.info("Detection result: " + pretty); console.log(pretty); @@ -393,7 +401,9 @@ export class Controller { }) .catch(error => { clearTimeout(timerId); - controller.progressview.removeProgress("detect_sensors"); + if (!silent) { + controller.progressview.removeProgress("detect_sensors"); + } toast.error("Autodetect failed: " + error); }); } @@ -426,7 +436,7 @@ export class Controller { timerId = setTimeout(updateProgress, 1000); - var ajax = new XMLHttpRequest(); + const ajax = new XMLHttpRequest(); ajax.responseType = 'json'; ajax.onreadystatechange = () => { if (ajax.readyState === 4) { @@ -435,7 +445,7 @@ export class Controller { this.networkView.setScanResult(ajax.response as SSIDList) } }; - ajax.onerror = (evt) => { + ajax.onerror = (_) => { clearTimeout(timerId); controller.progressview.removeProgress("scan_ssid"); alert("Failed to start see console") @@ -459,16 +469,22 @@ export class Controller { this.hardwareView.setConfig(current.hardware); } - measure_moisture() { + measure_moisture(silent: boolean = false) { let counter = 0 let limit = 2 - controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s") + if (!silent) { + controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s") + } + let timerId: string | number | NodeJS.Timeout | undefined function updateProgress() { counter++; - controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s") + if (!silent) { + controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s") + } + timerId = setTimeout(updateProgress, 1000); } @@ -476,17 +492,22 @@ export class Controller { timerId = setTimeout(updateProgress, 1000); - fetch(PUBLIC_URL + "/moisture") + return fetch(PUBLIC_URL + "/moisture") .then(response => response.json()) .then(json => json as Moistures) .then(time => { controller.plantViews.update(time.moisture_a, time.moisture_b) clearTimeout(timerId); - controller.progressview.removeProgress("measure_moisture"); + if (!silent) { + controller.progressview.removeProgress("measure_moisture"); + } + }) .catch(error => { clearTimeout(timerId); - controller.progressview.removeProgress("measure_moisture"); + if (!silent) { + controller.progressview.removeProgress("measure_moisture"); + } console.log(error); }); } @@ -516,7 +537,7 @@ export class Controller { }, 2000); } }) - .catch(err => { + .catch(_ => { console.log("Not reached yet, retrying") setTimeout(controller.waitForReboot, 1000) }) @@ -560,6 +581,8 @@ export class Controller { readonly logView: LogView readonly detectBtn: HTMLButtonElement readonly can_power: HTMLInputElement; + readonly auto_refresh_moisture_sensors: HTMLInputElement; + private auto_refresh_timer: NodeJS.Timeout | undefined; constructor() { this.timeView = new TimeView(this) @@ -597,6 +620,38 @@ export class Controller { this.can_power.onchange = () => { controller.setCanPower(this.can_power.checked); } + this.auto_refresh_moisture_sensors = document.getElementById("auto_refresh_moisture_sensors") as HTMLInputElement + this.auto_refresh_moisture_sensors.onchange = () => { + if (this.auto_refresh_timer) { + clearTimeout(this.auto_refresh_timer) + } + if (this.auto_refresh_moisture_sensors.checked) { + this.autoRefreshLoop() + } + } + } + + private async autoRefreshLoop() { + if (!this.auto_refresh_moisture_sensors.checked) { + return; + } + + try { + await this.measure_moisture(true); + const detection: Detection = { + plant: Array.from({length: PLANT_COUNT}, () => ({ + sensor_a: true, + sensor_b: true, + })), + }; + await this.detectSensors(detection, true); + } catch (e) { + console.error("Auto-refresh error", e); + } + + if (this.auto_refresh_moisture_sensors.checked) { + this.auto_refresh_timer = setTimeout(() => this.autoRefreshLoop(), 1000); + } } diff --git a/Software/MainBoard/rust/src_webpack/src/plant.ts b/Software/MainBoard/rust/src_webpack/src/plant.ts index 8f83f1a..702e282 100644 --- a/Software/MainBoard/rust/src_webpack/src/plant.ts +++ b/Software/MainBoard/rust/src_webpack/src/plant.ts @@ -12,7 +12,9 @@ export class PlantViews { constructor(syncConfig: Controller) { this.measure_moisture = document.getElementById("measure_moisture") as HTMLButtonElement - this.measure_moisture.onclick = syncConfig.measure_moisture + this.measure_moisture.onclick = async () => { + return syncConfig.measure_moisture(false) + } this.plantsDiv = document.getElementById("plants") as HTMLDivElement; for (let plantId = 0; plantId < PLANT_COUNT; plantId++) { this.plants[plantId] = new PlantView(plantId, this.plantsDiv, syncConfig); @@ -48,16 +50,15 @@ export class PlantViews { plantView.setTestResult(response) } - applyDetectionResult(json: Detection) { - for (let i = 0; i < PLANT_COUNT; i++) { - var plantResult = json.plant[i]; - this.plants[i].setDetectionResult(plantResult); - } - } + applyDetectionResult(json: Detection) { + for (let i = 0; i < PLANT_COUNT; i++) { + var plantResult = json.plant[i]; + this.plants[i].setDetectionResult(plantResult); + } + } } - export class PlantView { private readonly moistureSensorMinFrequency: HTMLInputElement; private readonly moistureSensorMaxFrequency: HTMLInputElement; @@ -240,10 +241,10 @@ export class PlantView { } updateVisibility(plantConfig: PlantConfig) { - let sensorOnly = document.getElementsByClassName("plantSensorEnabledOnly_"+ this.plantId) - let pumpOnly = document.getElementsByClassName("plantPumpEnabledOnly_"+ this.plantId) - let targetOnly = document.getElementsByClassName("plantTargetEnabledOnly_"+ this.plantId) - let minOnly = document.getElementsByClassName("plantMinEnabledOnly_"+ this.plantId) + let sensorOnly = document.getElementsByClassName("plantSensorEnabledOnly_" + this.plantId) + let pumpOnly = document.getElementsByClassName("plantPumpEnabledOnly_" + this.plantId) + let targetOnly = document.getElementsByClassName("plantTargetEnabledOnly_" + this.plantId) + let minOnly = document.getElementsByClassName("plantMinEnabledOnly_" + this.plantId) console.log("updateVisibility plantConfig: " + plantConfig.mode) let showSensor = plantConfig.sensor_a || plantConfig.sensor_b @@ -259,7 +260,7 @@ export class PlantView { // 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)) { // if (showSensor) { @@ -336,7 +337,7 @@ export class PlantView { getConfig(): PlantConfig { - let conv: PlantConfig = { + let conv: PlantConfig = { mode: this.mode.value, target_moisture: this.targetMoisture.valueAsNumber, min_moisture: this.minMoisture.valueAsNumber, @@ -361,11 +362,11 @@ export class PlantView { setDetectionResult(plantResult: DetectionPlant) { console.log("setDetectionResult plantResult: " + plantResult.sensor_a + " " + plantResult.sensor_b) var changed = false; - if (this.sensorAInstalled.checked != plantResult.sensor_a){ + if (this.sensorAInstalled.checked != plantResult.sensor_a) { changed = true; this.sensorAInstalled.checked = plantResult.sensor_a; } - if (this.sensorBInstalled.checked != plantResult.sensor_b){ + if (this.sensorBInstalled.checked != plantResult.sensor_b) { changed = true; this.sensorBInstalled.checked = plantResult.sensor_b; }