tangled
alpha
login
or
join now
dunkirk.sh
/
myrus
0
fork
atom
a scrappy gimbal that insults you in shakespearean english
0
fork
atom
overview
issues
pulls
pipelines
feat: use joystick
Kieran Klukas
1 year ago
d16f8e6d
b67e2160
+69
-28
2 changed files
expand all
collapse all
unified
split
server
index.ts
src
index.ts
+2
-2
server/index.ts
···
8
8
9
9
app.post("/motor1", (req, res) => {
10
10
const speed = req.body.speed;
11
11
-
const command = `1 ${speed}\n`;
11
11
+
const command = `1 ${speed}\r`;
12
12
serialPort.write(command);
13
13
res.send("OK");
14
14
});
15
15
16
16
app.post("/motor2", (req, res) => {
17
17
const speed = req.body.speed;
18
18
-
const command = `2 ${speed}\n`;
18
18
+
const command = `2 ${speed}\r`;
19
19
serialPort.write(command);
20
20
res.send("OK");
21
21
});
+67
-26
src/index.ts
···
5
5
let videoElement: HTMLVideoElement;
6
6
let lastError = 0;
7
7
let integral = 0;
8
8
+
let isDragging = false;
9
9
+
let joystickX = 0;
10
10
+
let joystickY = 0;
8
11
9
12
// PID constants
10
13
const Kp = 0.5;
···
26
29
<video id="webcam" width="640" height="480" autoplay muted></video>
27
30
<canvas id="overlay" style="position: absolute;"></canvas>
28
31
29
29
-
<div>
30
30
-
<label>Left Motor (1) Rotations:</label>
31
31
-
<input type="range" id="motor1" min="-10" max="10" step="0.1" value="0">
32
32
-
<span id="motor1Value">0</span>
33
33
-
<button id="send1">Send</button>
32
32
+
<div id="joystick" style="width: 200px; height: 200px; border: 2px solid black; border-radius: 50%; position: relative; margin: 20px;">
33
33
+
<div id="joystickHandle" style="width: 20px; height: 20px; background: red; border-radius: 50%; position: absolute; top: 90px; left: 90px; cursor: pointer;"></div>
34
34
</div>
35
35
-
36
35
<div>
37
37
-
<label>Right Motor (2) Rotations:</label>
38
38
-
<input type="range" id="motor2" min="-10" max="10" step="0.1" value="0">
39
39
-
<span id="motor2Value">0</span>
40
40
-
<button id="send2">Send</button>
36
36
+
<span>Motor Values - X: </span><span id="motor1Value">0</span>
37
37
+
<span>, Y: </span><span id="motor2Value">0</span>
38
38
+
<button id="sendJoystick">Send</button>
41
39
</div>
42
40
</div>
43
41
<div style="width: 300px; padding: 20px; border-left: 1px solid #ccc; overflow-y: auto;">
···
174
172
}
175
173
}
176
174
175
175
+
function updateJoystickPosition(x: number, y: number) {
176
176
+
const joystick = document.getElementById("joystick");
177
177
+
const handle = document.getElementById("joystickHandle");
178
178
+
if (!joystick || !handle) return;
179
179
+
180
180
+
const bounds = joystick.getBoundingClientRect();
181
181
+
const radius = bounds.width / 2;
182
182
+
183
183
+
// Calculate relative position from center
184
184
+
const relX = x - bounds.left - radius;
185
185
+
const relY = y - bounds.top - radius;
186
186
+
187
187
+
// Calculate distance from center
188
188
+
const distance = Math.sqrt(relX * relX + relY * relY);
189
189
+
190
190
+
// Normalize to radius if outside circle
191
191
+
const normalizedX = distance > radius ? (relX / distance) * radius : relX;
192
192
+
const normalizedY = distance > radius ? (relY / distance) * radius : relY;
193
193
+
194
194
+
// Update handle position
195
195
+
handle.style.left = normalizedX + radius - 10 + "px";
196
196
+
handle.style.top = normalizedY + radius - 10 + "px";
197
197
+
198
198
+
// Update values (-0.5 to 0.5 range)
199
199
+
joystickX = normalizedX / (radius * 2);
200
200
+
joystickY = normalizedY / (radius * 2);
201
201
+
202
202
+
document.getElementById("motor1Value")!.textContent = joystickX.toFixed(2);
203
203
+
document.getElementById("motor2Value")!.textContent = joystickY.toFixed(2);
204
204
+
}
205
205
+
177
206
function defaultPageRender() {
178
207
const app = document.querySelector<HTMLDivElement>("#app");
179
208
if (!app) throw new Error("App element not found");
···
181
210
182
211
videoElement = document.getElementById("webcam") as HTMLVideoElement;
183
212
184
184
-
document.getElementById("motor1")?.addEventListener("input", (e) => {
185
185
-
const value = (e.target as HTMLInputElement).value;
186
186
-
const display = document.getElementById("motor1Value");
187
187
-
if (display) display.textContent = value;
188
188
-
});
213
213
+
const joystick = document.getElementById("joystick");
214
214
+
const handle = document.getElementById("joystickHandle");
215
215
+
216
216
+
if (joystick && handle) {
217
217
+
handle.addEventListener("mousedown", () => {
218
218
+
isDragging = true;
219
219
+
});
220
220
+
221
221
+
document.addEventListener("mousemove", (e) => {
222
222
+
if (isDragging) {
223
223
+
updateJoystickPosition(e.clientX, e.clientY);
224
224
+
}
225
225
+
});
189
226
190
190
-
document.getElementById("motor2")?.addEventListener("input", (e) => {
191
191
-
const value = (e.target as HTMLInputElement).value;
192
192
-
const display = document.getElementById("motor2Value");
193
193
-
if (display) display.textContent = value;
194
194
-
});
227
227
+
document.addEventListener("mouseup", () => {
228
228
+
if (isDragging) {
229
229
+
isDragging = false;
230
230
+
}
231
231
+
});
232
232
+
}
195
233
196
234
document.getElementById("connect")?.addEventListener("click", connectSerial);
197
235
document
···
201
239
await startWebcam();
202
240
trackFaces();
203
241
});
204
204
-
document.getElementById("send1")?.addEventListener("click", () => {
205
205
-
const value = (document.getElementById("motor1") as HTMLInputElement).value;
206
206
-
sendMotorCommand(1, parseFloat(value));
242
242
+
document.getElementById("sendJoystick")?.addEventListener("click", () => {
243
243
+
sendMotorCommand(1, joystickX);
244
244
+
sendMotorCommand(2, joystickY);
207
245
});
208
208
-
document.getElementById("send2")?.addEventListener("click", () => {
209
209
-
const value = (document.getElementById("motor2") as HTMLInputElement).value;
210
210
-
sendMotorCommand(2, parseFloat(value));
246
246
+
247
247
+
document.addEventListener("keydown", (e) => {
248
248
+
if (e.key === "Enter") {
249
249
+
sendMotorCommand(1, joystickX);
250
250
+
sendMotorCommand(2, joystickY);
251
251
+
}
211
252
});
212
253
}
213
254