Public: anyone can read this; the crew can change it.
Migrated from github.com/Trevato/SO-ARM100
Cameras stream with the arm unplugged: serve starts with no serial port, and resting stops driving, not cameras #624
Check · Review · Land done · Live — Landed; nothing deploys from this branch, so this is the end of its path.
Cameras now stream whether or not the arm is plugged in, open or resting. Resting stops only driving, and `so101 serve` retries a missing serial port every 5 s. The new tests and the camera, relay, resting and workshop suites pass, and the type check reports no errors in the changed files. Nothing has been tried on the real Pi yet.
shipped merged
Cameras now stream whether or not the arm is plugged in, open or resting. Resting stops only driving, and so101 serve retries a missing serial port every 5 s. The new tests and the camera, relay, resting and workshop suites pass, and the type check reports no errors in the changed files. Nothing has been tried on the real Pi yet.
Serve with no arm. #617 already made so101 serve start without the port and answer /status 200 with arm: "not connected", but it never tried the port again. The new src/arm-plug.ts has retryArm try the port every 5 s. When the arm answers, src/cli.ts closes the server and starts it again with the bus, logging arm connected: serving <path>. Camera viewers lose their stream once at that moment. Retries are silent; only the first failure is logged, and that line now ends trying again every 5 s.
Cameras don't depend on the arm. In src/relay.ts, /camera/<id>/snapshot|stream|state no longer answer 503 while resting, and camera WebSockets (JPEG and H.264) are no longer refused. When the arm starts resting, only /follow streams are closed: the relay remembers which sockets are camera viewers and leaves them open. /story always gets the camera list. On the home page, /stations and the absent arm's card still list the arm's cameras and show their stills (src/workshop-page.ts). Each camera is read from its own source, not through the arm server.
Tests:
src/arm-plug.test.tsruns the realservecommand as a child process withSO101_PORTpointing at a missing path. It checks/statusis 200 witharm: "not connected"and that the retry is logged. It also checks the retry loop itself.- A new test in
src/resting.test.tstakes the arm offline and then closed. It checks that/camera/2/snapshotis 200 from a fake source, that a new camera WebSocket gets a frame, that a viewer from before the rest stays open, and that/followis still refused with 4503. It fails against the oldrelay.ts. - I changed two tests that asserted the old behaviour the issue rules out: the closed-arm test in
src/resting.test.tsno longer expects camera 503s, andsrc/stations.test.tsnow expects the closed arm's card to list its camera.
The README and the comment in src/opening.ts now describe the new behaviour.
Not done: the restart when the arm is plugged in could be a live swap of the bus inside the server instead. That would mean reworking startup, which reads the servo limits once.
To try it: run SO101_PORT=/dev/nothing bun -e 'import {main} from "./Software/so101/src/cli.ts"; await main(["serve","--listen","127.0.0.1:8101"])', then curl localhost:8101/status. Or close the arm in /admin and open /camera/1/snapshot.
🤖 Generated with Claude Code
Preview is up: / 200, /status shows the arm offline (resting card), and /camera/1/snapshot answers 200 while the arm is offline, so the cameras no longer rest with the arm. I could not open the forge screenshots from this sandbox; the verdict rests on the curl checks and the diff. The change does what #623 asks and nothing else, the tests cover the new behaviour (a real serve child process with a missing port, the retry loop, cameras while closed and offline, an existing camera viewer staying open, /follow still refused with 4503), and the README is updated. Two things to do next:
- Make the replug handover safe in
cli.ts. IfstartServer(found, options)throws afterserver.close()(for example the port is not yet free), the error is an unhandled rejection insideretryArm's attempt, and the process is left with no server. Catch it, log it, and keep trying, or restart the process. The handover itself, close then start with the bus andarm connectedlogged, has no test; add one with a fakeopen. Also, SIGINT during the swap can racestopRetry. - Add a note on the card or
/adminthat plugging the arm in restarts the camera streams once, and say in the README that the terminal shows cameras by the same rule as the page (the README's 'Both doors' line).
Cameras now stream whether or not the arm is plugged in, open or resting. Resting stops only driving, and
so101 serveretries a missing serial port every 5 s. The new tests and the camera, relay, resting and workshop suites pass, and the type check reports no errors in the changed files. Nothing has been tried on the real Pi yet.Serve with no arm. #617 already made
so101 servestart without the port and answer/status200 witharm: "not connected", but it never tried the port again. The newsrc/arm-plug.tshasretryArmtry the port every 5 s. When the arm answers,src/cli.tscloses the server and starts it again with the bus, loggingarm connected: serving <path>. Camera viewers lose their stream once at that moment. Retries are silent; only the first failure is logged, and that line now endstrying again every 5 s.Cameras don't depend on the arm. In
src/relay.ts,/camera/<id>/snapshot|stream|stateno longer answer 503 while resting, and camera WebSockets (JPEG and H.264) are no longer refused. When the arm starts resting, only/followstreams are closed: the relay remembers which sockets are camera viewers and leaves them open./storyalways gets the camera list. On the home page,/stationsand the absent arm's card still list the arm's cameras and show their stills (src/workshop-page.ts). Each camera is read from its own source, not through the arm server.Tests:
src/arm-plug.test.tsruns the realservecommand as a child process withSO101_PORTpointing at a missing path. It checks/statusis 200 witharm: "not connected"and that the retry is logged. It also checks the retry loop itself.src/resting.test.tstakes the arm offline and then closed. It checks that/camera/2/snapshotis 200 from a fake source, that a new camera WebSocket gets a frame, that a viewer from before the rest stays open, and that/followis still refused with 4503. It fails against the oldrelay.ts.src/resting.test.tsno longer expects camera 503s, andsrc/stations.test.tsnow expects the closed arm's card to list its camera.The README and the comment in
src/opening.tsnow describe the new behaviour.Not done: the restart when the arm is plugged in could be a live swap of the bus inside the server instead. That would mean reworking startup, which reads the servo limits once.
To try it: run
SO101_PORT=/dev/nothing bun -e 'import {main} from "./Software/so101/src/cli.ts"; await main(["serve","--listen","127.0.0.1:8101"])', thencurl localhost:8101/status. Or close the arm in/adminand open/camera/1/snapshot.🤖 Generated with Claude Code
Closes #623