Skip to content

PocketWUI 2.0: USB network + drive, QR sign-in, search, ZIP, previews, WebDAV, new UI - #1

Merged
AFetisa merged 8 commits into
mainfrom
claude/cardputer-wui-improvements-p7yi1x
Sep 25, 2026
Merged

AFetisa merged 8 commits into
mainfrom
claude/cardputer-wui-improvements-p7yi1x

Conversation

@AFetisa

@AFetisa AFetisa commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Renames the project to PocketWUI and adds the features discussed. The goal is something non-technical people can use and show off, not just a tool for tinkerers.

What changes for users

  • Scan to connect. The device's side button cycles through two QR codes: one joins the Cardputer's Wi‑Fi, the other signs you in. The sign-in code works once and expires after 2 minutes.
  • USB cable. Plugged into a computer, the Cardputer shows up as a network adapter at http://192.168.7.1, and the computer keeps its own internet. It can also lend the SD card to the computer as a USB drive.
  • Files.
    • Search the whole card, or filter the current folder as you type.
    • Sort columns and select several items at once.
    • Download a folder or a selection as one ZIP. The size is known up front, and archives over 4 GB work (ZIP64).
    • Trash, copy, a "Replace?" prompt on name clashes, SHA‑256 checksums, folder sizes.
    • Paste to upload; uploads go in 256 KB chunks.
  • Previews. Photo gallery, video and music with seeking, PDF, Markdown, text editor.
  • Wi‑Fi.
    • Remembers up to five networks and joins the strongest one in range.
    • Quietly retries saved networks from hotspot mode, but never while a phone is connected to the hotspot.
    • Captive portal on the hotspot; network scan.
    • Hotspot key, device name, and a "keep the hotspot on" option.
    • The device clock is set from the browser, or from the internet (NTP).
  • WebDAV network drive at /dav/.
  • M5Launcher.
    • A Back to Launcher button.
    • Under Launcher the device never flashes firmware itself; it checks an uploaded .bin and says to install it from Launcher.
    • Installed standalone, it updates itself, and rolls back if the new version fails to start.
  • New web UI. Light and dark themes, works on phones, a Settings panel. Still one self-contained 24 KB page that works offline.
  • Device screen. Battery level, USB status, dims when idle, a USB-drive screen.

Under the hood

  • Platform: moved to pioarduino (Arduino core 3.3.12), which EspUsbDevice needs.
  • USB mode: the build uses USB-OTG (TinyUSB) mode with no USB serial console. A compile-time check stops the build if the mode is wrong.
  • Flash layout: partitions.csv has two app slots, so standalone installs can update themselves.
  • Responsiveness: long downloads and ZIPs run on background threads. The SD lock is held for one chunk at a time, not a whole transfer.
  • Code layout: the server is split into api_files, api_device, dav, stream, jobs, usb, firmware and small helper modules.
  • Review fixes (hardware-only issues):
    • USB drive: the computer's reads and writes now run under the SD lock, fixing a use-after-free when the card is handed back.
    • Copy and checksum jobs now yield, so a long one can't trip the watchdog.
    • The Wi‑Fi join and the reconnect loop can no longer interfere with each other.
    • An idle upload is no longer closed while it is still being written.
    • Update rollback now actually works.

Testing

  • test/hostsim compiles the firmware's real server code for a PC, with stand-ins for esp_http_server, the SD card and the radio. It replaces tools/mock_server.py.
  • make -C test host: unit tests for paths, QR text, text helpers, ZIP (including a real archive over 4 GB when WUI_ZIP64_TEST=1) and firmware images, plus the browser upload loop.
  • make -C test sim: 59 API checks and 30 Chromium UI checks against the simulator. All of these run in CI along with the firmware build.
  • rclone sync, check, copy back and move over WebDAV worked against the simulator.
  • Not yet run on a real Cardputer: Wi‑Fi, the USB network adapter and drive, the screen, the battery reading, Back to Launcher and self-update. This PR is ready for hands-on testing with build/PocketWUI.bin from M5Launcher.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XgGV6mvKRxDHTmFyWfaSME


Generated by Claude Code

The side button now steps info -> join Wi-Fi -> sign in. The Wi-Fi screen
(AP mode only) shows a standard WIFI:T:WPA QR for the Cardputer's own
network. The sign-in screen shows http://<ip>/login?k=<token>: a
single-use, 2-minute token that exists only while that screen is up,
is traded for the normal session cookie, and is dropped on password
change. Bad or reused codes count toward the lockout and land on the
password prompt with a note. WUI_QR_LOGIN 0 turns the feature off.

