Files
usb-camera-webui/README.md
T

89 lines
3.9 KiB
Markdown
Raw Normal View History

2026-08-05 15:43:04 +00:00
# usb-camera-webui
A live video feed from a USB webcam, served through a small web UI, running on a Raspberry Pi 5.
## Why this is its own repo
There is a sibling project, **`camera-webui`**, doing the same job on a Jetson Orin Nano with a CSI
IMX219. This is deliberately *not* that repo, for two reasons:
1. **Two agents, two repos.** A Codex agent is working in `camera-webui` on the Orin. A second agent
editing the same files from another machine would collide for reasons that have nothing to do with
either one's ability.
2. **The point is measuring the agent.** Both projects exist to see how far Codex + qwen3.6 gets on
real hardware. Two independent runs are readable; one shared repo with merge conflicts is not.
Merging them later behind a source-selector — one UI, switchable backends — is a reasonable end
state. It is just not the starting point.
## Hardware, verified
| | |
|---|---|
| Board | Raspberry Pi 5, Debian 12 (bookworm), aarch64 |
| Camera | **Logitech C505 HD Webcam** (`046d:08e3`), USB/UVC |
| Node | `/dev/video0` |
| Formats | `MJPG` (Motion-JPEG) and `YUYV` (4:2:2) |
| Confirmed | 1280x720 MJPG frame captured, a genuine 33 KB JPEG |
```bash
v4l2-ctl -d /dev/video0 --list-formats
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1280,height=720,pixelformat=MJPG \
--stream-mmap --stream-count=1 --stream-to=/tmp/frame.jpg
file /tmp/frame.jpg # must say "JPEG image data", not just exist
```
**`/dev/video*` is crowded here.** The Pi 5 exposes many nodes (`/dev/video19``35`) belonging to
its ISP and codec blocks, present with no camera attached. The webcam is `/dev/video0`; a second node
(`/dev/video1`) is the UVC metadata interface, not a capture device. **Never pick a node by index —
confirm with `v4l2-ctl --list-devices`.**
## UVC is the portable path
This is plain **V4L2**. No libcamera, no `picamera2`, no Argus. That matters beyond this repo: UVC is
the one capture path that works identically on the Pi and the Jetson, so a backend written here is
the piece most likely to survive being moved.
**Prefer `MJPG` over `YUYV` for streaming.** The camera compresses in hardware, so MJPG frames come
off the wire ready to serve; `YUYV` is uncompressed and will spend Pi CPU on encoding you did not
need to do.
## ⚠ Power is a real constraint here
The Pi is running on a **3 A supply, not the 5 A one it wants**:
```
max_current = 3000 mA
usb_max_current_enable = 0 # restricted USB budget
```
The C505 fits within that, and the board has been stable with it attached — but the headroom is thin.
**Do not assume a second USB device will fit**, and do not add powered peripherals casually. If the
board drops out, check these after it returns:
```bash
vcgencmd get_throttled # undervoltage bits
cat /sys/firmware/devicetree/base/chosen/power/usb_over_current_detected
journalctl -b -1 -e # clean shutdown, or abrupt cut?
```
That last one is the useful one: this machine keeps **persistent logs**, so a previous boot ending in
an orderly shutdown sequence means something different from one that stops mid-line.
## Planned stages
1. **Capture** — open `/dev/video0`, pull frames, confirm format and rate. *(Hardware already
verified; the code is not written.)*
2. **Live feed** — MJPEG over HTTP first. It works in any browser with no negotiation, and the camera
already produces the frames.
3. **Web UI** — one page: the feed, plus basic controls.
4. **Later** — resolution/format switching, snapshots, and only then anything heavier.
Each stage should be usable on its own before the next begins.
## Development
Codex runs on this Pi, so development happens on the target — no cross-compiling, no deploy step. The
model endpoint and MCP gateway are on `halogen` and reachable from here by name. See
[AGENTS.md](AGENTS.md).