Files
espresso_frame/docs/hardware.md
T
tfaour 015993af00
Build and push server image / build-and-push (push) Successful in 32s
Add battery level reporting (Kconfig-gated) and display orientation
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).
2026-07-19 22:11:14 -04:00

101 lines
4.8 KiB
Markdown

# Hardware
## Parts
- An ESP32-C6 dev board (e.g. ESP32-C6-DevKitC-1). Needs 8MB flash --
see [`firmware/sdkconfig.defaults`](../firmware/sdkconfig.defaults) and
[`firmware/partitions.csv`](../firmware/partitions.csv) if yours differs.
- [Waveshare 7.3" E Ink Spectra 6 (E6) panel](https://www.waveshare.com/7.3inch-e-paper-hat-e.htm) --
800x480, 6-color, SPI.
- A USB cable for flashing/power.
## Wiring
The panel connects over SPI plus three control lines (data/command, reset,
busy). Defaults below match the reference build and are set in
[`firmware/components/epd7in3e/Kconfig`](../firmware/components/epd7in3e/Kconfig) --
override via `idf.py menuconfig` under **E-Paper Display (epd7in3e)
Configuration** if your wiring differs.
| Panel pin | ESP32-C6 GPIO | Kconfig option |
| --------- | ------------- | ----------------- |
| CLK | 20 | `EPD_PIN_CLK` |
| DIN | 19 | `EPD_PIN_MOSI` |
| CS | 18 | `EPD_PIN_CS` |
| DC | 9 | `EPD_PIN_DC` |
| RST | 10 | `EPD_PIN_RST` |
| BUSY | 11 | `EPD_PIN_BUSY` |
| VCC | 3.3V | -- |
| GND | GND | -- |
Optionally, three buttons, all wired the same way -- momentary push button
between the GPIO and GND, no external resistor needed (the firmware
enables each pin's internal pull-up, so it idles high and reads low when
pressed):
- **Next photo (GPIO2)**: a normal press skips immediately to the next
photo; see
[`firmware/README.md`](../firmware/README.md#skipping-to-the-next-photo).
- **Back photo (GPIO0)**: a normal press returns to the previously-shown
photo; see
[`firmware/README.md`](../firmware/README.md#going-back-to-the-previous-photo).
- **Menu / reset (GPIO1)**: one button, three actions by hold duration --
a quick press overlays a "scan to manage" QR code on the current photo
for 30 seconds; holding ~3s then releasing soft-resets the device
(config kept); holding ~15s factory-resets it (clears WiFi/server
config, reprovisions); see
[`firmware/README.md`](../firmware/README.md#managing-the-queue-soft-resetting-and-factory-resetting).
All three pins were picked because they're within GPIO 0-7 -- the only
pins the ESP32-C6 can wake from deep sleep on -- aren't strapping pins,
and aren't already used by the panel wiring above. Also, not
incidentally, GPIO 0-7 is *all* the deep-sleep-wakeup-capable pins this
chip has (`SOC_RTCIO_PIN_COUNT` is 8) -- worth knowing if you're
targeting a compact board like the Seeed XIAO ESP32-C6, which only
breaks out 3 of them (GPIO0/1/2). These three buttons were deliberately
designed to fit exactly that budget: two dedicated pins for the
actions where instant, unambiguous response matters most (next, back),
and everything else folded onto the third pin via hold duration instead
of needing its own pin.
A couple of things worth knowing if you pick different pins:
- **Avoid the ESP32-C6's strapping pins** (GPIO 4, 5, 8, 9, 15) and the
USB-JTAG pins (12, 13) where possible -- strapping pins are sampled at
reset to select boot mode. The reference wiring above already uses
GPIO9 for DC, which *is* a strapping pin; it's only sampled during
power-on/reset, so it's safe once the app is running, but if
flashing/boot ever misbehaves on your board, check whether the panel is
pulling that line low during reset.
- The panel's SPI interface is rated well above the firmware's default
4MHz clock (`EPD_SPI_CLOCK_HZ`), but breadboard/dupont-wire connections
are often unreliable much past a few MHz. Raise it once your physical
wiring is confirmed solid.
## Battery (optional, XIAO ESP32-C6)
For a battery-powered build on the Seeed XIAO ESP32-C6 (which has
BAT+/BAT- charge pads on its underside and charges over USB-C):
- A 1S 3.7V LiPo **with an integrated protection circuit** (the board
does no low-voltage cutoff of its own), soldered or JST-PH-pigtailed
to the BAT pads. JST-PH polarity is not standardized -- verify with a
multimeter before connecting.
- Battery level sense: 2x200k divider from BAT+ to GND, midpoint to
GPIO0/A0 (shared with the back button -- deliberate, see
[`firmware/README.md`](../firmware/README.md#battery-xiao-esp32-c6)).
- Optional mains detection: 2x100k divider from the 5V pin to GND,
midpoint to a spare GPIO (e.g. 22).
Both features are off by default in firmware
(`FRAME_BATTERY_ADC_GPIO`/`FRAME_VBUS_SENSE_GPIO` = -1).
## Power
The device spends nearly all its time in deep sleep, waking briefly once
an hour (configurable, see the server's web UI) to fetch and display a
photo. A full-color refresh on this panel takes 15-30+ seconds and draws
more current than deep sleep by a wide margin -- expect battery life (if
not running from USB power) to be dominated by refresh frequency, not
sleep current.