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.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -68,10 +68,12 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flexcontainer-rev {
|
||||
display: flex;
|
||||
flex-wrap: wrap-reverse;
|
||||
}
|
||||
|
||||
.subcontainer {
|
||||
min-width: 300px;
|
||||
max-width: 900px;
|
||||
@@ -80,6 +82,7 @@
|
||||
border-width: 1px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.subcontainercontainer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@@ -91,6 +94,7 @@
|
||||
border-width: 1px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
@media (min-width: 350px) {
|
||||
.plantcontainer {
|
||||
flex-grow: 1;
|
||||
@@ -100,6 +104,7 @@
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1100px) {
|
||||
.plantcontainer {
|
||||
flex-grow: 1;
|
||||
@@ -109,6 +114,7 @@
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 2150px) {
|
||||
.plantcontainer {
|
||||
flex-grow: 1;
|
||||
@@ -120,7 +126,6 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
.plantlist {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -133,7 +138,6 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -164,7 +168,9 @@
|
||||
<h3>Plants:</h3>
|
||||
<button id="measure_moisture">Measure Moisture</button>
|
||||
<button id="detect_sensors" style="display:none">Detect/Test Sensors</button>
|
||||
<input id="can_power" type="checkbox">Power CAN</input>
|
||||
<input id="can_power" type="checkbox"><label for="can_power">Power CAN</label>
|
||||
<input id="auto_refresh_moisture_sensors" type="checkbox"><label for="auto_refresh_moisture_sensors">Auto Refresh
|
||||
Moisture/Sensors</label>
|
||||
<div id="plants" class="plantlist"></div>
|
||||
|
||||
<div class="flexcontainer-rev">
|
||||
@@ -175,7 +181,6 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<button id="exit">Exit</button>
|
||||
<button id="reboot">Reboot</button>
|
||||
|
||||
@@ -187,9 +192,11 @@
|
||||
</div>
|
||||
|
||||
<div id="progressPane" class="progressPane">
|
||||
<div class="progressSpacer"></div>>
|
||||
<div class="progressSpacer"></div>
|
||||
>
|
||||
<div id="progressPaneBar" class="progress" data-label="50% Complete">
|
||||
<span id="progressPaneSpan" class="value" style="width:100%;"></span>
|
||||
</div>
|
||||
<div class="progressSpacer"></div>>
|
||||
<div class="progressSpacer"></div>
|
||||
>
|
||||
</div>
|
||||
@@ -362,16 +362,21 @@ export class Controller {
|
||||
)
|
||||
}
|
||||
|
||||
async detectSensors(detection: Detection) {
|
||||
async detectSensors(detection: Detection, silent: boolean = false) {
|
||||
let counter = 0
|
||||
let limit = 5
|
||||
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++;
|
||||
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);
|
||||
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);
|
||||
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
|
||||
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++;
|
||||
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);
|
||||
if (!silent) {
|
||||
controller.progressview.removeProgress("measure_moisture");
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
clearTimeout(timerId);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -57,7 +59,6 @@ export class PlantViews {
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class PlantView {
|
||||
private readonly moistureSensorMinFrequency: HTMLInputElement;
|
||||
private readonly moistureSensorMaxFrequency: HTMLInputElement;
|
||||
|
||||
Reference in New Issue
Block a user