Optimize SPI driver & web_server UIUX
This commit is contained in:
parent
49cdb5bbae
commit
8278fae0ee
@ -476,7 +476,7 @@ typedef struct mcp2515_dev
|
||||
typedef struct spi_description
|
||||
{
|
||||
char *spidev_path;
|
||||
uint8_t fd;
|
||||
int fd; /* spidev file descriptor; -1 = not open */
|
||||
uint8_t mode;
|
||||
uint8_t bits;
|
||||
uint32_t speed;
|
||||
|
||||
@ -266,9 +266,9 @@ static void http_post_file(const char *filepath)
|
||||
printf("[EVT] upload: %s sent OK\n", basename);
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════════════
|
||||
/*
|
||||
* Helpers
|
||||
* ═══════════════════════════════════════════════════════════════════════════ */
|
||||
* */
|
||||
|
||||
#define TZ_OFFSET_SEC (8 * 3600)
|
||||
|
||||
|
||||
@ -337,11 +337,14 @@ static void spi_autoprobe(mcp2515_dev *mcp2515_device)
|
||||
int spi_initial(mcp2515_dev *mcp2515_device)
|
||||
{
|
||||
int ret = 0;
|
||||
int bind_ok = 0;
|
||||
|
||||
bind_ok = ensure_spidev_binding(mcp2515_device->spi_dev->spidev_path);
|
||||
if (bind_ok < 0) {
|
||||
return -8;
|
||||
/* Close existing fd before reopening to prevent fd leak.
|
||||
* ensure_spidev_binding() is intentionally skipped here: the kernel
|
||||
* already exposes /dev/spidev1.0 at boot, so rebinding is unnecessary
|
||||
* and causes ESHUTDOWN errors on the old fd during CAN error recovery. */
|
||||
if (mcp2515_device->spi_dev->fd >= 0) {
|
||||
close(mcp2515_device->spi_dev->fd);
|
||||
mcp2515_device->spi_dev->fd = -1;
|
||||
}
|
||||
|
||||
int fd = open(mcp2515_device->spi_dev->spidev_path, O_RDWR);
|
||||
@ -349,7 +352,7 @@ int spi_initial(mcp2515_dev *mcp2515_device)
|
||||
printf("[SPI] failed to open %s (errno=%d)\n", mcp2515_device->spi_dev->spidev_path, errno);
|
||||
return -1;
|
||||
}
|
||||
mcp2515_device->spi_dev->fd = (uint8_t)fd;
|
||||
mcp2515_device->spi_dev->fd = fd;
|
||||
|
||||
ret = ioctl(fd, SPI_IOC_WR_MODE, &(mcp2515_device->spi_dev->mode));
|
||||
if (ret == -1) { printf("[SPI] WR_MODE failed (errno=%d)\n", errno); return -2; }
|
||||
@ -446,6 +449,7 @@ mcp2515_dev* new_mcp2515_dev(char *spidev_path)
|
||||
mcp2515_dev* mcp2515_device = (mcp2515_dev*)malloc(sizeof(mcp2515_dev));
|
||||
mcp2515_device->spi_dev = (spi_description*)malloc(sizeof(spi_description));
|
||||
mcp2515_device->spi_dev->spidev_path = spidev_path;
|
||||
mcp2515_device->spi_dev->fd = -1; /* not open yet */
|
||||
mcp2515_device->spi_dev->mode = 0;
|
||||
mcp2515_device->spi_dev->bits = 8;
|
||||
mcp2515_device->spi_dev->speed = 1000000;
|
||||
|
||||
195
web_serve.py
195
web_serve.py
@ -415,55 +415,6 @@ def _verify_firmware_running(tn):
|
||||
ok = any("FW PID:" in ln for ln in lines)
|
||||
return ok, lines
|
||||
|
||||
def _spi_setup_on_device(tn, spi_node="spi1.0"):
|
||||
"""Try to switch SPI node from display driver to spidev and report status."""
|
||||
steps = [
|
||||
("SPI preflight: listing SPI devices...",
|
||||
"ls -l /sys/bus/spi/devices 2>/dev/null || true", 10),
|
||||
("SPI preflight: current modalias...",
|
||||
f"cat /sys/bus/spi/devices/{spi_node}/modalias 2>/dev/null || echo 'modalias missing'", 10),
|
||||
("SPI preflight: current driver link...",
|
||||
f"readlink /sys/bus/spi/devices/{spi_node}/driver 2>/dev/null || echo 'driver link missing'", 10),
|
||||
("SPI setup: unbind current driver...",
|
||||
f"DRV=$(basename $(readlink /sys/bus/spi/devices/{spi_node}/driver 2>/dev/null) 2>/dev/null || true); "
|
||||
f"echo DRIVER=$DRV; "
|
||||
f"[ -n \"$DRV\" ] && echo {spi_node} > /sys/bus/spi/drivers/$DRV/unbind 2>/dev/null || true; "
|
||||
"echo 'unbind done'", 12),
|
||||
("SPI setup: set driver_override=spidev (if supported)...",
|
||||
f"if [ -e /sys/bus/spi/devices/{spi_node}/driver_override ]; then "
|
||||
f"echo spidev > /sys/bus/spi/devices/{spi_node}/driver_override && echo 'override OK'; "
|
||||
"else echo 'driver_override missing'; fi", 10),
|
||||
("SPI setup: bind spidev...",
|
||||
f"echo {spi_node} > /sys/bus/spi/drivers/spidev/bind 2>/dev/null || true; echo 'bind done'", 10),
|
||||
("SPI verify: modalias + driver + /dev/spidev*",
|
||||
f"echo 'modalias:'; cat /sys/bus/spi/devices/{spi_node}/modalias 2>/dev/null || true; "
|
||||
f"echo 'driver:'; readlink /sys/bus/spi/devices/{spi_node}/driver 2>/dev/null || true; "
|
||||
"echo '/dev:'; ls -l /dev/spidev* 2>/dev/null || echo 'no /dev/spidev'", 12),
|
||||
]
|
||||
|
||||
out = []
|
||||
for label, cmd, timeout in steps:
|
||||
out.append(("log", label))
|
||||
out.append(("prompt", f"$ {cmd}"))
|
||||
for ln in _telnet_run(tn, cmd, timeout=timeout):
|
||||
kind = "ok"
|
||||
low = ln.lower()
|
||||
if "missing" in low or "no /dev/spidev" in low:
|
||||
kind = "warn"
|
||||
out.append((kind, ln))
|
||||
|
||||
# Final health hint
|
||||
final = _telnet_run(
|
||||
tn,
|
||||
f"M=$(cat /sys/bus/spi/devices/{spi_node}/modalias 2>/dev/null || true); "
|
||||
"echo FINAL_MODALIAS=$M; "
|
||||
"echo $M | grep -q dh2228fv && echo 'SPI_BINDING_BLOCKED' || echo 'SPI_BINDING_OK'",
|
||||
timeout=10,
|
||||
)
|
||||
for ln in final:
|
||||
out.append(("warn" if "BLOCKED" in ln else "ok", ln))
|
||||
return out
|
||||
|
||||
# ── API: deploy via Telnet (SSE) ──────────────────────────────────────────────
|
||||
@app.route("/api/deploy/run")
|
||||
def api_deploy_run():
|
||||
@ -473,7 +424,6 @@ def api_deploy_run():
|
||||
port = int(request.args.get("port", cfg["port"]))
|
||||
out_rtsp = request.args.get("out_rtsp", "1") == "1"
|
||||
out_hdmi = request.args.get("out_hdmi", "0") == "1"
|
||||
spi_fix = request.args.get("spi_fix", "1") == "1"
|
||||
base = f"http://{h_ip}:{port}"
|
||||
bd = BIN_DIR_DEVICE
|
||||
fw = FW_PATH_DEVICE
|
||||
@ -540,11 +490,6 @@ def api_deploy_run():
|
||||
_drain_shell(tn)
|
||||
yield sse(f"Connected to {kl_ip}", "ok")
|
||||
|
||||
if spi_fix:
|
||||
yield sse("Running SPI setup preflight (spi1.0 -> spidev)...")
|
||||
for kind, text in _spi_setup_on_device(tn, "spi1.0"):
|
||||
yield sse(text, kind)
|
||||
|
||||
for label, cmd, bg, timeout in steps:
|
||||
yield sse(label)
|
||||
yield sse(f"$ {cmd}", "prompt")
|
||||
@ -651,131 +596,6 @@ def api_deploy_startsh():
|
||||
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})
|
||||
|
||||
# ── API: SPI setup via Telnet (SSE) ─────────────────────────────────────────
|
||||
@app.route("/api/spi_setup/run")
|
||||
def api_spi_setup_run():
|
||||
cfg = load_config()
|
||||
kl_ip = request.args.get("ip", cfg["kl630_ip"])
|
||||
spi_node = request.args.get("node", "spi1.0")
|
||||
|
||||
def generate():
|
||||
yield sse(f"Connecting to {kl_ip}:23 via Telnet...")
|
||||
try:
|
||||
tn = _telnet_connect(kl_ip)
|
||||
_drain_shell(tn)
|
||||
yield sse("Connected.", "ok")
|
||||
for kind, text in _spi_setup_on_device(tn, spi_node):
|
||||
yield sse(text, kind)
|
||||
tn.close()
|
||||
yield sse("SPI setup flow complete.", "ok")
|
||||
except Exception as e:
|
||||
yield sse(f"Telnet error: {e}", "error")
|
||||
yield sse_done()
|
||||
|
||||
return Response(generate(), mimetype="text/event-stream",
|
||||
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})
|
||||
|
||||
# ── API: BT UART one-time baud setup via Telnet (SSE) ────────────────────────
|
||||
@app.route("/api/bt_setup/run")
|
||||
def api_bt_setup_run():
|
||||
cfg = load_config()
|
||||
kl_ip = request.args.get("ip", cfg["kl630_ip"])
|
||||
bd = BIN_DIR_DEVICE
|
||||
fw = FW_PATH_DEVICE
|
||||
restart_cmd = (f"cd {bd} && rm -f /dev/shm/* && "
|
||||
f"nohup sh ./ini/demo_rtsp.sh > /tmp/fw.log 2>&1 &")
|
||||
|
||||
def generate():
|
||||
yield sse("⚠ 請先關閉手機 nRF Connect 並確保 DX-BT24 未連線,再繼續!", "warn")
|
||||
yield sse(f"Connecting to {kl_ip}:23 via Telnet...")
|
||||
try:
|
||||
tn = _telnet_connect(kl_ip)
|
||||
_drain_shell(tn)
|
||||
yield sse("Connected.", "ok")
|
||||
|
||||
# Step 1: kill firmware so UART is free
|
||||
yield sse("Step 1: stopping firmware...")
|
||||
_telnet_run(tn, "killall -9 kp_firmware_host_stream 2>/dev/null; "
|
||||
"killall -9 rtsps 2>/dev/null; sleep 1; echo stopped", timeout=10)
|
||||
|
||||
# Step 2: probe current baud by reading UART response
|
||||
yield sse("Step 2: probing module baud rate...")
|
||||
probe_115200 = (
|
||||
"stty -F /dev/ttyS1 115200 raw cs8 -parenb -cstopb -echo; "
|
||||
"cat /dev/ttyS1 > /tmp/_bt_resp.bin & CPID=$!; "
|
||||
"printf 'AT+BAUD\\r\\n' > /dev/ttyS1; sleep 1; "
|
||||
"kill $CPID 2>/dev/null; "
|
||||
"BYTES=$(wc -c < /tmp/_bt_resp.bin 2>/dev/null || echo 0); "
|
||||
"echo \"115200_bytes=$BYTES\"; "
|
||||
"[ \"$BYTES\" -gt 0 ] && cat /tmp/_bt_resp.bin || true"
|
||||
)
|
||||
resp_115200 = _telnet_run(tn, probe_115200, timeout=8)
|
||||
for ln in resp_115200:
|
||||
yield sse(ln, "ok")
|
||||
|
||||
got_115200 = any("115200_bytes=" in ln and not ln.endswith("=0") for ln in resp_115200)
|
||||
|
||||
if got_115200:
|
||||
yield sse("Module responded at 115200 — already configured!", "ok")
|
||||
else:
|
||||
yield sse("No response at 115200 — trying 9600...", "warn")
|
||||
probe_9600 = (
|
||||
"stty -F /dev/ttyS1 9600 raw cs8 -parenb -cstopb -echo; "
|
||||
"cat /dev/ttyS1 > /tmp/_bt_resp.bin & CPID=$!; "
|
||||
"printf 'AT+BAUD\\r\\n' > /dev/ttyS1; sleep 1; "
|
||||
"kill $CPID 2>/dev/null; "
|
||||
"BYTES=$(wc -c < /tmp/_bt_resp.bin 2>/dev/null || echo 0); "
|
||||
"echo \"9600_bytes=$BYTES\"; "
|
||||
"[ \"$BYTES\" -gt 0 ] && cat /tmp/_bt_resp.bin || true"
|
||||
)
|
||||
resp_9600 = _telnet_run(tn, probe_9600, timeout=8)
|
||||
for ln in resp_9600:
|
||||
yield sse(ln, "ok")
|
||||
|
||||
got_9600 = any("9600_bytes=" in ln and not ln.endswith("=0") for ln in resp_9600)
|
||||
|
||||
if got_9600:
|
||||
yield sse("Module at 9600 — sending AT+BAUD7 to upgrade...", "ok")
|
||||
upgrade = (
|
||||
"stty -F /dev/ttyS1 9600 raw cs8 -parenb -cstopb -echo; "
|
||||
"printf 'AT+BAUD7\\r\\n' > /dev/ttyS1; sleep 0.3; "
|
||||
"stty -F /dev/ttyS1 115200 raw cs8 -parenb -cstopb -echo; "
|
||||
"printf 'AT+RESET\\r\\n' > /dev/ttyS1; sleep 1.5; "
|
||||
"echo 'upgrade_sent'"
|
||||
)
|
||||
for ln in _telnet_run(tn, upgrade, timeout=8):
|
||||
yield sse(ln, "ok")
|
||||
yield sse("AT+BAUD7 + AT+RESET sent. Module rebooting...", "ok")
|
||||
else:
|
||||
yield sse("No response at 9600 either.", "warn")
|
||||
yield sse("Can't probe module — it may be in BLE transparent mode or disconnected.", "warn")
|
||||
yield sse("Make sure phone is DISCONNECTED from DX-BT24, then run this again.", "error")
|
||||
|
||||
# Step 3: send test string at 115200 to verify
|
||||
yield sse("Step 3: sending test ping at 115200...")
|
||||
test_cmd = (
|
||||
"stty -F /dev/ttyS1 115200 raw cs8 -parenb -cstopb -echo; "
|
||||
"printf '{\"class\":\"test\",\"level\":0}' > /dev/ttyS1; "
|
||||
"echo 'ping_sent'"
|
||||
)
|
||||
for ln in _telnet_run(tn, test_cmd, timeout=5):
|
||||
yield sse(ln, "ok")
|
||||
|
||||
# Step 4: restart firmware normally (bt_at_probe stays 0)
|
||||
yield sse("Step 4: restarting firmware...")
|
||||
_telnet_run(tn, "killall -9 kp_firmware_host_stream 2>/dev/null; "
|
||||
"killall -9 rtsps 2>/dev/null; sleep 1; rm -f /dev/shm/*", timeout=10)
|
||||
_telnet_run_bg(tn, restart_cmd)
|
||||
yield sse("Firmware restarted.", "ok")
|
||||
|
||||
tn.close()
|
||||
yield sse('完成!現在連上手機 → 訂閱 Notify → 如果看到 {"class":"test","level":0} 或 {"class":"boot","level":0} 表示成功', "ok")
|
||||
except Exception as e:
|
||||
yield sse(f"Telnet error: {e}", "error")
|
||||
yield sse_done()
|
||||
|
||||
return Response(generate(), mimetype="text/event-stream",
|
||||
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})
|
||||
|
||||
# ── API: RTSP → MJPEG proxy (via opencv-python, no system ffmpeg needed) ──────
|
||||
_stream_active = False
|
||||
|
||||
@ -1789,14 +1609,6 @@ input:checked+.slider:before{transform:translateX(16px);background:#fff}
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
||||
Update start.sh
|
||||
</button>
|
||||
<button class="btn btn-warn" id="btn-bt-setup" onclick="runAction('bt_setup')">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M6.5 6.5l11 11M17.5 6.5l-11 11"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/></svg>
|
||||
BT 初始化
|
||||
</button>
|
||||
<button class="btn btn-ghost" id="btn-spi-setup" onclick="runAction('spi_setup')">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M7 7h10v10H7z"/><path d="M3 10h4M17 10h4M10 3v4M10 17v4"/></svg>
|
||||
SPI 初始化
|
||||
</button>
|
||||
<button class="btn btn-ghost" onclick="clearLog()">Clear Log</button>
|
||||
<button class="btn btn-ghost" id="btn-autostart" onclick="runAction('autostart')">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>
|
||||
@ -1950,11 +1762,8 @@ function runAction(action) {
|
||||
deploy: '/api/deploy/run?ip=' + encodeURIComponent(cfg.kl630_ip) +
|
||||
'&host_ip=' + encodeURIComponent(cfg.host_ip) +
|
||||
'&port=' + encodeURIComponent(cfg.port) +
|
||||
'&spi_fix=1' +
|
||||
'&out_rtsp=' + (document.getElementById('out-rtsp').checked ? 1 : 0) +
|
||||
'&out_hdmi=' + (document.getElementById('out-hdmi').checked ? 1 : 0),
|
||||
bt_setup: '/api/bt_setup/run?ip=' + encodeURIComponent(cfg.kl630_ip),
|
||||
spi_setup: '/api/spi_setup/run?ip=' + encodeURIComponent(cfg.kl630_ip) + '&node=spi1.0',
|
||||
autostart: '/api/autostart/write?ip=' + encodeURIComponent(cfg.kl630_ip) +
|
||||
'&out_rtsp=' + (document.getElementById('out-rtsp').checked ? 1 : 0) +
|
||||
'&out_hdmi=' + (document.getElementById('out-hdmi').checked ? 1 : 0),
|
||||
@ -1969,7 +1778,7 @@ function runAction(action) {
|
||||
'&host_ip=' + encodeURIComponent(cfg.host_ip) +
|
||||
'&port=' + encodeURIComponent(cfg.port),
|
||||
};
|
||||
const labels = { compile:'Compile', deploy:'Deploy to KL630', bt_setup:'BT 初始化', spi_setup:'SPI 初始化', autostart:'Write Autostart', autostart_read:'Read Autostart', mount_sd:'Mount SD', deploy_binary:'Update Binary', deploy_startsh:'Update start.sh' };
|
||||
const labels = { compile:'Compile', deploy:'Deploy to KL630', autostart:'Write Autostart', autostart_read:'Read Autostart', mount_sd:'Mount SD', deploy_binary:'Update Binary', deploy_startsh:'Update start.sh' };
|
||||
|
||||
appendLog('\\n── ' + labels[action] + ' ' + '─'.repeat(40), 'prompt');
|
||||
setLogStatus('Running...');
|
||||
@ -2065,7 +1874,7 @@ function appendLog(text, kind) {
|
||||
}
|
||||
function clearLog() { document.getElementById('log').textContent = ''; setLogStatus(''); }
|
||||
function setLogStatus(s) { document.getElementById('log-status').textContent = s; }
|
||||
function setBtns(disabled){ ['btn-compile','btn-deploy','btn-bt-setup','btn-spi-setup','btn-autostart','btn-autostart-read','btn-mount-sd','btn-model-apply','btn-deploy-binary','btn-deploy-startsh'].forEach(id => document.getElementById(id).disabled = disabled); }
|
||||
function setBtns(disabled){ ['btn-compile','btn-deploy','btn-autostart','btn-autostart-read','btn-mount-sd','btn-model-apply','btn-deploy-binary','btn-deploy-startsh'].forEach(id => document.getElementById(id).disabled = disabled); }
|
||||
|
||||
// ── Terminal ──────────────────────────────────────────────────────────────────
|
||||
// by mars
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user