a0dc4e0115
Closes the sell-to-friend gap where a buyer's freshly-reset device
would briefly display the seller's photos before the buyer reached
/setup/{mac} to claim. The firmware had no way to tell the server
"I just got reset" — now it does.
Flow:
- WiFi-setup completion (handle_connect in main.cpp) writes
NVS_KEY_JUST_PROVISIONED=1 alongside the SSID/PASS save.
- Every poll while the flag is set sends X-Just-Provisioned: 1.
- Server (DeviceImageController, paired commit on the webApp side)
responds with 204 + X-Interval-Ms when the binding is stale,
forcing the device to its setup-QR fallback. Once the user
re-claims via /setup/{mac}, the binding is fresh, and the server
answers with X-Claimed: 1 alongside whatever response code applies.
- Firmware clears the NVS flag on seeing X-Claimed: 1 — once
cleared, the device is back to normal long-stable polling.
Tests:
- PROV-A: flag set in NVS → header on the request
- PROV-B: no flag → no header (steady state)
- PROV-C: response with X-Claimed: 1 → flag cleared
- PROV-D: response without X-Claimed → flag stays (so the next
poll keeps signaling "not yet acknowledged")
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
152 lines
6.4 KiB
C
152 lines
6.4 KiB
C
#pragma once
|
|
// repo: pictureFrame-firmware
|
|
//
|
|
// Hardware specifics (panel dimensions + GPIO pinout) come from the build
|
|
// env's -D flags in platformio.ini, not from this file. That keeps the
|
|
// shared sources panel-agnostic and lets each `src/panels/{vendor}/v{N}/`
|
|
// subtree stay focused on its own driver.
|
|
//
|
|
// Strict: missing a -D flag is a compile error here, not a silent fallback
|
|
// to V1 values. A bad env config fails fast at build time. Native tests
|
|
// (UNIT_TEST defined) get harmless defaults — they don't drive real
|
|
// hardware, so the values never matter beyond satisfying the compiler.
|
|
|
|
// ── EPD pixel dimensions ─────────────────────────────────────────────────
|
|
#ifndef EPD_WIDTH
|
|
# ifdef UNIT_TEST
|
|
# define EPD_WIDTH 800
|
|
# else
|
|
# error "EPD_WIDTH not defined — set -DEPD_WIDTH=… in platformio.ini build_flags"
|
|
# endif
|
|
#endif
|
|
#ifndef EPD_HEIGHT
|
|
# ifdef UNIT_TEST
|
|
# define EPD_HEIGHT 480
|
|
# else
|
|
# error "EPD_HEIGHT not defined — set -DEPD_HEIGHT=… in platformio.ini build_flags"
|
|
# endif
|
|
#endif
|
|
|
|
// Largest panel width any build env may target. Static buffers in the
|
|
// driver use this to size themselves at compile time so we don't pay for
|
|
// heap allocation on every draw. Default: assume the active env's panel
|
|
// (so V1 builds get an 800-wide buffer; V2 builds will set it to 1872).
|
|
#ifndef MAX_PANEL_WIDTH
|
|
#define MAX_PANEL_WIDTH EPD_WIDTH
|
|
#endif
|
|
|
|
// ── GPIO pinout ──────────────────────────────────────────────────────────
|
|
// Same UNIT_TEST escape: native tests get the V1 dev-board pinout so
|
|
// shared sources compile, even though no real GPIO is touched.
|
|
#ifndef PIN_SCK
|
|
# ifdef UNIT_TEST
|
|
# define PIN_SCK 18
|
|
# else
|
|
# error "PIN_SCK not defined — set -DPIN_SCK=… in platformio.ini build_flags"
|
|
# endif
|
|
#endif
|
|
#ifndef PIN_MOSI
|
|
# ifdef UNIT_TEST
|
|
# define PIN_MOSI 23
|
|
# else
|
|
# error "PIN_MOSI not defined — set -DPIN_MOSI=… in platformio.ini build_flags"
|
|
# endif
|
|
#endif
|
|
#ifndef PIN_CS
|
|
# ifdef UNIT_TEST
|
|
# define PIN_CS 5
|
|
# else
|
|
# error "PIN_CS not defined — set -DPIN_CS=… in platformio.ini build_flags"
|
|
# endif
|
|
#endif
|
|
#ifndef PIN_DC
|
|
# ifdef UNIT_TEST
|
|
# define PIN_DC 17
|
|
# else
|
|
# error "PIN_DC not defined — set -DPIN_DC=… in platformio.ini build_flags"
|
|
# endif
|
|
#endif
|
|
#ifndef PIN_RST
|
|
# ifdef UNIT_TEST
|
|
# define PIN_RST 16
|
|
# else
|
|
# error "PIN_RST not defined — set -DPIN_RST=… in platformio.ini build_flags"
|
|
# endif
|
|
#endif
|
|
#ifndef PIN_BUSY
|
|
# ifdef UNIT_TEST
|
|
# define PIN_BUSY 4
|
|
# else
|
|
# error "PIN_BUSY not defined — set -DPIN_BUSY=… in platformio.ini build_flags"
|
|
# endif
|
|
#endif
|
|
|
|
// ── Panel telemetry strings ──────────────────────────────────────────────
|
|
// Reported to the server at provisioning. PANEL_ID identifies the hardware
|
|
// model (vendor + size); PANEL_FW_VERSION lives in src/panels/{…}/v{N}/version.h
|
|
// and ticks when the panel-specific driver code changes.
|
|
#ifndef PANEL_ID
|
|
#define PANEL_ID "unknown"
|
|
#endif
|
|
|
|
// ── Reset button (BOOT button = GPIO 0 on most dev boards) ──────────────
|
|
#define PIN_BTN_RESET 0
|
|
#define RESET_HOLD_MS 5000
|
|
|
|
// ── EPD color nibbles ────────────────────────────────────────────────────
|
|
// Verified on Waveshare 7.3" hardware. Same map on 13.3" Spectra 6.
|
|
#define COLOR_BLACK 0x0
|
|
#define COLOR_WHITE 0x1
|
|
#define COLOR_YELLOW 0x2
|
|
#define COLOR_RED 0x3
|
|
#define COLOR_BLUE 0x5
|
|
#define COLOR_GREEN 0x6
|
|
|
|
// ── NVS ──────────────────────────────────────────────────────────────────
|
|
#define NVS_NAMESPACE "pf"
|
|
#define NVS_KEY_SSID "ssid"
|
|
#define NVS_KEY_PASS "pass"
|
|
#define NVS_KEY_IMG_ID "img_id"
|
|
#define NVS_KEY_DRAW_NEEDED "draw"
|
|
#define NVS_KEY_ERR_BORDER "err" // set when display is showing a sync-fail border; force a clean redraw on next 200/304
|
|
#define NVS_KEY_SCHEMA_V "schema_v"
|
|
// Set on every fresh provisioning (WiFi-setup completion). Stays in NVS across
|
|
// reboots until the server explicitly acknowledges the device is claimed by
|
|
// returning X-Claimed: 1 — at which point the firmware clears the flag and
|
|
// resumes regular operation. Without this, a device that gets sold and reset
|
|
// would silently keep displaying the prior owner's photos until the new
|
|
// owner happens to navigate to /setup/{mac}.
|
|
#define NVS_KEY_JUST_PROVISIONED "just_prov"
|
|
|
|
// Bump when introducing a schema migration. Each new value can force a one-shot
|
|
// recovery action on first boot of the new firmware.
|
|
#define NVS_SCHEMA_VERSION 1
|
|
|
|
// Width of the sync-fail / no-WiFi border, in pixels.
|
|
#define BORDER_THICKNESS_PX 4
|
|
|
|
// ── Network ──────────────────────────────────────────────────────────────
|
|
#define APP_BASE_URL "https://pictureframe.edholm.me"
|
|
#define AP_IP "192.168.4.1"
|
|
#define WIFI_TIMEOUT_MS 30000
|
|
// Server's X-Interval-Ms is the primary schedule — driven by the user's
|
|
// rotationIntervalMinutes / wakeTimes settings. The constants below are
|
|
// only safety nets:
|
|
// - FALLBACK is used when the server omits the header (shouldn't happen
|
|
// in normal operation, but guards against a rolling deploy or hand-
|
|
// crafted response).
|
|
// - CLAMP_MIN/MAX bound the server value to sane physical limits — no
|
|
// runaway polling on a malformed 0/negative, no week-long naps on a
|
|
// misconfigured 999 days. CLAMP_MAX is just past 24 h to give DST
|
|
// transitions and edge-case wake-time math a little slack.
|
|
#ifndef FETCH_INTERVAL_MS_FALLBACK
|
|
#define FETCH_INTERVAL_MS_FALLBACK 60000ULL // 1 min, used only when no X-Interval-Ms header
|
|
#endif
|
|
#ifndef SLEEP_CLAMP_MIN_MS
|
|
#define SLEEP_CLAMP_MIN_MS 30000ULL // 30 s — protect against runaway polls
|
|
#endif
|
|
#ifndef SLEEP_CLAMP_MAX_MS
|
|
#define SLEEP_CLAMP_MAX_MS (25ULL * 60ULL * 60ULL * 1000ULL) // 25 h — past 24h with DST slack
|
|
#endif
|
|
#define IMAGE_PATH "/img.bin"
|