Implement proper stepper idling. Should close #3
This commit is contained in:
@@ -92,11 +92,13 @@ Name: WijiBoard
|
|||||||
| `HOMEALL` | Home both motors simultaneously |
|
| `HOMEALL` | Home both motors simultaneously |
|
||||||
| `HOME1` | Home motor 1 |
|
| `HOME1` | Home motor 1 |
|
||||||
| `HOME2` | Home motor 2 |
|
| `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) |
|
| `POS` | Request current positions (triggers NOTIFY) |
|
||||||
|
|
||||||
### Status notifications (firmware → browser)
|
### Status notifications (firmware → browser)
|
||||||
|
|
||||||
`P:<s1>,<s2>` — current step positions for motor 1 and 2, sent every 200 ms while moving.
|
`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 -->
|
<!-- BLE actions -->
|
||||||
<div class="topbar-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 -->
|
<!-- BLE status pill -->
|
||||||
<div class="ble-status" id="ble-status" aria-live="polite">
|
<div class="ble-status" id="ble-status" aria-live="polite">
|
||||||
<div class="ble-dot" id="ble-dot"></div>
|
<div class="ble-dot" id="ble-dot"></div>
|
||||||
@@ -202,6 +210,34 @@
|
|||||||
window.dispatchEvent(new Event('pwa-installable'));
|
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 ──────────────────────────
|
// ── Check Web Bluetooth availability ──────────────────────────
|
||||||
if (!navigator.bluetooth) {
|
if (!navigator.bluetooth) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
+30
-5
@@ -15,6 +15,7 @@
|
|||||||
* HOME1 → Home motor 1 (Right)
|
* HOME1 → Home motor 1 (Right)
|
||||||
* HOME2 → Home motor 2 (Left)
|
* HOME2 → Home motor 2 (Left)
|
||||||
* HOMEALL → Home both motors simultaneously
|
* HOMEALL → Home both motors simultaneously
|
||||||
|
* DISABLE → Disable steppers (un-hold) and require rehoming
|
||||||
* POS → Request current positions (triggers NOTIFY)
|
* POS → Request current positions (triggers NOTIFY)
|
||||||
*
|
*
|
||||||
* BLE Status Characteristic (NOTIFY):
|
* BLE Status Characteristic (NOTIFY):
|
||||||
@@ -72,6 +73,11 @@ bool bleConnected = false;
|
|||||||
unsigned long lastNotify = 0;
|
unsigned long lastNotify = 0;
|
||||||
const unsigned long NOTIFY_INTERVAL_MS = 200;
|
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 ─────────────────────────────────────────
|
// ─── Forward declarations ─────────────────────────────────────────
|
||||||
void parseCommand(const String &cmd);
|
void parseCommand(const String &cmd);
|
||||||
void sendPosition();
|
void sendPosition();
|
||||||
@@ -193,6 +199,14 @@ void parseCommand(const String &cmd) {
|
|||||||
sendPosition();
|
sendPosition();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// DISABLE
|
||||||
|
if (cmd == "DISABLE") {
|
||||||
|
stepper1.disableOutputs();
|
||||||
|
stepper2.disableOutputs();
|
||||||
|
motorsDisabled = true;
|
||||||
|
Serial.println("[SYS] Steppers manually disabled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
// POS – explicit position request
|
// POS – explicit position request
|
||||||
if (cmd == "POS") {
|
if (cmd == "POS") {
|
||||||
sendPosition();
|
sendPosition();
|
||||||
@@ -280,6 +294,22 @@ void loop() {
|
|||||||
stepper1.run();
|
stepper1.run();
|
||||||
stepper2.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 (homingState == HOME_ALL_PHASE1) {
|
||||||
if (stepper1.distanceToGo() == 0 && stepper2.distanceToGo() == 0) {
|
if (stepper1.distanceToGo() == 0 && stepper2.distanceToGo() == 0) {
|
||||||
stepper1.setCurrentPosition(1024);
|
stepper1.setCurrentPosition(1024);
|
||||||
@@ -291,8 +321,6 @@ void loop() {
|
|||||||
}
|
}
|
||||||
} else if (homingState == HOME_ALL_PHASE2) {
|
} else if (homingState == HOME_ALL_PHASE2) {
|
||||||
if (stepper1.distanceToGo() == 0 && stepper2.distanceToGo() == 0) {
|
if (stepper1.distanceToGo() == 0 && stepper2.distanceToGo() == 0) {
|
||||||
stepper1.disableOutputs();
|
|
||||||
stepper2.disableOutputs();
|
|
||||||
stepper1.setMaxSpeed(currentMaxSpeed);
|
stepper1.setMaxSpeed(currentMaxSpeed);
|
||||||
stepper2.setMaxSpeed(currentMaxSpeed);
|
stepper2.setMaxSpeed(currentMaxSpeed);
|
||||||
stepper1.setAcceleration(currentAccel);
|
stepper1.setAcceleration(currentAccel);
|
||||||
@@ -309,7 +337,6 @@ void loop() {
|
|||||||
}
|
}
|
||||||
} else if (homingState == HOME_1_PHASE2) {
|
} else if (homingState == HOME_1_PHASE2) {
|
||||||
if (stepper1.distanceToGo() == 0) {
|
if (stepper1.distanceToGo() == 0) {
|
||||||
stepper1.disableOutputs();
|
|
||||||
stepper1.setMaxSpeed(currentMaxSpeed);
|
stepper1.setMaxSpeed(currentMaxSpeed);
|
||||||
stepper1.setAcceleration(currentAccel);
|
stepper1.setAcceleration(currentAccel);
|
||||||
homingState = HOME_IDLE;
|
homingState = HOME_IDLE;
|
||||||
@@ -322,7 +349,6 @@ void loop() {
|
|||||||
}
|
}
|
||||||
} else if (homingState == HOME_2_PHASE2) {
|
} else if (homingState == HOME_2_PHASE2) {
|
||||||
if (stepper2.distanceToGo() == 0) {
|
if (stepper2.distanceToGo() == 0) {
|
||||||
stepper2.disableOutputs();
|
|
||||||
stepper2.setMaxSpeed(currentMaxSpeed);
|
stepper2.setMaxSpeed(currentMaxSpeed);
|
||||||
stepper2.setAcceleration(currentAccel);
|
stepper2.setAcceleration(currentAccel);
|
||||||
homingState = HOME_IDLE;
|
homingState = HOME_IDLE;
|
||||||
@@ -332,7 +358,6 @@ void loop() {
|
|||||||
// Periodic position notify while motors are moving
|
// Periodic position notify while motors are moving
|
||||||
if (bleConnected) {
|
if (bleConnected) {
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
bool moving = stepper1.isRunning() || stepper2.isRunning() || homingState != HOME_IDLE;
|
|
||||||
if (moving && (now - lastNotify >= NOTIFY_INTERVAL_MS)) {
|
if (moving && (now - lastNotify >= NOTIFY_INTERVAL_MS)) {
|
||||||
lastNotify = now;
|
lastNotify = now;
|
||||||
sendPosition();
|
sendPosition();
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
* SPD:<n> → set max speed (steps/sec)
|
* SPD:<n> → set max speed (steps/sec)
|
||||||
* ACC:<n> → set acceleration (steps/sec²)
|
* ACC:<n> → set acceleration (steps/sec²)
|
||||||
* HOME → zero both steppers
|
* HOME → zero both steppers
|
||||||
|
* DISABLE → disable steppers (un-hold)
|
||||||
* POS → request position report
|
* POS → request position report
|
||||||
*
|
*
|
||||||
* Status characteristic (NOTIFY):
|
* Status characteristic (NOTIFY):
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const HomeSection = {
|
|||||||
<div class="section-page fade-in">
|
<div class="section-page fade-in">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h2>WijiBoard</h2>
|
<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>
|
||||||
|
|
||||||
<div class="grid-2" style="margin-bottom:16px">
|
<div class="grid-2" style="margin-bottom:16px">
|
||||||
|
|||||||
Reference in New Issue
Block a user