Files
espresso_frame/firmware/main/main.c
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

77 lines
3.0 KiB
C

#include <stdbool.h>
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "wifi_provisioning.h"
#include "frame_client.h"
#include "next_button.h"
#include "back_button.h"
#include "combo_button.h"
#include "battery.h"
static const char *TAG = "main";
void app_main(void)
{
/* Redirected/invalid captive-portal traffic generates a lot of noise
* at the default log level. */
esp_log_level_set("httpd_uri", ESP_LOG_ERROR);
esp_log_level_set("httpd_txrx", ESP_LOG_ERROR);
esp_log_level_set("httpd_parse", ESP_LOG_ERROR);
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_err_t nvs_err = nvs_flash_init();
if (nvs_err == ESP_ERR_NVS_NO_FREE_PAGES || nvs_err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
nvs_err = nvs_flash_init();
}
ESP_ERROR_CHECK(nvs_err);
/* Arms all three buttons as deep-sleep wakeup sources (so holding one
* wakes the device promptly, not just during its brief awake
* windows), then checks them -- covers both "held while asleep, just
* woke us up" and "held while powering on" the same way, since both
* look identical from here: the pin is just low right now. */
next_button_init();
back_button_init();
combo_button_init();
bool next_pressed = next_button_check();
bool back_pressed = back_button_check();
/* Next takes priority over back if somehow both read pressed at once
* (e.g. both held through a power-on) -- an arbitrary but
* deterministic tie-break, not expected to matter in practice. */
fetch_action_t action = next_pressed ? FETCH_ADVANCE : back_pressed ? FETCH_BACK : FETCH_NORMAL;
/* Soft-resets or clears config + restarts internally for a medium/
* long hold and never returns in those cases -- only returns here
* for "not pressed" (false) or "quick press" (true, show the menu). */
bool show_management_qr = combo_button_check();
/* Must come after the button checks: the battery pin is (by design,
* on the XIAO board) shared with a button, and the ADC read briefly
* takes the pin over -- see battery.h. -1 = no reading (disabled,
* on mains, or implausible). */
int battery_percent = battery_read_percent();
frame_config_t cfg;
esp_err_t cfg_err = frame_config_load(&cfg);
if (cfg_err == ESP_OK) {
ESP_LOGI(TAG, "Found stored config for '%s', connecting to home WiFi", cfg.sta_ssid);
if (frame_wifi_connect_sta(&cfg) == ESP_OK) {
frame_client_run(&cfg, action, show_management_qr, battery_percent);
return; /* frame_client_run currently never returns */
}
ESP_LOGW(TAG, "Could not connect to stored WiFi after %d attempts, falling back to provisioning",
CONFIG_FRAME_STA_CONNECT_MAX_RETRIES);
} else {
ESP_LOGI(TAG, "No stored config found (%s), starting provisioning", esp_err_to_name(cfg_err));
}
wifi_provisioning_start();
}