Stream slave accel via ESP-NOW with master snapshot cache.
Slaves push BMA456 samples at 16ms when enabled; the master caches per client and exposes ACCEL_SNAPSHOT and ACCEL_STREAM over UART. goTool adds dashboard stream controls, HTTP accel-stream routes, and an external WebSocket API with per-connection receive/interval and slave stream commands. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+76
-3
@@ -55,11 +55,12 @@
|
||||
.badge-offline { background: #5c6570; color: #f0f3f5; }
|
||||
.badge.bg-secondary { background: #4a5560 !important; color: #f0f3f5; }
|
||||
|
||||
.mac {
|
||||
.mac, .accel {
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 0.85rem;
|
||||
color: var(--pp-accent);
|
||||
}
|
||||
.accel-stale { color: var(--pp-text-muted); }
|
||||
|
||||
.pp-table {
|
||||
--bs-table-color: var(--pp-text);
|
||||
@@ -265,7 +266,9 @@
|
||||
<span class="badge bg-secondary" x-text="(state.clients || []).length + ' registered'"></span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-muted small px-3 pt-2 mb-0">Slaves per ESP-NOW — Master-Deadzone bleibt separat.</p>
|
||||
<p class="text-muted small px-3 pt-2 mb-0">
|
||||
Accel-Stream pro Slave per „Stream an“ aktivieren (~16 ms ESP-NOW). Ohne Aktivierung keine Werte.
|
||||
</p>
|
||||
<div class="card-body p-0 pt-2">
|
||||
<div class="table-responsive">
|
||||
<table class="table pp-table table-hover">
|
||||
@@ -276,12 +279,14 @@
|
||||
<th>Ver</th>
|
||||
<th>Status</th>
|
||||
<th>Deadzone</th>
|
||||
<th>Accel (LSB)</th>
|
||||
<th>Stream</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-if="!(state.clients || []).length">
|
||||
<tr><td colspan="6" class="text-muted text-center py-4">No clients</td></tr>
|
||||
<tr><td colspan="8" class="text-muted text-center py-4">No clients</td></tr>
|
||||
</template>
|
||||
<template x-for="c in (state.clients || [])" :key="c.id + c.mac">
|
||||
<tr>
|
||||
@@ -294,6 +299,20 @@
|
||||
x-text="c.available ? 'available' : 'inactive'"></span>
|
||||
</td>
|
||||
<td x-text="c.deadzone != null ? c.deadzone : '—'"></td>
|
||||
<td>
|
||||
<span class="accel"
|
||||
:class="accelCellClass(c)"
|
||||
x-text="formatAccel(c)"
|
||||
:title="accelTitle(c)"></span>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button"
|
||||
class="btn btn-sm"
|
||||
:class="c.accel_stream ? 'btn-warning' : 'btn-outline-success'"
|
||||
@click="setAccelStream(c.id, !c.accel_stream)"
|
||||
:disabled="busy || !state.uart_connected || !c.available"
|
||||
x-text="c.accel_stream ? 'Aus' : 'An'"></button>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-wrap gap-1 align-items-center">
|
||||
<input type="number" class="form-control form-control-sm dz-input"
|
||||
@@ -496,6 +515,22 @@
|
||||
if (!hex || hex.length !== 12) return hex || '';
|
||||
return hex.match(/.{2}/g).join(':');
|
||||
},
|
||||
formatAccel(c) {
|
||||
if (!c?.accel_stream) return '—';
|
||||
if (!c?.accel_valid) return '…';
|
||||
return `${c.accel_x} / ${c.accel_y} / ${c.accel_z}`;
|
||||
},
|
||||
accelTitle(c) {
|
||||
if (!c?.accel_stream) return 'Accel-Stream nicht aktiviert';
|
||||
if (!c?.accel_valid) return 'Warte auf erste ESP-NOW Samples…';
|
||||
const age = c.accel_age_ms != null ? `${c.accel_age_ms} ms alt` : '';
|
||||
return `x=${c.accel_x} y=${c.accel_y} z=${c.accel_z} (raw LSB, ±2g)${age ? ' · ' + age : ''}`;
|
||||
},
|
||||
accelCellClass(c) {
|
||||
if (!c?.accel_valid) return 'accel-stale';
|
||||
if (c.accel_age_ms != null && c.accel_age_ms > 200) return 'accel-stale';
|
||||
return '';
|
||||
},
|
||||
formatSize(n) {
|
||||
if (n == null) return '';
|
||||
if (n < 1024) return n + ' B';
|
||||
@@ -732,6 +767,44 @@
|
||||
async setMasterDeadzone() {
|
||||
await this.setDeadzone(0, this.masterDz);
|
||||
},
|
||||
patchClientAccelStream(clientId, enabled) {
|
||||
const clients = (this.state.clients || []).map((c) => {
|
||||
if (c.id !== clientId) {
|
||||
return c;
|
||||
}
|
||||
const next = { ...c, accel_stream: enabled };
|
||||
if (!enabled) {
|
||||
next.accel_valid = false;
|
||||
next.accel_x = 0;
|
||||
next.accel_y = 0;
|
||||
next.accel_z = 0;
|
||||
next.accel_age_ms = 0;
|
||||
}
|
||||
return next;
|
||||
});
|
||||
this.state = { ...this.state, clients };
|
||||
},
|
||||
async setAccelStream(clientId, enable) {
|
||||
this.busy = true;
|
||||
try {
|
||||
const r = await fetch(`/api/clients/${clientId}/accel-stream`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ enable: enable })
|
||||
});
|
||||
const data = await r.json();
|
||||
if (!r.ok || !data.success) {
|
||||
this.flash(data.error || `Accel-Stream Slave ${clientId} fehlgeschlagen`, false);
|
||||
return;
|
||||
}
|
||||
this.patchClientAccelStream(clientId, !!data.enabled);
|
||||
this.flash(`Slave ${clientId}: Accel-Stream ${data.enabled ? 'an' : 'aus'}`, true);
|
||||
} catch (e) {
|
||||
this.flash(String(e), false);
|
||||
} finally {
|
||||
this.busy = false;
|
||||
}
|
||||
},
|
||||
async setDeadzoneAll(deadzone) {
|
||||
if (deadzone == null || deadzone < 0) {
|
||||
this.flash('Ungültiger Deadzone-Wert', false);
|
||||
|
||||
Reference in New Issue
Block a user