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