mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 01:50:47 -04:00
might ditch the esp32, this is the best ive gotten with it.
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
const socket = io();
|
||||
|
||||
const DRIVE_SPEED = 250;
|
||||
const TURN_SPEED = 200;
|
||||
const STATUS_FLAGS = [
|
||||
{ bit: 0x01, label: 'wifi' },
|
||||
{ bit: 0x02, label: 'oi-ready' },
|
||||
{ bit: 0x04, label: 'sensors' },
|
||||
];
|
||||
|
||||
const state = {
|
||||
robots: [],
|
||||
selectedRobotId: null,
|
||||
telemetry: {},
|
||||
activeKeys: new Set(),
|
||||
};
|
||||
|
||||
const robotSelect = document.getElementById('robotSelect');
|
||||
const telemetrySummary = document.getElementById('telemetrySummary');
|
||||
const sensorList = document.getElementById('sensorList');
|
||||
const safeModeBtn = document.getElementById('safeModeBtn');
|
||||
const fullModeBtn = document.getElementById('fullModeBtn');
|
||||
const enableOiBtn = document.getElementById('enableOiBtn');
|
||||
const seekDockBtn = document.getElementById('seekDockBtn');
|
||||
const playSongBtn = document.getElementById('playSongBtn');
|
||||
const songSlotInput = document.getElementById('songSlot');
|
||||
|
||||
function flattenSensors(obj, prefix = '') {
|
||||
const result = {};
|
||||
Object.entries(obj || {}).forEach(([key, value]) => {
|
||||
const path = prefix ? `${prefix}.${key}` : key;
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
Object.assign(result, flattenSensors(value, path));
|
||||
} else {
|
||||
result[path] = value;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function renderRobots() {
|
||||
robotSelect.innerHTML = '';
|
||||
state.robots.forEach((robot) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = robot.id;
|
||||
option.textContent = robot.id;
|
||||
if (robot.id === state.selectedRobotId) {
|
||||
option.selected = true;
|
||||
}
|
||||
robotSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
function renderTelemetry() {
|
||||
const telemetry = state.telemetry[state.selectedRobotId];
|
||||
if (!telemetry) {
|
||||
telemetrySummary.textContent = 'No telemetry';
|
||||
sensorList.textContent = '';
|
||||
return;
|
||||
}
|
||||
const { header, trailer, sensors } = telemetry;
|
||||
const flags = STATUS_FLAGS
|
||||
.filter((flag) => header.statusBits & flag.bit)
|
||||
.map((flag) => flag.label)
|
||||
.join(', ');
|
||||
const summaryLines = [
|
||||
`Seq: ${header.seq}`,
|
||||
`Uptime: ${header.uptimeMs} ms`,
|
||||
`Last Control Age: ${header.lastControlAgeMs} ms`,
|
||||
`WiFi RSSI: ${header.wifiRssiDbm} dBm`,
|
||||
`Status: ${flags || 'none'}`,
|
||||
`Applied mm/s: L ${trailer.appliedLeftMmps} | R ${trailer.appliedRightMmps}`,
|
||||
`Dropped control packets: ${trailer.droppedControlPackets}`,
|
||||
];
|
||||
telemetrySummary.textContent = summaryLines.join('\n');
|
||||
|
||||
if (sensors) {
|
||||
const flat = flattenSensors(sensors);
|
||||
sensorList.textContent = Object.entries(flat)
|
||||
.map(([key, value]) => `${key}: ${value}`)
|
||||
.join('\n');
|
||||
} else {
|
||||
sensorList.textContent = 'Sensor block missing';
|
||||
}
|
||||
}
|
||||
|
||||
function broadcastDrive() {
|
||||
if (!state.selectedRobotId) {
|
||||
return;
|
||||
}
|
||||
const vectors = { w: 0, a: 0, s: 0, d: 0 };
|
||||
state.activeKeys.forEach((key) => {
|
||||
if (vectors[key] !== undefined) {
|
||||
vectors[key] = 1;
|
||||
}
|
||||
});
|
||||
|
||||
let left = 0;
|
||||
let right = 0;
|
||||
if (vectors.w) {
|
||||
left += DRIVE_SPEED;
|
||||
right += DRIVE_SPEED;
|
||||
}
|
||||
if (vectors.s) {
|
||||
left -= DRIVE_SPEED;
|
||||
right -= DRIVE_SPEED;
|
||||
}
|
||||
if (vectors.a) {
|
||||
left -= TURN_SPEED;
|
||||
right += TURN_SPEED;
|
||||
}
|
||||
if (vectors.d) {
|
||||
left += TURN_SPEED;
|
||||
right -= TURN_SPEED;
|
||||
}
|
||||
|
||||
socket.emit('drive', {
|
||||
robotId: state.selectedRobotId,
|
||||
left,
|
||||
right,
|
||||
});
|
||||
}
|
||||
|
||||
function handleKey(event, isDown) {
|
||||
const key = event.key.toLowerCase();
|
||||
if (!['w', 'a', 's', 'd'].includes(key)) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
if (isDown) {
|
||||
state.activeKeys.add(key);
|
||||
} else {
|
||||
state.activeKeys.delete(key);
|
||||
}
|
||||
broadcastDrive();
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', (event) => handleKey(event, true));
|
||||
document.addEventListener('keyup', (event) => handleKey(event, false));
|
||||
|
||||
robotSelect.addEventListener('change', (event) => {
|
||||
state.selectedRobotId = event.target.value;
|
||||
renderTelemetry();
|
||||
});
|
||||
|
||||
safeModeBtn.addEventListener('click', () => {
|
||||
if (!state.selectedRobotId) return;
|
||||
socket.emit('mode', { robotId: state.selectedRobotId, mode: 'SAFE' });
|
||||
});
|
||||
|
||||
fullModeBtn.addEventListener('click', () => {
|
||||
if (!state.selectedRobotId) return;
|
||||
socket.emit('mode', { robotId: state.selectedRobotId, mode: 'FULL' });
|
||||
});
|
||||
|
||||
enableOiBtn.addEventListener('click', () => {
|
||||
if (!state.selectedRobotId) return;
|
||||
socket.emit('enableOi', { robotId: state.selectedRobotId });
|
||||
});
|
||||
|
||||
seekDockBtn.addEventListener('click', () => {
|
||||
if (!state.selectedRobotId) return;
|
||||
socket.emit('seekDock', { robotId: state.selectedRobotId });
|
||||
});
|
||||
|
||||
playSongBtn.addEventListener('click', () => {
|
||||
if (!state.selectedRobotId) return;
|
||||
const slot = Number(songSlotInput.value) || 0;
|
||||
socket.emit('playSong', { robotId: state.selectedRobotId, slot });
|
||||
});
|
||||
|
||||
socket.on('robots', (robots) => {
|
||||
state.robots = robots;
|
||||
if (!state.selectedRobotId && robots.length > 0) {
|
||||
state.selectedRobotId = robots[0].id;
|
||||
}
|
||||
renderRobots();
|
||||
renderTelemetry();
|
||||
});
|
||||
|
||||
socket.on('telemetrySnapshot', (entries) => {
|
||||
entries.forEach(({ robotId, telemetry }) => {
|
||||
state.telemetry[robotId] = telemetry;
|
||||
});
|
||||
renderTelemetry();
|
||||
});
|
||||
|
||||
socket.on('telemetry', ({ robotId, telemetry }) => {
|
||||
state.telemetry[robotId] = telemetry;
|
||||
if (robotId === state.selectedRobotId) {
|
||||
renderTelemetry();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user