added homing.

This commit is contained in:
PROFERIS - Mi³osz Stocki
2026-07-06 16:09:06 +02:00
parent a6aa6f00df
commit 2e3780cb0b
4 changed files with 115 additions and 19 deletions
+6 -6
View File
@@ -91,8 +91,9 @@ Name: WijiBoard
| `S2-<n>` | Move motor 2 CCW by n steps | | `S2-<n>` | Move motor 2 CCW by n steps |
| `SPD:<n>` | Set max speed (steps/sec) for both motors | | `SPD:<n>` | Set max speed (steps/sec) for both motors |
| `ACC:<n>` | Set acceleration (steps/sec²) for both motors | | `ACC:<n>` | Set acceleration (steps/sec²) for both motors |
| `HOME1` | Zero motor 1 step counter | | `HOMEALL` | Home both motors simultaneously |
| `HOME2` | Zero motor 2 step counter | | `HOME1` | Home motor 1 |
| `HOME2` | Home motor 2 |
| `POS` | Request current positions (triggers NOTIFY) | | `POS` | Request current positions (triggers NOTIFY) |
### Status notifications (firmware → browser) ### Status notifications (firmware → browser)
@@ -424,10 +425,9 @@ To add a new section:
assume a specific physical motor orientation. If the real arm moves in the wrong direction assume a specific physical motor orientation. If the real arm moves in the wrong direction
when clicking a target, negate the step delta for that motor in `moveToXY()`. when clicking a target, negate the step delta for that motor in `moveToXY()`.
- **Home position**: `steps1 = 0, steps2 = 0` is wherever the firmware powers up (no homing - **Home position**: Stall homing is fully implemented matching the original C++ routine but adjusted to align with `PositionControl.cpp` motor mappings. Homing pushes the motors against their physical limits and sets `steps1 = 0` (0°, points right) and `steps2 = -1024` (-180°, points left). This corresponds to the arms folded OUTWARDS.
routine implemented yet). The arm must be manually set to a known starting configuration - The initial web simulation starts with these coordinates, meaning the UI assumes the arm has already been homed before the browser connects.
before the IK will be accurate. Suggested home: arms roughly parallel, EE near centre of - Homing can be triggered individually per-motor (`HOME1`, `HOME2`) or combined (`HOMEALL`) from the web UI.
the working area.
- **LIMITS tuning**: All values in `LIMITS` in `kinematics.js` are estimates based on the - **LIMITS tuning**: All values in `LIMITS` in `kinematics.js` are estimates based on the
original project's data. They need physical verification: original project's data. They need physical verification:
+91 -6
View File
@@ -12,8 +12,9 @@
* S2-<n> → Step motor 2 CCW n steps * S2-<n> → Step motor 2 CCW n steps
* SPD:<n> → Set max speed for both motors (steps/sec) * SPD:<n> → Set max speed for both motors (steps/sec)
* ACC:<n> → Set acceleration for both motors (steps/sec²) * ACC:<n> → Set acceleration for both motors (steps/sec²)
* HOME1 → Zero motor 1 position * HOME1 → Home motor 1 (Right)
* HOME2 → Zero motor 2 position * HOME2 → Home motor 2 (Left)
* HOMEALL → Home both motors simultaneously
* POS → Request current positions (triggers NOTIFY) * POS → Request current positions (triggers NOTIFY)
* *
* BLE Status Characteristic (NOTIFY): * BLE Status Characteristic (NOTIFY):
@@ -73,6 +74,9 @@ const unsigned long NOTIFY_INTERVAL_MS = 200;
// ─── Forward declarations ───────────────────────────────────────── // ─── Forward declarations ─────────────────────────────────────────
void parseCommand(const String& cmd); void parseCommand(const String& cmd);
void sendPosition(); void sendPosition();
void performHomingAll();
void performHoming1();
void performHoming2();
// ─── BLE Server Callbacks ───────────────────────────────────────── // ─── BLE Server Callbacks ─────────────────────────────────────────
class ServerCallbacks : public BLEServerCallbacks { class ServerCallbacks : public BLEServerCallbacks {
@@ -132,15 +136,22 @@ void parseCommand(const String& cmd) {
} }
// HOME1 // HOME1
if (cmd == "HOME1") { if (cmd == "HOME1") {
stepper1.setCurrentPosition(0); Serial.println("[S1] Homing Motor 1...");
Serial.println("[S1] Zeroed"); performHoming1();
sendPosition(); sendPosition();
return; return;
} }
// HOME2 // HOME2
if (cmd == "HOME2") { if (cmd == "HOME2") {
stepper2.setCurrentPosition(0); Serial.println("[S2] Homing Motor 2...");
Serial.println("[S2] Zeroed"); performHoming2();
sendPosition();
return;
}
// HOMEALL
if (cmd == "HOMEALL") {
Serial.println("[SYS] Homing both motors...");
performHomingAll();
sendPosition(); sendPosition();
return; return;
} }
@@ -233,3 +244,77 @@ void loop() {
} }
} }
} }
// ─── Homing Routines ──────────────────────────────────────────────
void performHomingAll() {
// 1. Move against limits (M1 CCW, M2 CCW)
stepper2.moveTo(1024);
stepper1.moveTo(2048);
while (stepper1.distanceToGo() != 0 || stepper2.distanceToGo() != 0) {
stepper1.run();
stepper2.run();
}
stepper1.setCurrentPosition(0);
// 2. Move to negative limits (CW)
stepper2.moveTo(-1050);
stepper1.move(-1300); // equivalent to moveTo(-1300) since pos is 0
while (stepper1.distanceToGo() != 0 || stepper2.distanceToGo() != 0) {
stepper1.run();
stepper2.run();
}
stepper2.setCurrentPosition(0);
// 3. Move to final home position
stepper2.moveTo(550);
stepper1.moveTo(-530);
while (stepper1.distanceToGo() != 0 || stepper2.distanceToGo() != 0) {
stepper1.run();
stepper2.run();
}
// 4. Set origin relative to this final position
stepper2.setCurrentPosition(-1024);
stepper1.setCurrentPosition(0);
stepper1.disableOutputs();
stepper2.disableOutputs();
Serial.println("[SYS] Homing all complete.");
}
void performHoming1() {
// M1 (Right) stalls going positive (CCW)
stepper1.move(2048);
while (stepper1.distanceToGo() != 0) {
stepper1.run();
}
stepper1.setCurrentPosition(0);
// Move to final home position (CW)
stepper1.moveTo(-530);
while (stepper1.distanceToGo() != 0) {
stepper1.run();
}
stepper1.setCurrentPosition(0);
stepper1.disableOutputs();
Serial.println("[S1] Homing complete.");
}
void performHoming2() {
// M2 (Left) stalls going negative (CW)
stepper2.move(-2048);
while (stepper2.distanceToGo() != 0) {
stepper2.run();
}
stepper2.setCurrentPosition(0);
// Move to final home position (CCW)
stepper2.moveTo(550);
while (stepper2.distanceToGo() != 0) {
stepper2.run();
}
stepper2.setCurrentPosition(-1024);
stepper2.disableOutputs();
Serial.println("[S2] Homing complete.");
}
+1
View File
@@ -39,6 +39,7 @@ const ARM = {
l2: 110.0, // distal link length (mm) l2: 110.0, // distal link length (mm)
STEPS_PER_REV: 2048, STEPS_PER_REV: 2048,
STEP_ANGLE_DEG: 360 / 2048, // ≈ 0.17578125 °/step STEP_ANGLE_DEG: 360 / 2048, // ≈ 0.17578125 °/step
HOME_STEPS: { m1: 0, m2: -1024 }, // M1 at 0°, M2 at -180° (arms folded outward)
}; };
// ── WORKSPACE CONSTRAINTS ───────────────────────────────────────── // ── WORKSPACE CONSTRAINTS ─────────────────────────────────────────
+17 -7
View File
@@ -45,8 +45,8 @@ const PX_L2 = IK.ARM.l2 * SV.SCALE;
const PX_D2 = IK.ARM.d2 * SV.SCALE; const PX_D2 = IK.ARM.d2 * SV.SCALE;
// ── Section state ───────────────────────────────────────────────── // ── Section state ─────────────────────────────────────────────────
let steps1 = 0; let steps1 = IK.ARM.HOME_STEPS.m1;
let steps2 = 0; let steps2 = IK.ARM.HOME_STEPS.m2;
let eventCleanup = []; let eventCleanup = [];
// ── SVG element refs ────────────────────────────────────────────── // ── SVG element refs ──────────────────────────────────────────────
@@ -215,10 +215,16 @@ async function jogMotor(motor, delta) {
} }
async function zeroMotor(motor) { async function zeroMotor(motor) {
if (motor === 1) steps1 = 0; if (motor === 'ALL') {
else steps2 = 0; steps1 = IK.ARM.HOME_STEPS.m1;
steps2 = IK.ARM.HOME_STEPS.m2;
} else if (motor === 1) {
steps1 = IK.ARM.HOME_STEPS.m1;
} else {
steps2 = IK.ARM.HOME_STEPS.m2;
}
renderArmFromSteps(); renderArmFromSteps();
const cmd = `HOME${motor}`; const cmd = motor === 'ALL' ? 'HOMEALL' : `HOME${motor}`;
BLE.isConnected() BLE.isConnected()
? await BLE.write(cmd).catch(e => UI.log(e.message, 'error')) ? await BLE.write(cmd).catch(e => UI.log(e.message, 'error'))
: UI.log(`[sim] ${cmd}`, 'info'); : UI.log(`[sim] ${cmd}`, 'info');
@@ -568,7 +574,7 @@ function buildHTML() {
${[10,50,100,512].map(n=>`<button class="btn btn-ghost" data-motor="1" data-steps="${n}">+${n}</button>`).join('')} ${[10,50,100,512].map(n=>`<button class="btn btn-ghost" data-motor="1" data-steps="${n}">+${n}</button>`).join('')}
${[10,50,100,512].map(n=>`<button class="btn btn-ghost" data-motor="1" data-steps="-${n}">-${n}</button>`).join('')} ${[10,50,100,512].map(n=>`<button class="btn btn-ghost" data-motor="1" data-steps="-${n}">-${n}</button>`).join('')}
</div> </div>
<button class="btn btn-danger btn-sm btn-full" id="s1-zero">Zero M1</button> <button class="btn btn-danger btn-sm btn-full" id="s1-zero">Home M1</button>
</div> </div>
<!-- Motor 2 --> <!-- Motor 2 -->
<div class="jog-motor"> <div class="jog-motor">
@@ -584,9 +590,12 @@ function buildHTML() {
${[10,50,100,512].map(n=>`<button class="btn btn-ghost" data-motor="2" data-steps="${n}">+${n}</button>`).join('')} ${[10,50,100,512].map(n=>`<button class="btn btn-ghost" data-motor="2" data-steps="${n}">+${n}</button>`).join('')}
${[10,50,100,512].map(n=>`<button class="btn btn-ghost" data-motor="2" data-steps="-${n}">-${n}</button>`).join('')} ${[10,50,100,512].map(n=>`<button class="btn btn-ghost" data-motor="2" data-steps="-${n}">-${n}</button>`).join('')}
</div> </div>
<button class="btn btn-danger btn-sm btn-full" id="s2-zero">Zero M2</button> <button class="btn btn-danger btn-sm btn-full" id="s2-zero">Home M2</button>
</div> </div>
</div> </div>
<div style="margin-top: 12px;">
<button class="btn btn-danger btn-full" id="sall-zero" style="padding: 10px; font-weight: bold; font-size: 0.9rem;">HOME BOTH MOTORS</button>
</div>
</div> </div>
<!-- Event log --> <!-- Event log -->
@@ -698,6 +707,7 @@ const StepperTestSection = {
document.getElementById('s2-ccw').addEventListener('click', () => jogMotor(2, -gs(2))); document.getElementById('s2-ccw').addEventListener('click', () => jogMotor(2, -gs(2)));
document.getElementById('s1-zero').addEventListener('click', () => zeroMotor(1)); document.getElementById('s1-zero').addEventListener('click', () => zeroMotor(1));
document.getElementById('s2-zero').addEventListener('click', () => zeroMotor(2)); document.getElementById('s2-zero').addEventListener('click', () => zeroMotor(2));
document.getElementById('sall-zero').addEventListener('click', () => zeroMotor('ALL'));
// ── Quick-step buttons ──────────────────────────────────────── // ── Quick-step buttons ────────────────────────────────────────
container.querySelectorAll('[data-motor][data-steps]').forEach(btn => { container.querySelectorAll('[data-motor][data-steps]').forEach(btn => {