Repository navigation
W6: the Raspberry Pi on the cart, with Wi-Fi settings in the web app - #14
Merged
Merged
Conversation
- scripts/pi-setup.sh for Raspberry Pi OS 64-bit with the MCP2518FD CAN FD HAT: SPI and the mcp251xfd overlay (crystal, interrupt and chip select as options), can0 at 1 Mbit/s CAN FD without bit rate switch by systemd-networkd, cypilot in /opt/cypilot for a user cypilot, the DSDL and the register description file fetched while online, cypilot.service on port 80 with a PIN, runs on the SSD, the host name for mDNS, and Wi-Fi: a known network, else the Pi's own access point after 45 s. --dry-run writes the files under --root and prints the commands. - serve takes the PIN from $CYPILOT_PIN. - scripts/load_test.py: drives (demo or a real bus), a recording run and browsers on the plots of every node; reports CPU, status latency, the run's frames and size and the browsers' events. - docs/pi.md, README, AGENTS, web_plan (the day on the bench is open). - Tests: the dry run (files, commands, a second run keeps the PIN, bad options); shellcheck clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEh6hu8r51ZDjgfECmELvM
- cypilot/wifi.py: the Wi-Fi over nmcli (argument lists, no shell): the state and address, scan, the known networks, join (saved as known), forget, the access point's SSID and key, open it now. A failed join reopens the access point when it was up, so the cart keeps a network. - API /api/wifi (state, scan for everybody; join, forget, access point for the operator), serve --wifi / CYPILOT_WIFI=1. - Wi-Fi view, shown only when the server manages the Wi-Fi. - pi-setup.sh: a polkit rule lets the user cypilot use NetworkManager, CYPILOT_WIFI=1, and a second run keeps the access point key as the web app set it. - Tests with a fake nmcli (tests/fake_nmcli.py): the parser, the wrapper with the fallback, the API with the control lease, the view in Chromium. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEh6hu8r51ZDjgfECmELvM
On CI (Python 3.13) the drive did not show as engaged after the second enable; the test ignored that enable's answer. A runner that stalls the loop for the 0.5 s lease trips the watchdog, which is right, but not what this test is about: it now runs with the 1 s lease, checks the answer and names the control, stop and log events when it fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEh6hu8r51ZDjgfECmELvM
…omically The web app runs several nmcli calls at once (a reload on the wifi event next to the view's own). The fake read its state and wrote it back unlocked, so a read that started before a delete undid it: the forgotten network came back (CI, and 1 of 3 runs of a local race check). It now locks the state file and replaces it in one step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEh6hu8r51ZDjgfECmELvM
The two-node scope test timed out on CI (Python 3.13) without a message: a follower that never sees the trigger node's event waited for the whole capture timeout (30 s), as long as the test. Once the trigger node has its capture, the others now get 2 s plus twice the capture's length and then fail with 'node N did not trigger on the trigger event of node M'. The browser test reports the scope state and the message when it fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WEh6hu8r51ZDjgfECmELvM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase W6 of
docs/web_plan.md, with the hardware decided on 2026-10-25: the MCP2518FD CAN FD HAT. For Wi-Fi, the Pi joins a known network when it is in range, else it opens its own access point. On request, the Wi-Fi can be set in the web app.Changes
scripts/pi-setup.sh(Raspberry Pi OS 64-bit, Bookworm or later; run as root):dtoverlay=mcp251xfd,<spi>,oscillator=<Hz>,interrupt=<GPIO>inconfig.txt, in a marked block that a second run replaces;can0by systemd-networkd: 1 Mbit/s, CAN FD without bit rate switch, restart after a bus-off;/opt/cypilotfor a system usercypilot; the DSDL and the register description file are fetched during setup, because the cart is offline;cypilot.service:cypilot-ctl -i socketcan:can0 serve --host 0.0.0.0 --port 80,Restart=always,CAP_NET_BIND_SERVICE. The PIN, the runs directory, the cache andCYPILOT_WIFI=1are in/etc/cypilot/cypilot.env;cypilot-apthat does not autoconnect.cypilot-wifi-fallback.serviceopens it when no known Wi-Fi has connected within 45 s of the boot (--no-apfor none);cypilotuse NetworkManager, for the Wi-Fi view;http://<name>.local(avahi);--dry-run --root DIRwrites the files under DIR and prints the commands.Wi-Fi in the web app (
src/cypilot/wifi.py,serve --wifior$CYPILOT_WIFI=1):nmcliruns with argument lists, never a shell. Reading is open; changes need the control lease. If joining fails while the access point was up, the access point is opened again, so the cart is never left without a network. The view appears only when the server manages the Wi-Fi.serve --pindefaults to$CYPILOT_PIN.scripts/load_test.py: demo devices or a real bus, a recording run, and browsers showing every subject of every node. Locally, 4 demo devices and 2 browsers: about 30 % of one core, status median 47 ms, 155 frames/s recorded.Scope (from CI): in a multi-node capture, once the trigger node has its capture, the other nodes get 2 s plus twice the capture's length. One that missed the trigger event then fails with "node N did not trigger on the trigger event of node M". Before, it waited the whole capture timeout (30 s) without a message.
docs/pi.mdcovers hardware, setup, use, Wi-Fi in the web app and the load test. README, AGENTS andweb_plan.mdare updated; the plan marks the day on the bench as open.Tests
tests/unit/test_pi_setup.py, the dry run:can0, the service and the env file;tests/unit/test_wifi.pywith a fakenmcli(tests/fake_nmcli.py; one call at a time, the state written atomically): the terse parser, status, scan, known networks, join with the access point fallback, forget, the access point, and the API with the control lease.tests/browser/test_wifi_ui.py: join, forget and change the access point in Chromium; no Wi-Fi view without--wifi.tests/unit/test_scope.py: a follower that misses the trigger event fails within a few seconds.test_web_simuses the 1 s lease and checks the second enable;pytest132 passed, 1 skipped. CI is green on all jobs.Not testable here: the W6 acceptance test, a day on the cart bench with the drives. Nothing in this PR has run on a real Pi with NetworkManager yet.
🤖 Generated with Claude Code
https://claude.ai/code/session_01WEh6hu8r51ZDjgfECmELvM