update
Browse files- certs/cert.pem +19 -0
- certs/key.pem +28 -0
- dummy_server.py +2 -2
- requirements.txt +3 -0
- server.py +5 -0
- static/main.js +2 -2
- static/sinus-io.js +97 -0
- static/sinus.html +60 -0
- static/sinus.js +84 -0
certs/cert.pem
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-----BEGIN CERTIFICATE-----
|
2 |
+
MIIDGjCCAgKgAwIBAgIUdNm0OQEuMPg1qCqxpUdmqWEwlpIwDQYJKoZIhvcNAQEL
|
3 |
+
BQAwFDESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTI1MDUxMjE4NDY0MFoXDTI2MDUx
|
4 |
+
MjE4NDY0MFowFDESMBAGA1UEAwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEF
|
5 |
+
AAOCAQ8AMIIBCgKCAQEAtX4QVKYQOKceZLfstjNTV3FPBHkDqneaDwyVvsPbm8gm
|
6 |
+
slhPTY5upHFjMJFH5MBkbR6jdG0aKTLIaBOct8RcJ6LkECNtb5Wn7A4QhPogHrev
|
7 |
+
qOTT7/tVhCOgDTmvmJD33GA3tHfo2QLXbfYxXxiAO0WB2L2F2/oWPOOji6UCKL9B
|
8 |
+
+TgoivP99im2GIanJy8muVU4piKfOUskQs0WbjkCclNROohxiMUK8iBXXFvIxhpg
|
9 |
+
2l1MKVSo9IPbwvrlha5Ixyb3yguzkfCILjTDtJoXsUkpfdc4XYaHbcuI8LEujcoN
|
10 |
+
dmHwImkpWd7I/MTZDNbvcaCpd1+APqJyuIOhZCdXxwIDAQABo2QwYjAdBgNVHQ4E
|
11 |
+
FgQUG/GjXqossD4HAaDWRT17HMQ9190wHwYDVR0jBBgwFoAUG/GjXqossD4HAaDW
|
12 |
+
RT17HMQ9190wDwYDVR0TAQH/BAUwAwEB/zAPBgNVHREECDAGhwR/AAABMA0GCSqG
|
13 |
+
SIb3DQEBCwUAA4IBAQBnhkeoWRk/2FLlSXx4kI8nAUVyOfzdRGtNw80IZk5eIJun
|
14 |
+
eI030W0NvrQIxuzvfQIUB3D1sfwkfMAgR10viKSOUcAalrJYup6TKOizXIB+r3Yx
|
15 |
+
rhRiolw4wP3HixmnlcqfZXekVcRVM3GW9+0o0NW223EEp3vNCskMvSiJ2Ia6J+TI
|
16 |
+
LAoteVZ1FxZBt0b9uWuDD6btfhBXza/dzIxVTWswfaqlfBQYznqX2ZzUS9bIMC37
|
17 |
+
UdihzUgEpPoiqKfN3PoeXsXq3NZvqTx4E25qJWhIVSuOBUmzdfizRAy4SiKJfEmT
|
18 |
+
5MJvnthT47CH2Yym50YL9ZBVj6K9vBGiSYjTaLhV
|
19 |
+
-----END CERTIFICATE-----
|
certs/key.pem
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-----BEGIN PRIVATE KEY-----
|
2 |
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1fhBUphA4px5k
|
3 |
+
t+y2M1NXcU8EeQOqd5oPDJW+w9ubyCayWE9Njm6kcWMwkUfkwGRtHqN0bRopMsho
|
4 |
+
E5y3xFwnouQQI21vlafsDhCE+iAet6+o5NPv+1WEI6ANOa+YkPfcYDe0d+jZAtdt
|
5 |
+
9jFfGIA7RYHYvYXb+hY846OLpQIov0H5OCiK8/32KbYYhqcnLya5VTimIp85SyRC
|
6 |
+
zRZuOQJyU1E6iHGIxQryIFdcW8jGGmDaXUwpVKj0g9vC+uWFrkjHJvfKC7OR8Igu
|
7 |
+
NMO0mhexSSl91zhdhodty4jwsS6Nyg12YfAiaSlZ3sj8xNkM1u9xoKl3X4A+onK4
|
8 |
+
g6FkJ1fHAgMBAAECggEACIQ3LJAZ8CnTamMarfzf+v+O3cUp9iyKygyYO3pOvbm/
|
9 |
+
xLWri8mhVbRMzo4hFmuUXges7Un88ZSRXktK56REtZPWUA8n7WKgULQWQI8vxURv
|
10 |
+
iNh4CemJPHXWTSVfh1eIi5eBdav8yeLxJf1PMsjgPfSUhvQM9uLk4T8s9C2uWY52
|
11 |
+
DeqW0SbVfs0LXEJ6GZT3oGm5seN5Gac1vkPEoRAHFo5kvi5zMmMJkP0UgVOdlgE6
|
12 |
+
mxusdYlNdQCUTebQpaBUMBAFz7geyTnx3uMJJ5G1KxW7VA6uSVAN5RUuhD467/2n
|
13 |
+
U40EnI9Skv7o/+pznmZu6yKtIs6OwEy+ASQGuodxDQKBgQDBiRmJHmybQXqvKXCH
|
14 |
+
82Th4eiNoAMz5PmK8VrMhZraXJOIV7IywPiywDMWNFfWM3LEMsJKl3VXHtUrQL5N
|
15 |
+
7WY7xCkRF9Qhay7Hlhi7aBBWk0DPqgN4qdJhWr9xvKuhOxZVC+pMtHZZFyBjtADW
|
16 |
+
NI9rnC0uosAoSzjzPwdNikXZHQKBgQDwEeaJ3mK4lev5IbqZO6SKxJ2IFdViD+h2
|
17 |
+
TtCjSx2XKJ0fPLDjHhYE7quPFrUQh9kzDoWKTjCdwLOu/sr+VL9nn5bhcsQT7t9l
|
18 |
+
TmehjxYgHaqVdtwNjV3S/E6T3gKbWRAHMpLcvqiwt9PdWELsAbjf/ugwraPM9CUr
|
19 |
+
xjNgCIzDMwKBgAh3afGQrimglLBi/LRF1oz3KAhCDsHPa4dDhbhaw+p3kFCvnXEQ
|
20 |
+
9hBDzjhTc+BAAe5JViyTMaPtCmBJBco873L/4tgHldUcbkB29YAFTmmrKXOsOVim
|
21 |
+
/TgbEzLzkQKNpi10RvyyDFdbZqRV9I9qXzfS7jsTDZr1p3kksboXqXSVAoGACUhH
|
22 |
+
57DHlGeHljxtoJsjw5HSnX5qn74JuPlXK9ktrbiOSrToCgARzeMEkyXHnnoCNe0r
|
23 |
+
0KsxSgg3al7Dro3MUM9k4Ba16idkT+B2NVL5AgjjnZ/Y5lU++Xdz+letNiB9dCnK
|
24 |
+
b+qXTy3sbTSKceGnKlIK3Eb6fGQ8Q7MILYBnIO0CgYEAn9WJHcJodXFVd1vngU1A
|
25 |
+
/+hGs9PMOUHqD7SuNy0HggifuCXJ9w+PWPT5gFtGlgY5EyH0DYKQ+ZrTgRdL2mTA
|
26 |
+
6BYUjaIi1ggWsc71xwM8loqahz+Amwo1Mqfk3KAbR2LQ8qog87+Pn3AVn3T/EWGf
|
27 |
+
Tbg2W8xEdzpksMzugg8Vx7E=
|
28 |
+
-----END PRIVATE KEY-----
|
dummy_server.py
CHANGED
@@ -2,7 +2,7 @@ from flask import Flask, request
|
|
2 |
from flask_cors import CORS
|
3 |
|
4 |
app = Flask(__name__)
|
5 |
-
CORS(app)
|
6 |
|
7 |
motor_states = {f"motor_{i}": 0.0 for i in range(6)}
|
8 |
|
@@ -18,4 +18,4 @@ def control_motor():
|
|
18 |
|
19 |
|
20 |
if __name__ == "__main__":
|
21 |
-
app.run(host="0.0.0.0", port=5001)
|
|
|
2 |
from flask_cors import CORS
|
3 |
|
4 |
app = Flask(__name__)
|
5 |
+
CORS(app)
|
6 |
|
7 |
motor_states = {f"motor_{i}": 0.0 for i in range(6)}
|
8 |
|
|
|
18 |
|
19 |
|
20 |
if __name__ == "__main__":
|
21 |
+
app.run(host="0.0.0.0", port=5001, ssl_context=("certs/cert.pem", "certs/key.pem"))
|
requirements.txt
CHANGED
@@ -1 +1,4 @@
|
|
|
|
1 |
flask
|
|
|
|
|
|
1 |
+
eventlet
|
2 |
flask
|
3 |
+
flask-cors
|
4 |
+
flask-socketio
|
server.py
CHANGED
@@ -8,5 +8,10 @@ def root():
|
|
8 |
return send_from_directory("static", "index.html")
|
9 |
|
10 |
|
|
|
|
|
|
|
|
|
|
|
11 |
if __name__ == "__main__":
|
12 |
app.run(host="0.0.0.0", port=7860)
|
|
|
8 |
return send_from_directory("static", "index.html")
|
9 |
|
10 |
|
11 |
+
@app.route("/sinus")
|
12 |
+
def sinus():
|
13 |
+
return send_from_directory("static", "sinus.html")
|
14 |
+
|
15 |
+
|
16 |
if __name__ == "__main__":
|
17 |
app.run(host="0.0.0.0", port=7860)
|
static/main.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
const robotIp = "
|
2 |
window.onload = () => {
|
3 |
const container = document.getElementById("sliders");
|
4 |
for (let i = 0; i < 6; i++) {
|
@@ -13,7 +13,7 @@ window.onload = () => {
|
|
13 |
fetch(`${robotIp}/motor_control`, {
|
14 |
method: "POST",
|
15 |
headers: { "Content-Type": "application/json" },
|
16 |
-
body: JSON.stringify({
|
17 |
}).catch(err => console.error("Send error:", err));
|
18 |
};
|
19 |
container.appendChild(label);
|
|
|
1 |
+
const robotIp = "http://127.0.0.1:5001"
|
2 |
window.onload = () => {
|
3 |
const container = document.getElementById("sliders");
|
4 |
for (let i = 0; i < 6; i++) {
|
|
|
13 |
fetch(`${robotIp}/motor_control`, {
|
14 |
method: "POST",
|
15 |
headers: { "Content-Type": "application/json" },
|
16 |
+
body: JSON.stringify({ [`motor_${i}`]: parseFloat(input.value) }),
|
17 |
}).catch(err => console.error("Send error:", err));
|
18 |
};
|
19 |
container.appendChild(label);
|
static/sinus-io.js
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
window.onload = () => {
|
2 |
+
const socket = io("127.0.0.1:5001");
|
3 |
+
let connected = false;
|
4 |
+
|
5 |
+
socket.on("connect", () => {
|
6 |
+
console.log("Connected to the server");
|
7 |
+
connected = true;
|
8 |
+
});
|
9 |
+
socket.on("disconnect", () => {
|
10 |
+
console.log("Disconnected from the server");
|
11 |
+
connected = false;
|
12 |
+
});
|
13 |
+
|
14 |
+
const robotIp = "http://127.0.0.1:5001"
|
15 |
+
|
16 |
+
const amplitudeSlider = document.getElementById("amplitude");
|
17 |
+
const amplitudeValue = document.getElementById("amplitudeValue");
|
18 |
+
const frequencySlider = document.getElementById("frequency");
|
19 |
+
const frequencyValue = document.getElementById("frequencyValue");
|
20 |
+
const syncSlider = document.getElementById("sync");
|
21 |
+
const syncValue = document.getElementById("syncValue");
|
22 |
+
|
23 |
+
const motor_0Synced = document.getElementById("motor_0_synced");
|
24 |
+
const motor_1Synced = document.getElementById("motor_1_synced");
|
25 |
+
const motor_2Synced = document.getElementById("motor_2_synced");
|
26 |
+
const motor_3Synced = document.getElementById("motor_3_synced");
|
27 |
+
const motor_4Synced = document.getElementById("motor_4_synced");
|
28 |
+
const motor_5Synced = document.getElementById("motor_5_synced");
|
29 |
+
|
30 |
+
const verboseCheckbox = document.getElementById("verbose");
|
31 |
+
|
32 |
+
amplitudeSlider.addEventListener("input", function () {
|
33 |
+
amplitudeValue.textContent = amplitudeSlider.value;
|
34 |
+
});
|
35 |
+
|
36 |
+
frequencySlider.addEventListener("input", function () {
|
37 |
+
frequencyValue.textContent = frequencySlider.value;
|
38 |
+
});
|
39 |
+
|
40 |
+
syncSlider.addEventListener("input", function () {
|
41 |
+
syncValue.textContent = syncSlider.value;
|
42 |
+
});
|
43 |
+
|
44 |
+
let t0 = Date.now();
|
45 |
+
|
46 |
+
const syncStep = () => {
|
47 |
+
let t = Date.now() - t0;
|
48 |
+
|
49 |
+
const value = computeSinValue(
|
50 |
+
frequencySlider.value,
|
51 |
+
amplitudeSlider.value,
|
52 |
+
t / 1000
|
53 |
+
);
|
54 |
+
|
55 |
+
let goals = {};
|
56 |
+
if (motor_0Synced.checked) {
|
57 |
+
goals["motor_0"] = value;
|
58 |
+
}
|
59 |
+
if (motor_1Synced.checked) {
|
60 |
+
goals["motor_1"] = value;
|
61 |
+
}
|
62 |
+
if (motor_2Synced.checked) {
|
63 |
+
goals["motor_2"] = value;
|
64 |
+
}
|
65 |
+
if (motor_3Synced.checked) {
|
66 |
+
goals["motor_3"] = value;
|
67 |
+
}
|
68 |
+
if (motor_4Synced.checked) {
|
69 |
+
goals["motor_4"] = value;
|
70 |
+
}
|
71 |
+
if (motor_5Synced.checked) {
|
72 |
+
goals["motor_5"] = value;
|
73 |
+
}
|
74 |
+
|
75 |
+
if (verboseCheckbox.checked) {
|
76 |
+
console.log("Sending goals:", goals);
|
77 |
+
}
|
78 |
+
|
79 |
+
if (Object.keys(goals).length !== 0 && connected) {
|
80 |
+
// fetch(`${robotIp}/motor_control`, {
|
81 |
+
// method: "POST",
|
82 |
+
// headers: { "Content-Type": "application/json" },
|
83 |
+
// body: JSON.stringify(goals),
|
84 |
+
// }).catch(err => console.error("Send error:", err));
|
85 |
+
socket.emit("motor_control", goals);
|
86 |
+
}
|
87 |
+
const syncPeriod = 1000.0 / syncSlider.value;
|
88 |
+
setTimeout(syncStep, syncPeriod);
|
89 |
+
};
|
90 |
+
syncStep();
|
91 |
+
|
92 |
+
// Compute the current sin value at a given frequency
|
93 |
+
function computeSinValue(frequency, amplitude, time) {
|
94 |
+
return amplitude * Math.sin(2 * Math.PI * frequency * time);
|
95 |
+
}
|
96 |
+
|
97 |
+
};
|
static/sinus.html
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8" />
|
6 |
+
<title>Sinus</title>
|
7 |
+
</head>
|
8 |
+
|
9 |
+
<body>
|
10 |
+
<h1>Sinus</h1>
|
11 |
+
<div id="sliders">
|
12 |
+
<label for="amplitude">Amplitude:</label>
|
13 |
+
<input type="range" id="amplitude" min="0" max="100" value="25" />
|
14 |
+
<span id="amplitudeValue">25</span>
|
15 |
+
<br />
|
16 |
+
<label for="frequency">Sinus Frequency:</label>
|
17 |
+
<input type="range" id="frequency" min="0" max="2" step="0.1" value="0.5" />
|
18 |
+
<span id="frequencyValue">0.5</span>
|
19 |
+
<br />
|
20 |
+
</div>
|
21 |
+
<h1>Synchronisation</h1>
|
22 |
+
<div id="synchronisation">
|
23 |
+
<label for="sync">Sync freq:</label>
|
24 |
+
<input type="range" id="sync" min="1" max="200" step="10" value="100" />
|
25 |
+
<span id="syncValue">100</span>
|
26 |
+
<br />
|
27 |
+
<label for="motor_0">Motor 0 synced:</label>
|
28 |
+
<input type="checkbox" id="motor_0_synced" />
|
29 |
+
<br />
|
30 |
+
<label for="motor_1">Motor 1 synced:</label>
|
31 |
+
<input type="checkbox" id="motor_1_synced" />
|
32 |
+
<br />
|
33 |
+
<label for="motor_2">Motor 2 synced:</label>
|
34 |
+
<input type="checkbox" id="motor_2_synced" checked />
|
35 |
+
<br />
|
36 |
+
<label for="motor_3">Motor 3 synced:</label>
|
37 |
+
<input type="checkbox" id="motor_3_synced" checked />
|
38 |
+
<br />
|
39 |
+
<label for="motor_4">Motor 4 synced:</label>
|
40 |
+
<input type="checkbox" id="motor_4_synced" />
|
41 |
+
<br />
|
42 |
+
<label for="motor_5">Motor 5 synced:</label>
|
43 |
+
<input type="checkbox" id="motor_5_synced" />
|
44 |
+
<br />
|
45 |
+
</div>
|
46 |
+
|
47 |
+
<h1>Debug</h1>
|
48 |
+
<div id="debug">
|
49 |
+
<label for="verbose">Verbose:</label>
|
50 |
+
<input type="checkbox" id="verbose" />
|
51 |
+
</div>
|
52 |
+
|
53 |
+
<!-- <script src="sinus.js"></script> -->
|
54 |
+
|
55 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
|
56 |
+
<script src="sinus-io.js"></script>
|
57 |
+
|
58 |
+
</body>
|
59 |
+
|
60 |
+
</html>
|
static/sinus.js
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
window.onload = () => {
|
2 |
+
const robotIp = "http://127.0.0.1:5001"
|
3 |
+
|
4 |
+
const amplitudeSlider = document.getElementById("amplitude");
|
5 |
+
const amplitudeValue = document.getElementById("amplitudeValue");
|
6 |
+
const frequencySlider = document.getElementById("frequency");
|
7 |
+
const frequencyValue = document.getElementById("frequencyValue");
|
8 |
+
const syncSlider = document.getElementById("sync");
|
9 |
+
const syncValue = document.getElementById("syncValue");
|
10 |
+
|
11 |
+
const motor_0Synced = document.getElementById("motor_0_synced");
|
12 |
+
const motor_1Synced = document.getElementById("motor_1_synced");
|
13 |
+
const motor_2Synced = document.getElementById("motor_2_synced");
|
14 |
+
const motor_3Synced = document.getElementById("motor_3_synced");
|
15 |
+
const motor_4Synced = document.getElementById("motor_4_synced");
|
16 |
+
const motor_5Synced = document.getElementById("motor_5_synced");
|
17 |
+
|
18 |
+
const verboseCheckbox = document.getElementById("verbose");
|
19 |
+
|
20 |
+
amplitudeSlider.addEventListener("input", function () {
|
21 |
+
amplitudeValue.textContent = amplitudeSlider.value;
|
22 |
+
});
|
23 |
+
|
24 |
+
frequencySlider.addEventListener("input", function () {
|
25 |
+
frequencyValue.textContent = frequencySlider.value;
|
26 |
+
});
|
27 |
+
|
28 |
+
syncSlider.addEventListener("input", function () {
|
29 |
+
syncValue.textContent = syncSlider.value;
|
30 |
+
});
|
31 |
+
|
32 |
+
let t0 = Date.now();
|
33 |
+
|
34 |
+
const syncStep = () => {
|
35 |
+
let t = Date.now() - t0;
|
36 |
+
|
37 |
+
const value = computeSinValue(
|
38 |
+
frequencySlider.value,
|
39 |
+
amplitudeSlider.value,
|
40 |
+
t / 1000
|
41 |
+
);
|
42 |
+
|
43 |
+
let goals = {};
|
44 |
+
if (motor_0Synced.checked) {
|
45 |
+
goals["motor_0"] = value;
|
46 |
+
}
|
47 |
+
if (motor_1Synced.checked) {
|
48 |
+
goals["motor_1"] = value;
|
49 |
+
}
|
50 |
+
if (motor_2Synced.checked) {
|
51 |
+
goals["motor_2"] = value;
|
52 |
+
}
|
53 |
+
if (motor_3Synced.checked) {
|
54 |
+
goals["motor_3"] = value;
|
55 |
+
}
|
56 |
+
if (motor_4Synced.checked) {
|
57 |
+
goals["motor_4"] = value;
|
58 |
+
}
|
59 |
+
if (motor_5Synced.checked) {
|
60 |
+
goals["motor_5"] = value;
|
61 |
+
}
|
62 |
+
|
63 |
+
if (verboseCheckbox.checked) {
|
64 |
+
console.log("Sending goals:", goals);
|
65 |
+
}
|
66 |
+
|
67 |
+
if (Object.keys(goals).length !== 0) {
|
68 |
+
fetch(`${robotIp}/motor_control`, {
|
69 |
+
method: "POST",
|
70 |
+
headers: { "Content-Type": "application/json" },
|
71 |
+
body: JSON.stringify(goals),
|
72 |
+
}).catch(err => console.error("Send error:", err));
|
73 |
+
}
|
74 |
+
const syncPeriod = 1000.0 / syncSlider.value;
|
75 |
+
setTimeout(syncStep, syncPeriod);
|
76 |
+
};
|
77 |
+
syncStep();
|
78 |
+
|
79 |
+
// Compute the current sin value at a given frequency
|
80 |
+
function computeSinValue(frequency, amplitude, time) {
|
81 |
+
return amplitude * Math.sin(2 * Math.PI * frequency * time);
|
82 |
+
}
|
83 |
+
|
84 |
+
};
|