Implement proper stepper idling. Should close #3
This commit is contained in:
@@ -92,11 +92,13 @@ Name: WijiBoard
|
||||
| `HOMEALL` | Home both motors simultaneously |
|
||||
| `HOME1` | Home motor 1 |
|
||||
| `HOME2` | Home motor 2 |
|
||||
| `DISABLE` | Disable steppers (un-hold) and require rehoming. Auto-triggers after 30 mins (`30 * 60 * 1000` ms) of inactivity |
|
||||
| `POS` | Request current positions (triggers NOTIFY) |
|
||||
|
||||
### Status notifications (firmware → browser)
|
||||
|
||||
`P:<s1>,<s2>` — current step positions for motor 1 and 2, sent every 200 ms while moving.
|
||||
`SYS:IDLE_TIMEOUT` — Sent when motors are automatically disabled due to 30 mins of inactivity.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+36
@@ -86,6 +86,14 @@
|
||||
|
||||
<!-- BLE actions -->
|
||||
<div class="topbar-actions">
|
||||
<!-- Disable steppers button (hidden by default) -->
|
||||
<button class="btn btn-sm btn-ghost" id="btn-disable-steppers" style="display:none; margin-right:8px; padding:0 8px; color: var(--danger-color);" title="Disable Motors (Requires Rehoming)" aria-label="Disable Steppers">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18.36 6.64a9 9 0 1 1-12.73 0"></path>
|
||||
<line x1="12" y1="2" x2="12" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- BLE status pill -->
|
||||
<div class="ble-status" id="ble-status" aria-live="polite">
|
||||
<div class="ble-dot" id="ble-dot"></div>
|
||||
@@ -202,6 +210,34 @@
|
||||
window.dispatchEvent(new Event('pwa-installable'));
|
||||
});
|
||||
|
||||
// ── Disable Steppers & Idle Timeout Logic ──────────────────────
|
||||
const btnDisable = document.getElementById('btn-disable-steppers');
|
||||
btnDisable.addEventListener('click', async () => {
|
||||
if (BLE.isConnected()) {
|
||||
await BLE.write('DISABLE').catch(e => UI.log(e.message, 'error'));
|
||||
localStorage.setItem('wiji_homed', 'false');
|
||||
UI.log('Steppers manually disabled. Rehoming required.', 'warn');
|
||||
const warningEl = document.getElementById('home-warning');
|
||||
if (warningEl) warningEl.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('ble:status', (e) => {
|
||||
if (e.detail === 'SYS:IDLE_TIMEOUT') {
|
||||
localStorage.setItem('wiji_homed', 'false');
|
||||
UI.log('Steppers disabled due to 30 mins inactivity. Rehoming required.', 'warn');
|
||||
const warningEl = document.getElementById('home-warning');
|
||||
if (warningEl) warningEl.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
BLE.on('connected', () => {
|
||||
btnDisable.style.display = 'inline-flex';
|
||||
});
|
||||
BLE.on('disconnected', () => {
|
||||
btnDisable.style.display = 'none';
|
||||
});
|
||||
|
||||
// ── Check Web Bluetooth availability ──────────────────────────
|
||||
if (!navigator.bluetooth) {
|
||||
setTimeout(() => {
|
||||
|
||||
+30
-5
@@ -15,6 +15,7 @@
|
||||
* HOME1 → Home motor 1 (Right)
|
||||
* HOME2 → Home motor 2 (Left)
|
||||
* HOMEALL → Home both motors simultaneously
|
||||
* DISABLE → Disable steppers (un-hold) and require rehoming
|
||||
* POS → Request current positions (triggers NOTIFY)
|
||||
*
|
||||
* BLE Status Characteristic (NOTIFY):
|
||||
@@ -72,6 +73,11 @@ bool bleConnected = false;
|
||||
unsigned long lastNotify = 0;
|
||||
const unsigned long NOTIFY_INTERVAL_MS = 200;
|
||||
|
||||
// ─── Idle timeout tracker ─────────────────────────────────────────
|
||||
unsigned long lastMotorActive = 0;
|
||||
const unsigned long IDLE_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
||||
bool motorsDisabled = true; // Assume disabled at startup until moved
|
||||
|
||||
// ─── Forward declarations ─────────────────────────────────────────
|
||||
void parseCommand(const String &cmd);
|
||||
void sendPosition();
|
||||
@@ -193,6 +199,14 @@ void parseCommand(const String &cmd) {
|
||||
sendPosition();
|
||||
return;
|
||||
}
|
||||
// DISABLE
|
||||
if (cmd == "DISABLE") {
|
||||
stepper1.disableOutputs();
|
||||
stepper2.disableOutputs();
|
||||
motorsDisabled = true;
|
||||
Serial.println("[SYS] Steppers manually disabled.");
|
||||
return;
|
||||
}
|
||||
// POS – explicit position request
|
||||
if (cmd == "POS") {
|
||||
sendPosition();
|
||||
@@ -280,6 +294,22 @@ void loop() {
|
||||
stepper1.run();
|
||||
stepper2.run();
|
||||
|
||||
bool moving = stepper1.isRunning() || stepper2.isRunning() || homingState != HOME_IDLE;
|
||||
|
||||
if (moving) {
|
||||
lastMotorActive = millis();
|
||||
motorsDisabled = false;
|
||||
} else if (!motorsDisabled && (millis() - lastMotorActive > IDLE_TIMEOUT_MS)) {
|
||||
stepper1.disableOutputs();
|
||||
stepper2.disableOutputs();
|
||||
motorsDisabled = true;
|
||||
Serial.println("[SYS] Idle timeout reached. Steppers disabled.");
|
||||
if (bleConnected && pStatusChar) {
|
||||
pStatusChar->setValue("SYS:IDLE_TIMEOUT");
|
||||
pStatusChar->notify();
|
||||
}
|
||||
}
|
||||
|
||||
if (homingState == HOME_ALL_PHASE1) {
|
||||
if (stepper1.distanceToGo() == 0 && stepper2.distanceToGo() == 0) {
|
||||
stepper1.setCurrentPosition(1024);
|
||||
@@ -291,8 +321,6 @@ void loop() {
|
||||
}
|
||||
} else if (homingState == HOME_ALL_PHASE2) {
|
||||
if (stepper1.distanceToGo() == 0 && stepper2.distanceToGo() == 0) {
|
||||
stepper1.disableOutputs();
|
||||
stepper2.disableOutputs();
|
||||
stepper1.setMaxSpeed(currentMaxSpeed);
|
||||
stepper2.setMaxSpeed(currentMaxSpeed);
|
||||
stepper1.setAcceleration(currentAccel);
|
||||
@@ -309,7 +337,6 @@ void loop() {
|
||||
}
|
||||
} else if (homingState == HOME_1_PHASE2) {
|
||||
if (stepper1.distanceToGo() == 0) {
|
||||
stepper1.disableOutputs();
|
||||
stepper1.setMaxSpeed(currentMaxSpeed);
|
||||
stepper1.setAcceleration(currentAccel);
|
||||
homingState = HOME_IDLE;
|
||||
@@ -322,7 +349,6 @@ void loop() {
|
||||
}
|
||||
} else if (homingState == HOME_2_PHASE2) {
|
||||
if (stepper2.distanceToGo() == 0) {
|
||||
stepper2.disableOutputs();
|
||||
stepper2.setMaxSpeed(currentMaxSpeed);
|
||||
stepper2.setAcceleration(currentAccel);
|
||||
homingState = HOME_IDLE;
|
||||
@@ -332,7 +358,6 @@ void loop() {
|
||||
// Periodic position notify while motors are moving
|
||||
if (bleConnected) {
|
||||
unsigned long now = millis();
|
||||
bool moving = stepper1.isRunning() || stepper2.isRunning() || homingState != HOME_IDLE;
|
||||
if (moving && (now - lastNotify >= NOTIFY_INTERVAL_MS)) {
|
||||
lastNotify = now;
|
||||
sendPosition();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* SPD:<n> → set max speed (steps/sec)
|
||||
* ACC:<n> → set acceleration (steps/sec²)
|
||||
* HOME → zero both steppers
|
||||
* DISABLE → disable steppers (un-hold)
|
||||
* POS → request position report
|
||||
*
|
||||
* Status characteristic (NOTIFY):
|
||||
|
||||
@@ -15,7 +15,7 @@ const HomeSection = {
|
||||
<div class="section-page fade-in">
|
||||
<div class="section-header">
|
||||
<h2>WijiBoard</h2>
|
||||
<p>WiFi Spirit Board — SCARA arm controller. Select a section from the sidebar to get started.</p>
|
||||
<p>Bluetooth Spirit Board — SCARA arm controller. Select a section from the sidebar to get started.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid-2" style="margin-bottom:16px">
|
||||
|
||||
Reference in New Issue
Block a user