Commit Graph

11 Commits

Author SHA1 Message Date
football2801 91cbe851a3 chore(screens): regenerated ap_bg outputs (companion to 8cbd035)
gen_screens scripts rewrite all six panels per run; ap_bg byte-output
shifted even though the AP-screen copy doesn't reference brand or URL.
Committed to keep data/ consistent with the scripts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 21:34:11 -04:00
football2801 c1bed8c218 ux(setup): rewrite captive-portal + AP/setup-screen copy
Beta tester called the previous setup wording "Chinglish."
Tighter, plainer language across all three surfaces:

- Captive portal (PORTAL_HTML): "Connect your frame", explicit
  "Home WiFi name / Home WiFi password", clearer footer.
- AP screen step list: 5-line plain-English checklist; no more
  Safari-specific reference.
- Setup screen: fixed step 2 wrapping mid-domain
  ("pictureframe / .edholm.me"), tightened steps 1 and 3.

Regenerated bg.bin to match the new gen_screens.py output.
NEEDS-FLASH: in-field beta unit still has prior copy.
2026-05-09 15:17:15 -04:00
football2801 f1d867c659 ux(provisioning): step 1 is now "Plug in power"
Frames will ship with the battery disconnected to preserve shelf life
— the setup screen polling is non-trivial draw on e-ink, and a unit
that sits on a shelf for weeks before unboxing would arrive flat.
Surface "plug in power" as the prerequisite step ahead of unlock and
scan, so the recipient can't miss it.

Step list grows from 4 to 5; pitch shrinks from 38 → 32 px to keep
the manual-help QR clear of the bottom edge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:41:49 -04:00
football2801 1399cc3756 fix(provisioning): two-QR flow — WiFi join + open-browser trigger
User report: iOS sees the captive network correctly (the captive UI
fires the moment any app makes an HTTP request), but won't auto-pop
it from a QR-scan join. This is recent-iOS hardening — Apple no
longer aggressively opens CNA on QR-initiated joins.

