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));
|
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
||||||
|
|
||||||
can.add_filter(CanFilter::accept_all());
|
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();
|
// 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);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progressSpacer{
|
.progressSpacer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,10 +68,12 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
.flexcontainer-rev{
|
|
||||||
|
.flexcontainer-rev {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap-reverse;
|
flex-wrap: wrap-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subcontainer {
|
.subcontainer {
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
@@ -80,7 +82,8 @@
|
|||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
.subcontainercontainer{
|
|
||||||
|
.subcontainercontainer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +94,7 @@
|
|||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 350px) {
|
@media (min-width: 350px) {
|
||||||
.plantcontainer {
|
.plantcontainer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@@ -100,6 +104,7 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1100px) {
|
@media (min-width: 1100px) {
|
||||||
.plantcontainer {
|
.plantcontainer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@@ -109,6 +114,7 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 2150px) {
|
@media (min-width: 2150px) {
|
||||||
.plantcontainer {
|
.plantcontainer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@@ -120,7 +126,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.plantlist {
|
.plantlist {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -133,7 +138,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
@@ -164,18 +168,19 @@
|
|||||||
<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>
|
<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 id="plants" class="plantlist"></div>
|
||||||
|
|
||||||
<div class="flexcontainer-rev">
|
<div class="flexcontainer-rev">
|
||||||
<div id = "submitview" class="subcontainer">
|
<div id="submitview" class="subcontainer">
|
||||||
</div>
|
</div>
|
||||||
<div id="fileview" class="subcontainer">
|
<div id="fileview" class="subcontainer">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button id="exit">Exit</button>
|
<button id="exit">Exit</button>
|
||||||
<button id="reboot">Reboot</button>
|
<button id="reboot">Reboot</button>
|
||||||
|
|
||||||
@@ -187,9 +192,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="progressPane" class="progressPane">
|
<div id="progressPane" class="progressPane">
|
||||||
<div class="progressSpacer"></div>>
|
<div class="progressSpacer"></div>
|
||||||
|
>
|
||||||
<div id="progressPaneBar" class="progress" data-label="50% Complete">
|
<div id="progressPaneBar" class="progress" data-label="50% Complete">
|
||||||
<span id="progressPaneSpan" class="value" style="width:100%;"></span>
|
<span id="progressPaneSpan" class="value" style="width:100%;"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="progressSpacer"></div>>
|
<div class="progressSpacer"></div>
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -362,16 +362,21 @@ export class Controller {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async detectSensors(detection: Detection) {
|
async detectSensors(detection: Detection, silent: boolean = false) {
|
||||||
let counter = 0
|
let counter = 0
|
||||||
let limit = 5
|
let limit = 5
|
||||||
|
if (!silent) {
|
||||||
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let timerId: string | number | NodeJS.Timeout | undefined
|
let timerId: string | number | NodeJS.Timeout | undefined
|
||||||
|
|
||||||
function updateProgress() {
|
function updateProgress() {
|
||||||
counter++;
|
counter++;
|
||||||
|
if (!silent) {
|
||||||
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")
|
||||||
|
}
|
||||||
timerId = setTimeout(updateProgress, 1000);
|
timerId = setTimeout(updateProgress, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,12 +384,15 @@ export class Controller {
|
|||||||
|
|
||||||
var pretty = JSON.stringify(detection, undefined, 1);
|
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(response => response.json())
|
||||||
.then(json => json as Detection)
|
.then(json => json as Detection)
|
||||||
.then(json => {
|
.then(json => {
|
||||||
clearTimeout(timerId);
|
clearTimeout(timerId);
|
||||||
|
if (!silent) {
|
||||||
controller.progressview.removeProgress("detect_sensors");
|
controller.progressview.removeProgress("detect_sensors");
|
||||||
|
}
|
||||||
|
|
||||||
const pretty = JSON.stringify(json);
|
const pretty = JSON.stringify(json);
|
||||||
toast.info("Detection result: " + pretty);
|
toast.info("Detection result: " + pretty);
|
||||||
console.log(pretty);
|
console.log(pretty);
|
||||||
@@ -393,7 +401,9 @@ export class Controller {
|
|||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
clearTimeout(timerId);
|
clearTimeout(timerId);
|
||||||
|
if (!silent) {
|
||||||
controller.progressview.removeProgress("detect_sensors");
|
controller.progressview.removeProgress("detect_sensors");
|
||||||
|
}
|
||||||
toast.error("Autodetect failed: " + error);
|
toast.error("Autodetect failed: " + error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -426,7 +436,7 @@ export class Controller {
|
|||||||
timerId = setTimeout(updateProgress, 1000);
|
timerId = setTimeout(updateProgress, 1000);
|
||||||
|
|
||||||
|
|
||||||
var ajax = new XMLHttpRequest();
|
const ajax = new XMLHttpRequest();
|
||||||
ajax.responseType = 'json';
|
ajax.responseType = 'json';
|
||||||
ajax.onreadystatechange = () => {
|
ajax.onreadystatechange = () => {
|
||||||
if (ajax.readyState === 4) {
|
if (ajax.readyState === 4) {
|
||||||
@@ -435,7 +445,7 @@ export class Controller {
|
|||||||
this.networkView.setScanResult(ajax.response as SSIDList)
|
this.networkView.setScanResult(ajax.response as SSIDList)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ajax.onerror = (evt) => {
|
ajax.onerror = (_) => {
|
||||||
clearTimeout(timerId);
|
clearTimeout(timerId);
|
||||||
controller.progressview.removeProgress("scan_ssid");
|
controller.progressview.removeProgress("scan_ssid");
|
||||||
alert("Failed to start see console")
|
alert("Failed to start see console")
|
||||||
@@ -459,16 +469,22 @@ export class Controller {
|
|||||||
this.hardwareView.setConfig(current.hardware);
|
this.hardwareView.setConfig(current.hardware);
|
||||||
}
|
}
|
||||||
|
|
||||||
measure_moisture() {
|
measure_moisture(silent: boolean = false) {
|
||||||
let counter = 0
|
let counter = 0
|
||||||
let limit = 2
|
let limit = 2
|
||||||
|
if (!silent) {
|
||||||
controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s")
|
controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let timerId: string | number | NodeJS.Timeout | undefined
|
let timerId: string | number | NodeJS.Timeout | undefined
|
||||||
|
|
||||||
function updateProgress() {
|
function updateProgress() {
|
||||||
counter++;
|
counter++;
|
||||||
|
if (!silent) {
|
||||||
controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s")
|
controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s")
|
||||||
|
}
|
||||||
|
|
||||||
timerId = setTimeout(updateProgress, 1000);
|
timerId = setTimeout(updateProgress, 1000);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -476,17 +492,22 @@ export class Controller {
|
|||||||
timerId = setTimeout(updateProgress, 1000);
|
timerId = setTimeout(updateProgress, 1000);
|
||||||
|
|
||||||
|
|
||||||
fetch(PUBLIC_URL + "/moisture")
|
return fetch(PUBLIC_URL + "/moisture")
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(json => json as Moistures)
|
.then(json => json as Moistures)
|
||||||
.then(time => {
|
.then(time => {
|
||||||
controller.plantViews.update(time.moisture_a, time.moisture_b)
|
controller.plantViews.update(time.moisture_a, time.moisture_b)
|
||||||
clearTimeout(timerId);
|
clearTimeout(timerId);
|
||||||
|
if (!silent) {
|
||||||
controller.progressview.removeProgress("measure_moisture");
|
controller.progressview.removeProgress("measure_moisture");
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
clearTimeout(timerId);
|
clearTimeout(timerId);
|
||||||
|
if (!silent) {
|
||||||
controller.progressview.removeProgress("measure_moisture");
|
controller.progressview.removeProgress("measure_moisture");
|
||||||
|
}
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -516,7 +537,7 @@ export class Controller {
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(_ => {
|
||||||
console.log("Not reached yet, retrying")
|
console.log("Not reached yet, retrying")
|
||||||
setTimeout(controller.waitForReboot, 1000)
|
setTimeout(controller.waitForReboot, 1000)
|
||||||
})
|
})
|
||||||
@@ -560,6 +581,8 @@ export class Controller {
|
|||||||
readonly logView: LogView
|
readonly logView: LogView
|
||||||
readonly detectBtn: HTMLButtonElement
|
readonly detectBtn: HTMLButtonElement
|
||||||
readonly can_power: HTMLInputElement;
|
readonly can_power: HTMLInputElement;
|
||||||
|
readonly auto_refresh_moisture_sensors: HTMLInputElement;
|
||||||
|
private auto_refresh_timer: NodeJS.Timeout | undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.timeView = new TimeView(this)
|
this.timeView = new TimeView(this)
|
||||||
@@ -597,6 +620,38 @@ export class Controller {
|
|||||||
this.can_power.onchange = () => {
|
this.can_power.onchange = () => {
|
||||||
controller.setCanPower(this.can_power.checked);
|
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) {
|
constructor(syncConfig: Controller) {
|
||||||
this.measure_moisture = document.getElementById("measure_moisture") as HTMLButtonElement
|
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;
|
this.plantsDiv = document.getElementById("plants") as HTMLDivElement;
|
||||||
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
|
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
|
||||||
this.plants[plantId] = new PlantView(plantId, this.plantsDiv, syncConfig);
|
this.plants[plantId] = new PlantView(plantId, this.plantsDiv, syncConfig);
|
||||||
@@ -57,7 +59,6 @@ export class PlantViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class PlantView {
|
export class PlantView {
|
||||||
private readonly moistureSensorMinFrequency: HTMLInputElement;
|
private readonly moistureSensorMinFrequency: HTMLInputElement;
|
||||||
private readonly moistureSensorMaxFrequency: HTMLInputElement;
|
private readonly moistureSensorMaxFrequency: HTMLInputElement;
|
||||||
@@ -240,10 +241,10 @@ export class PlantView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateVisibility(plantConfig: PlantConfig) {
|
updateVisibility(plantConfig: PlantConfig) {
|
||||||
let sensorOnly = document.getElementsByClassName("plantSensorEnabledOnly_"+ this.plantId)
|
let sensorOnly = document.getElementsByClassName("plantSensorEnabledOnly_" + this.plantId)
|
||||||
let pumpOnly = document.getElementsByClassName("plantPumpEnabledOnly_"+ this.plantId)
|
let pumpOnly = document.getElementsByClassName("plantPumpEnabledOnly_" + this.plantId)
|
||||||
let targetOnly = document.getElementsByClassName("plantTargetEnabledOnly_"+ this.plantId)
|
let targetOnly = document.getElementsByClassName("plantTargetEnabledOnly_" + this.plantId)
|
||||||
let minOnly = document.getElementsByClassName("plantMinEnabledOnly_"+ this.plantId)
|
let minOnly = document.getElementsByClassName("plantMinEnabledOnly_" + this.plantId)
|
||||||
|
|
||||||
console.log("updateVisibility plantConfig: " + plantConfig.mode)
|
console.log("updateVisibility plantConfig: " + plantConfig.mode)
|
||||||
let showSensor = plantConfig.sensor_a || plantConfig.sensor_b
|
let showSensor = plantConfig.sensor_a || plantConfig.sensor_b
|
||||||
@@ -259,7 +260,7 @@ export class PlantView {
|
|||||||
// 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) {
|
||||||
@@ -361,11 +362,11 @@ export class PlantView {
|
|||||||
setDetectionResult(plantResult: DetectionPlant) {
|
setDetectionResult(plantResult: DetectionPlant) {
|
||||||
console.log("setDetectionResult plantResult: " + plantResult.sensor_a + " " + plantResult.sensor_b)
|
console.log("setDetectionResult plantResult: " + plantResult.sensor_a + " " + plantResult.sensor_b)
|
||||||
var changed = false;
|
var changed = false;
|
||||||
if (this.sensorAInstalled.checked != plantResult.sensor_a){
|
if (this.sensorAInstalled.checked != plantResult.sensor_a) {
|
||||||
changed = true;
|
changed = true;
|
||||||
this.sensorAInstalled.checked = plantResult.sensor_a;
|
this.sensorAInstalled.checked = plantResult.sensor_a;
|
||||||
}
|
}
|
||||||
if (this.sensorBInstalled.checked != plantResult.sensor_b){
|
if (this.sensorBInstalled.checked != plantResult.sensor_b) {
|
||||||
changed = true;
|
changed = true;
|
||||||
this.sensorBInstalled.checked = plantResult.sensor_b;
|
this.sensorBInstalled.checked = plantResult.sensor_b;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user