Add battery level reporting (Kconfig-gated) and display orientation
Build and push server image / build-and-push (push) Successful in 32s

Battery (firmware + server, disabled by default): new battery.c reads
a 2x200k voltage divider via ADC oneshot with curve-fitting calibration
(the ESP32-C6's scheme), maps through a piecewise LiPo discharge curve,
and restores the pin to button duty after each read -- the settled
XIAO ESP32-C6 design shares the back button's GPIO0/A0, time-shared per
wake. Skipped entirely when on mains (a 2x100k VBUS divider into a
spare digital pin -- the 5V pin is dead on battery power, so presence =
mains, where the charging voltage would read misleadingly full) or when
the reading is implausible. The manage overlay gains a battery region
(static outline glyph + "NN%", below the manage QR, all menu levels),
and the device POSTs to the new /frame/battery endpoint after a
successful fetch; the server stores percent + as-of timestamp, exposed
via /api/queue and shown in the web UI. FRAME_BATTERY_ADC_GPIO /
FRAME_VBUS_SENSE_GPIO default to -1 (fully inert on the dev board);
compile-verified both disabled and enabled, hardware bring-up deferred
until the ordered XIAO + batteries arrive.

Orientation (server-side only): new config setting + web UI dropdown
(landscape / portrait / landscape_flipped / portrait_flipped). Photos
are composed/cropped at the logical hanging shape (portrait crops at
480x800, so face-aware crops match how the frame actually hangs), then
rotated losslessly into the panel's native 800x480 byte layout after
dithering -- the device never knows. Face-label anchors are transformed
through the same rotation (logical_to_native()) so they stay attached
to faces on rotated frames. Known documented limitation: the on-device
manage overlay still renders in native orientation, so it appears
sideways on a portrait-hung frame (QRs scan at any rotation; text reads
sideways).
This commit is contained in:
2026-07-19 22:11:14 -04:00
parent 86d5852f8e
commit 015993af00
17 changed files with 593 additions and 42 deletions
+33
View File
@@ -49,6 +49,8 @@ Under **ESPresso Frame Configuration**:
| `FRAME_COMBO_BUTTON_GPIO` | 1 | Menu/reset button GPIO (-1 to disable). Must be 0-7 |
| `FRAME_COMBO_SOFT_RESET_HOLD_MS` | 3000 | How long the combo button must be held (then released) to soft-reset |
| `FRAME_COMBO_FACTORY_RESET_HOLD_MS` | 15000 | How long the combo button must be held to factory-reset |
| `FRAME_BATTERY_ADC_GPIO` | -1 (disabled) | Battery voltage-divider ADC GPIO; see the Battery section below |
| `FRAME_VBUS_SENSE_GPIO` | -1 (disabled) | USB-power sense GPIO for hiding the battery indicator on mains |
Under **E-Paper Display (epd7in3e) Configuration**: SPI/GPIO pin
assignments and SPI clock speed -- see
@@ -177,6 +179,37 @@ If there's nothing to go back to yet (freshly provisioned, or you've
already gone back as far as there is history), it's a no-op -- the
current photo stays exactly as it was, no flash on the panel.
## Battery (XIAO ESP32-C6)
Disabled by default (`FRAME_BATTERY_ADC_GPIO = -1`) -- correct for a
dev board with no battery wired. On the intended production board (Seeed
XIAO ESP32-C6) with a 1S LiPo:
**Wiring** (beyond soldering the battery itself to the BAT+/BAT- pads on
the XIAO's underside -- note the polarity markings there, and that
JST-PH battery connector polarity is *not* standardized, so verify with
a multimeter before first plug-in):
- **Battery sense**: a 2x200k voltage divider from BAT+ to GND, midpoint
into an ADC-capable pin (GPIO 0-6; the settled design shares the back
button's GPIO0/A0 -- the high-impedance divider coexists fine with the
button, and firmware time-shares the pin with a brief ADC read once
per wake). Set `FRAME_BATTERY_ADC_GPIO` to match.
- **Mains detection** (optional): a 2x100k divider from the 5V pin
(which only carries voltage when USB is plugged in) into any spare
GPIO -- plain digital high/low, no ADC needed. The divider is
required: raw 5V exceeds the 3.3V pin limit. Set
`FRAME_VBUS_SENSE_GPIO`.
**Behavior**: once per wake the firmware reads the battery voltage,
converts it to a percent via a LiPo discharge curve, shows it (battery
icon + "NN%") below the "scan to manage" QR box whenever the management
menu is up, and reports it to the server (`POST /frame/battery`), which
displays it in the web UI with an "as of" timestamp. All of that is
skipped when on mains power (the charging voltage would read
misleadingly full), when the reading is implausible (no battery
attached), or when the feature is disabled.
## Managing the queue, soft-resetting, and factory-resetting
One more button, wired between GPIO1 and GND (same wiring style as the