Workaround: a single QR can only encode one action, but two QRs
side-by-side close the loop —

  STEP 1 — WiFi-join QR (WIFI:T:WPA;S:NAME;P:pass;;)
           Phone joins PictureFrame.
  STEP 2 — URL QR (http://192.168.4.1/)
           Phone opens Safari → Safari hits 192.168.4.1 → that HTTP
           request is the "any app" trigger that fires the captive
           UI deterministically.

Implementation:
- WiFi QR shrinks from cell 5 (185 px) to cell 4 (148 px) to make
  room for the URL QR below.
- URL QR is static, baked into ap_bg.bin via Python qrcode at gen
  time — no firmware QR-render changes needed for it.
- epd_draw_ap_screen / _retry overlay coords updated to match the
  new WiFi QR position (581, 100, 4).
- Left-panel step list now reads "1. Unlock / 2. Scan QR 1 / 3. Scan
  QR 2 / 4. Enter password and tap Connect".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:30:39 -04:00
football2801 d1599a726d fix(provisioning): mirror aqua-iq's working AP pattern
After repeated 200-OK / DHCP-option / DNS-hijack permutations failed to
make the iOS captive banner fire reliably, port the configuration that
empirically works on the aqua-iq Pi to the ESP32:

* WPA2-PSK secured AP (was open). iOS handles secured-network
  captive-portal detection more aggressively than open networks. The
  PSK ('pictureframe') is baked into both firmware and the WIFI: QR so
  the user never types it — there's no real secret value here.
* Explicit channel 6 (was the softAP default of channel 1). Channel 6
  is the "middle" 2.4 GHz channel and tends to be less contested in
  dense environments; aqua-iq picks it for the same reason.
* 1.5 s settle delay after softAP. The radio + DHCP server need a
  beat before they're ready to handle a phone that joins the moment
  the SSID is broadcast (aqua-iq sleeps 3 s for NetworkManager+dnsmasq
  to fully initialize; the ESP softAP stack is faster but a small pad
  still kills race conditions).
* CNA paths revert to 302 redirect → "/". This is what aqua-iq does
  and what WiFiManager does. Serving the portal HTML inline at 200 on
  these endpoints (the previous attempt) didn't reliably trigger the
  iOS banner. The redirect is what iOS / Android / Windows look for.

QR string format updates from WIFI:S:NAME;T:nopass;; to
WIFI:T:WPA;S:NAME;P:pictureframe;; — phones consume both, but the
WPA variant is needed now that the AP requires a password.

Bg image's format-string footnote regenerated to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 12:48:11 -04:00
football2801 9c911b36b6 ux(provisioning): draw portrait diagram pre-rotated
Portrait was drawn upright (tall rect, ribbon on left, arrow up) so the
diagram only made sense while looking at the frame in landscape. The
intended UX is the opposite: the diagram is meant to be read AFTER the
user tilts the frame 90° CW into portrait, at which point it should
look correct.

Render the portrait diagram rotated 90° CCW from upright in the
landscape source — wide rect, ribbon on the bottom, arrow pointing
left. When the user tilts the frame CW, this rotates with the e-ink
content and lands as the canonical portrait view: tall rect, ribbon
on the left, up-arrow pointing up.

Side effect: the landscape diagram looks "rotated" when the frame is
in portrait, which is the same trick in reverse and the desired
behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:29:59 -04:00
football2801 e1eab1fdab ux(provisioning): portrait arrow points right, not up
In portrait orientation the cable/ribbon is on the left, so the
physical 'up' edge of the hung frame corresponds to the right side
of the rendered diagram — not the top. Swap the up-arrow inside the
portrait screen for a right-arrow so the diagram actually shows the
user which edge will be up when they hang the frame.

Landscape diagram is unchanged (cable on bottom → up is up).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:19:53 -04:00
football2801 251fafa01b ux(provisioning): up-arrow inside portrait diagram too
The arrow's purpose is to show the user which edge is "up" when they
hang the frame, regardless of orientation — so it belongs in both
diagrams, not just landscape. Slightly smaller arrow in portrait
(half_w=12, h=22) to match the narrower screen footprint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:16:41 -04:00
football2801 44bd2777c2 ux(provisioning): up-arrow inside landscape diagram, drop accent fill
Render both orientation diagrams in black so neither orientation looks
privileged-by-default — the previous yellow/green active highlight on
landscape was redundant once the supported orientation was clear from
the rest of the layout, and dropping it cleans up the centre panel.

Communicate "this edge is up" with a filled up-arrow inside the
landscape screen instead of relying on color. Setup screen (green
Step 2/2) inherits the same change since orientation_diagrams is
shared between gen_ap and gen_setup.

show_active_ls / accent params kept on the function signature for
call-site stability but no longer drive any colour decisions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:12:34 -04:00
football2801 e089911cfa ux(provisioning): unlock-first step + manual QR on Step 1/2
Two changes to the yellow Step 1/2 (and red retry-twin) screens, both
in service of the locked-phone-scan failure mode where iOS joins the
AP but never opens the captive portal:

* Promote 'Unlock your phone first' to step 1 of a four-step list
  (was three steps starting with 'Scan the QR'). Tightens step pitch
  from 46→38 px to fit the new step. Surfacing the requirement
  visually beats discovering it by scanning, getting nothing, and
  giving up.

* Bake a manual-link QR into the bottom of the left panel pointing
  to https://pictureframe.edholm.me/help. Side label 'Need help? /
  Scan for setup / & troubleshooting'. Static URL → encoded directly
  into ap_bg.bin via the qrcode Python lib at gen time, no firmware
  QR-render changes needed. Retry twin (ap_bg_retry.bin) inherits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:06:30 -04:00
football2801 fb4c5ff5d3 fix(provisioning): stop redrawing the QR on every poll, add WiFi-fail retry screen
Two related fixes that together let the post-WiFi-setup window be quiet:

1. operation.h 204/404: skip the panel redraw entirely. The panel already
   holds the right thing — setup QR if no image has ever been painted
   (img_id == -1), or a real photo if img_id >= 0. Redrawing the QR every
   15s during the bootstrap claim window put the e-ink into a perpetual
   ~20s mid-refresh loop and risked ghosting. Tests updated to assert
   no redraw on either sub-case.

2. main.cpp WiFi-fail path: drop the epd_fill(RED) + 3s delay + AP
   re-redraw sequence (~43s of e-ink work that destroyed the QR mid-flow)
   and replace with a single repaint of a new "Connection Failed — try
   again" Step 1/2 screen with red accents. gen_screens.py grows a
   gen_ap_retry() variant that recolors yellow → red and swaps the
   header/QR labels; the result is shipped as ap_bg_retry.bin alongside
   ap_bg.bin in LittleFS. epd.h exposes epd_draw_ap_screen_retry().
2026-05-08 23:43:59 -04:00