The mock server supports /login too. Host tests cover the Wi-Fi payload
escaping. Not compiled for the ESP32 here: the PlatformIO toolchain
could not be downloaded, so build/firmware.bin predates this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgGV6mvKRxDHTmFyWfaSME
… Launcher

Platform: move to pioarduino (Arduino core 3.3.12), needed by EspUsbDevice.

- USB: CDC-NCM network adapter (WUI at http://192.168.7.1/, the computer
  keeps its own internet) and an on-demand USB drive mode for the SD card.
- Wi-Fi: up to 5 saved networks, strongest first; quiet rejoin from AP
  mode (never while a phone is on the AP); captive portal; scan; AP key,
  device name and "keep AP on" settings; clock from the browser or NTP.
- Files: search (substring or glob), streamed stored ZIP downloads with
  exact Content-Length and ZIP64, trash, copy and SHA-256 as background
  jobs, folder sizes, inline previews with a sandbox CSP for active types.
- Long downloads run on async worker threads, so one download no longer
  freezes the UI; the SD lock is taken per chunk instead of per request.
- WebDAV at /dav/ (Basic auth with the WUI password).
- Firmware: checks uploaded images; self-update when flashed standalone
  (new dual-slot partitions.csv); under M5Launcher never self-flashes and
  offers "back to Launcher" via its deep-sleep entry.
- test/hostsim: the real server sources compiled for the PC against
  stand-ins for esp_http_server, the SD card and the radio; replaces the
  Python mock. New host tests for ZIP (incl. ZIP64), text helpers and
  firmware image checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgGV6mvKRxDHTmFyWfaSME
Web UI (single 24 KB gzipped page, still fully offline):
- friendly light/dark design that works on phones
- instant filter + whole-card search, sortable columns, multi-select
  (download as ZIP, copy, move, delete), drag-to-move, paste-to-upload
- previews: image gallery with arrow keys, video/audio (seekable), PDF,
  Markdown, text editor; firmware .bin files explain what to do
- Trash with "Empty Trash"; "Replace?" prompt on name clashes
- Settings: Wi-Fi (saved networks, scan and join, hotspot), USB (network
  adapter, lend the card as a USB drive), WebDAV address, device name,
  password, firmware update, back to M5Launcher, theme
- background job cards for copy/checksum/firmware
- the browser lends the device its clock, so new files get real dates

Build: USB-OTG (TinyUSB) mode with no CDC console, as EspUsbDevice needs;
a compile-time guard catches the wrong mode.

Tests: test/e2e/test_api.py (59 checks) and test/e2e/test_ui.mjs
(Chromium, 30 checks) run against test/hostsim; both in CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgGV6mvKRxDHTmFyWfaSME
…flash.bin

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgGV6mvKRxDHTmFyWfaSME
… races

- USB drive: host sector I/O now runs under the SD lock and only while the
  card is lent; give-back flips that under the lock, remounts, then reopens
  the card to the web (was: use-after-free on SD.end, and a window where
  handlers used a card about to be unmounted). Missing/swapped cards are
  refused (raw reads on an unmounted card crash). Serialised mode changes;
  only a real eject (not power-management STOP UNIT) hands the card back;
  pulling the cable returns it after 3 s.
- Background jobs yield every chunk and run at loop() priority: SD/SPI
  polling never blocks, so a long copy could trip the task watchdog.
- Wi-Fi: one lock for all state; tick() try-locks so a join is never
  interrupted by the rejoin rotation; the lost-link timer resets after a
  healed drop; once the AP is up the station's endless auto-reconnect is
  stopped so it can't stall AP clients.
- Uploads: the idle closer never closes a handle an upload handler holds.
- Updates: rollback stays armed until the web server is up.
- Firmware install verifies every byte was read; truncated downloads close
  the connection; stream worker stack 8 KB; SPI re-init under the SD lock;
  "connecting to Wi-Fi" on screen during the boot-time join.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgGV6mvKRxDHTmFyWfaSME
@AFetisa
AFetisa merged commit 87849ac into main Sep 25, 2026
2 checks passed
@AFetisa
AFetisa deleted the claude/cardputer-wui-improvements-p7yi1x branch September 25, 2026 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants