oleg-02/src/web_ui.cpp
2026-01-22 23:15:20 +03:00

147 lines
3.7 KiB
C++

#include <Arduino.h>
#include "web_ui.h"
const char INDEX_HTML[] PROGMEM = R"HTML(
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8">
<title>ESP32 Robot</title>
<style>
body { font-family: sans-serif; margin: 16px; }
.grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; max-width: 420px; }
button { font-size: 18px; padding: 18px 10px; }
.wide { grid-column: span 3; }
.row2 { margin-top: 14px; max-width: 420px; }
input[type=range]{ width: 100%; }
.muted { opacity: 0.7; font-size: 13px; }
.status { margin-top: 10px; font-family: monospace; }
</style>
</head>
<body>
<h2>ESP32 Robot</h2>
<div class="grid">
<div></div>
<button id="btnF"></button>
<div></div>
<button id="btnL"></button>
<button id="btnS"></button>
<button id="btnR"></button>
<div></div>
<button id="btnB"></button>
<div></div>
<button class="wide" id="btnStop">STOP</button>
</div>
<div class="row2">
<label>Speed: <span id="spv">150</span></label>
<input id="speed" type="range" min="0" max="255" value="150" />
<div class="muted">Tip: удерживай стрелку — робот едет. Отпустил — STOP.</div>
<div class="status" id="st"></div>
</div>
<div class="row2">
<button onclick="setMode('IDLE')">IDLE</button>
<button onclick="setMode('MANUAL')">MANUAL</button>
<button onclick="setMode('AUTO')">AUTO</button>
</div>
<script>
const st = (t) => document.getElementById('st').textContent = t;
let holdTimer = null;
async function cmd(c){
try{
const r = await fetch(`/cmd?c=${c}`);
st(await r.text());
}catch(e){
st('ERR');
}
}
async function setSpeed(v){
document.getElementById('spv').textContent = v;
try{
const r = await fetch(`/speed?l=${v}&r=${v}`);
st(await r.text());
}catch(e){
st('ERR');
}
}
async function setMode(m){
try{
const r = await fetch(`/mode?m=${m}`);
st(await r.text());
}catch(e){
st('ERR');
}
}
async function updateStatus(){
try{
const r = await fetch('/status');
const j = await r.json();
st(
`MODE=${j.mode} | ` +
`L=${j.speedL} R=${j.speedR} | ` +
`RSSI=${j.rssi}dBm | ` +
`UP=${Math.floor(j.uptime/1000)}s`
);
}catch(e){
st('STATUS ERR');
}
}
setInterval(updateStatus, 500);
// управление "пока держишь" (heartbeat)
function bindHold(btn, onPressCmd){
const press = (e)=>{
e.preventDefault();
cmd(onPressCmd);
holdTimer = setInterval(() => {
cmd(onPressCmd);
}, 200); // каждые 200 мс
};
const release = (e)=>{
e.preventDefault();
if (holdTimer) {
clearInterval(holdTimer);
holdTimer = null;
}
cmd('STOP');
};
btn.addEventListener('mousedown', press);
btn.addEventListener('touchstart', press, {passive:false});
btn.addEventListener('mouseup', release);
btn.addEventListener('mouseleave', release);
btn.addEventListener('touchend', release);
btn.addEventListener('touchcancel', release);
}
bindHold(document.getElementById('btnF'), 'FWD');
bindHold(document.getElementById('btnB'), 'BACK');
bindHold(document.getElementById('btnL'), 'LEFT');
bindHold(document.getElementById('btnR'), 'RIGHT');
document.getElementById('btnS').onclick = ()=>cmd('STOP');
document.getElementById('btnStop').onclick = ()=>cmd('STOP');
const slider = document.getElementById('speed');
slider.addEventListener('input', ()=> setSpeed(slider.value));
st('Ready');
</script>
</body>
</html>
)HTML";