Compare commits

...
4 Commits
Author SHA1 Message Date
Kaj SchittecatandClaude Opus 4.8 c931df6054 touch: beta_10 — trackball D-pad mode, microSD tile-cache fill, Battery group
- T-Deck: trackball mouse/D-pad navigation toggle (Settings -> Keyboard).
- Map (#20): microSD-tile mode now caches Wi-Fi-fetched tiles onto the SD card so
  the offline library fills in + grows instead of going blank past its edge.
- Power: PR #19 idle power-save (Dan Vybiral / @Vybo) relocated into a new
  'Battery' group under Settings -> System, alongside a button that opens the
  battery / power-history screen.
- TouchCfg v10 (ui_scale, tb_keypad, sleep_idle). Mesh America catalog -> beta_10.

Curated touch overlay onto main (the WIP Tanmatsu ESP32-P4 build system stays on
the tanmatsu-port branch). Co-Authored-By: Dan Vybiral <dan.vybiral@greencode.cz>

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 00:08:13 +02:00
Dan Vybiral 11b31e1cdd touch: idle power-save — loop-throttle + downclock when parked (#19)
Opt-in Idle power-save for the T-Deck: when parked (screen off, on battery, no
companion client, Wi-Fi+BLE off, mesh idle) the main loop is throttled with
vTaskDelay so the FreeRTOS idle task can halt the CPU between ticks, on top of the
existing 80 MHz screen-off downclock — without ever stopping mesh RX. Default OFF
(Settings -> Lock, persisted TouchCfg v8 sleep_idle); status-bar moon indicator +
idle stats. Heltec V4 compiles (gate never passes there). A plain scheduler throttle
rather than esp_light_sleep_start(), which trips INT_WDT in this build.

Co-authored-by: Dan Vybiral <dan.vybiral@greencode.cz>
2026-06-21 22:39:59 +02:00
Kaj SchittecatandClaude Opus 4.8 c16b5230a6 release: auto-refresh + push the Mesh America catalog each beta
release.sh now regenerates deploy/meshamerica-catalog.json for the tag and, when
run on main, commits + pushes that one file so apps.meshamerica.com (the flasher
Cascadia Mesh uses) always points at the newest immutable bins instead of going
stale on the previous beta. Idempotent (no-op when already current); non-fatal if
the push fails; also mirrors the catalog into the VPS publish tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 22:27:28 +02:00
Kaj SchittecatandClaude Opus 4.8 f5362b1cb3 deploy: add Mesh America Configurator provider catalog
Catalog JSON (Mesh America provider schema) + a reusable generator so the two
wadamesh touch boards appear in the Mesh America Device Configurator
(apps.meshamerica.com — the flasher Cascadia Mesh uses). Points at the immutable
per-tag bins on firmware.wadamesh.com (HTTPS + CORS). Served live over
raw.githubusercontent.com.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 22:20:29 +02:00
13 changed files with 2023 additions and 188 deletions

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Generate the Mesh America Configurator catalog JSON for wadamesh.
Mesh America (apps.meshamerica.com — the flasher Cascadia Mesh uses) merges a
provider "catalog" JSON into its device list. Spec: third-party hosts ONE static
JSON (this file's output) at a stable HTTPS+CORS URL, plus the firmware bins over
HTTPS+CORS; their app reads it live. See provider-api-spec.md from TJ Downes.
We host:
- the bins on firmware.wadamesh.com/releases/TOUCH/<TAG>/ (Cloudflare HTTPS +
`Access-Control-Allow-Origin: *`, immutable per-tag dir — see deploy/nginx/
firmware.wadamesh.com.conf), and
- this catalog at a stable HTTPS+CORS URL (deployed alongside the firmware).
Usage: python3 deploy/gen-meshamerica-catalog.py <TAG> > meshamerica-catalog.json
(TAG defaults to the newest beta_N dir; notes come from release-notes/<TAG>.txt)
Re-run it each release (or wire it into scripts/release.sh) so the catalog always
points at the newest immutable bins — the spec wants firmware URLs treated as
immutable, so each release adds a fresh versioned URL.
"""
import sys, json, os, glob, re
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def newest_tag():
tags = []
for p in glob.glob(os.path.join(REPO, "release-notes", "beta_*.txt")):
m = re.search(r"beta_(\d+)", os.path.basename(p))
if m:
tags.append(int(m.group(1)))
return "beta_%d" % max(tags) if tags else "beta_9"
TAG = sys.argv[1] if len(sys.argv) > 1 else newest_tag()
BASE = "https://firmware.wadamesh.com/releases/TOUCH/%s" % TAG
ICON = "https://wadamesh.com/wadamesh-icon-512.png"
# "What's new" — one user-facing line per non-blank, non-# line of the notes file.
notes = ""
nf = os.path.join(REPO, "release-notes", "%s.txt" % TAG)
if os.path.exists(nf):
notes = "\n".join(l.strip() for l in open(nf, encoding="utf-8")
if l.strip() and not l.strip().startswith("#"))
# Both touch boards are ESP32-S3, single touch-UI ("gui") role. ota_0 sits at
# 0x10000 on both (partitions_t*_touch.csv), so flash-update@0x10000 is correct.
BOARDS = [
{"name": "wadamesh — Heltec V4 TFT", "slug": "heltec-v4-tft"},
{"name": "wadamesh — LilyGo T-Deck", "slug": "tdeck"},
]
def device(b):
merged, appbin = "wadamesh-%s-merged.bin" % b["slug"], "wadamesh-%s.bin" % b["slug"]
return {
"maker": "wadamesh",
"class": "wadamesh",
"name": b["name"],
"type": "esp32",
"tooltip": "<img class='device' src='%s'>" % ICON,
"firmware": [{
"role": "gui",
"title": "Touch UI",
"version": {TAG: {
"notes": notes,
"files": [
{"type": "flash-wipe", "name": merged,
"url": "%s/%s" % (BASE, merged),
"title": "Full install (bootloader + firmware)"},
{"type": "flash-update", "name": appbin,
"url": "%s/%s" % (BASE, appbin),
"title": "Update (app only)"},
],
}},
}],
}
catalog = {
"maker": {"wadamesh": {"name": "wadamesh"}},
"device": [device(b) for b in BOARDS],
}
print(json.dumps(catalog, indent=2, ensure_ascii=False))
+73
View File
@@ -0,0 +1,73 @@
{
"maker": {
"wadamesh": {
"name": "wadamesh"
}
},
"device": [
{
"maker": "wadamesh",
"class": "wadamesh",
"name": "wadamesh — Heltec V4 TFT",
"type": "esp32",
"tooltip": "<img class='device' src='https://wadamesh.com/wadamesh-icon-512.png'>",
"firmware": [
{
"role": "gui",
"title": "Touch UI",
"version": {
"beta_10": {
"notes": "",
"files": [
{
"type": "flash-wipe",
"name": "wadamesh-heltec-v4-tft-merged.bin",
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_10/wadamesh-heltec-v4-tft-merged.bin",
"title": "Full install (bootloader + firmware)"
},
{
"type": "flash-update",
"name": "wadamesh-heltec-v4-tft.bin",
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_10/wadamesh-heltec-v4-tft.bin",
"title": "Update (app only)"
}
]
}
}
}
]
},
{
"maker": "wadamesh",
"class": "wadamesh",
"name": "wadamesh — LilyGo T-Deck",
"type": "esp32",
"tooltip": "<img class='device' src='https://wadamesh.com/wadamesh-icon-512.png'>",
"firmware": [
{
"role": "gui",
"title": "Touch UI",
"version": {
"beta_10": {
"notes": "",
"files": [
{
"type": "flash-wipe",
"name": "wadamesh-tdeck-merged.bin",
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_10/wadamesh-tdeck-merged.bin",
"title": "Full install (bootloader + firmware)"
},
{
"type": "flash-update",
"name": "wadamesh-tdeck.bin",
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_10/wadamesh-tdeck.bin",
"title": "Update (app only)"
}
]
}
}
}
]
}
]
}
+13
View File
@@ -0,0 +1,13 @@
# beta_10 — what's new (one user-facing note per non-blank, non-# line)
# --- T-Deck input ---
New trackball mode — switch the T-Deck trackball between a mouse cursor (the default) and D-pad navigation (roll to move the selection, click to choose), under Settings → Keyboard. Handy if you prefer button-style navigation.
# --- Map ---
microSD map tiles now fill in as you explore: with "Use microSD for tiles" on, the device also downloads any missing tiles over Wi-Fi and saves them straight onto the SD card, so your offline library grows instead of showing blank areas past its edge (thanks 1nerdherder, #20)
# --- Power ---
Experimental battery saver (idle power-save): throttles the CPU when the device is parked — screen off, on battery, no app connected and the mesh quiet — to trim idle power use, without ever missing a received packet. Off by default; enable it under Settings → System → Battery. (Dan Vybiral / @Vybo, #19)
# --- Settings ---
New "Battery" section under Settings → System — opens the battery & power-history screen (the same one you get by tapping the battery icon up top) and is where the experimental battery saver now lives (it moved out of the Lock settings).
+22
View File
@@ -54,6 +54,28 @@ echo "listing: $(python3 -c 'import json,sys;print(", ".join(x["name"] for x in
# frozen one. Notes come from release-notes/<tag>.txt (one per line; optional).
python3 "$ROOT/scripts/build/gen-flasher-meta.py" "$TAG" "$LATEST" "$ROOT/release-notes/$TAG.txt"
# 3c. Mesh America Configurator provider catalog (apps.meshamerica.com — the web
# flasher Cascadia Mesh uses). It is served LIVE from raw.githubusercontent
# main, so each release must regenerate it for $TAG and push that ONE file to
# main; otherwise the configurator keeps offering the previous beta. It points
# at the immutable /releases/TOUCH/$TAG/ bins published below.
# (Generator: deploy/gen-meshamerica-catalog.py — reads release-notes/$TAG.txt.)
CATALOG="$ROOT/deploy/meshamerica-catalog.json"
python3 "$ROOT/deploy/gen-meshamerica-catalog.py" "$TAG" > "$CATALOG.tmp" && mv "$CATALOG.tmp" "$CATALOG"
mkdir -p "$OUT/meshamerica" && cp "$CATALOG" "$OUT/meshamerica/catalog.json" # also mirror to the VPS (enables the branded URL once its nginx location is deployed)
if git -C "$ROOT" diff --quiet -- "$CATALOG"; then
echo "Mesh America catalog already current for $TAG"
elif [ "$(git -C "$ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null)" = "main" ]; then
if git -C "$ROOT" commit -q -m "chore: refresh Mesh America catalog for $TAG" -- "$CATALOG" \
&& git -C "$ROOT" push origin HEAD:main; then
echo "Mesh America catalog refreshed + pushed to main ($TAG)"
else
echo "WARN: Mesh America catalog commit/push failed — push deploy/meshamerica-catalog.json to main by hand"
fi
else
echo "NOTE: regenerated $CATALOG for $TAG but not on 'main' — commit + push it to main so apps.meshamerica.com updates."
fi
# 4. Publish to the VPS (Cloudflare caches at the edge).
if [ -n "$DEST" ]; then
rsync -av "$OUT/" "$DEST:$DEST_PATH/"
+10
View File
@@ -780,6 +780,16 @@ public:
// To check if there is pending work
bool hasPendingWork() const;
// Number of companion clients currently connected on any transport.
// Used by the idle light-sleep gate (TouchSleep) to confirm no one is
// actively talking to us before the node parks in light sleep.
int getProtoNumClients() const { return proto_num_clients; }
// True if the radio is currently mid-receive of a packet (preamble→RxDone).
// _radio is the protected Dispatcher::Radio pointer; isReceiving() returns false
// by default and is overridden by RadioLibWrapper with a real preamble-detect check.
bool isRadioReceiving() const { return _radio && _radio->isReceiving(); }
private:
void writeOKFrame();
void writeErrFrame(uint8_t err_code);
+36 -1
View File
@@ -35,7 +35,7 @@ static bool s_begun = false;
// short read (→ treat as absent → defaults); `ver` lets later builds add fields.
static const char* KEY_CFG = "cfg";
static const uint16_t TOUCH_CFG_MAGIC = 0x5743; // 'WC' (WadaCfg)
static const uint8_t TOUCH_CFG_VER = 7; // v2 sig_probe/poll; v3 tz_zone; v4 hide_node_name; v5 map_night/map_zoom; v6 map text/marker visibility; v7 app_grid_large
static const uint8_t TOUCH_CFG_VER = 10; // v2 sig_probe/poll; v3 tz_zone; v4 hide_node_name; v5 map_night/map_zoom; v6 map text/marker visibility; v7 app_grid_large; v8 ui_scale; v9 tb_keypad; v10 sleep_idle
// Defaults (kept identical to the historical per-key defaults).
static const uint16_t DEFAULT_SCREEN_TIMEOUT_S = 20;
@@ -80,6 +80,9 @@ struct __attribute__((packed)) TouchCfg {
uint8_t map_show_tilexyz; // show the zoom + tile z/x/y line on the map (bool) — v6
uint8_t map_show_contacts;// show contact markers on the map (bool) — v6
uint8_t app_grid_large; // app drawer: large grid (one fewer column, bigger icons) — v7
uint8_t ui_scale; // UI resolution scale: 0=100% 1=150% 2=200% (Tanmatsu, applied at boot) — v8
uint8_t tb_keypad; // T-Deck trackball: 0=mouse cursor (default), 1=D-pad focus navigation — v9
uint8_t sleep_idle; // idle light-sleep feature on/off (bool) — v10 (trailing so existing blobs default it OFF)
};
static TouchCfg s_cfg;
@@ -136,6 +139,9 @@ static void cfgSetDefaults(TouchCfg& c) {
c.map_show_tilexyz = 1;
c.map_show_contacts = 1;
c.app_grid_large = 0; // default: compact app grid (T-Deck 4 cols / V4 3 cols)
c.ui_scale = 0; // default: 100% UI scale (native resolution)
c.tb_keypad = 0; // default: trackball = mouse cursor
c.sleep_idle = 0; // default: idle light-sleep OFF
}
// Persist the whole blob using the same end()/begin(RW)/put/end()/begin(RO)
@@ -725,6 +731,15 @@ bool touchPrefsSetMapZoom(uint8_t z) {
s_cfg.map_zoom = z;
return cfgFlush();
}
bool touchPrefsGetSleepIdle() {
if (!s_begun) touchPrefsBegin();
return s_cfg.sleep_idle != 0;
}
bool touchPrefsSetSleepIdle(bool on) {
if (!s_begun) touchPrefsBegin();
s_cfg.sleep_idle = on ? 1 : 0;
return cfgFlush();
}
bool touchPrefsGetMapShowCoords() {
if (!s_begun) touchPrefsBegin();
@@ -763,6 +778,26 @@ bool touchPrefsSetAppGridLarge(bool on) {
return cfgFlush();
}
uint8_t touchPrefsGetUiScale() {
if (!s_begun) touchPrefsBegin();
return s_cfg.ui_scale > 2 ? 0 : s_cfg.ui_scale; // 0=100% 1=150% 2=200%
}
bool touchPrefsSetUiScale(uint8_t scale) {
if (!s_begun) touchPrefsBegin();
s_cfg.ui_scale = scale > 2 ? 0 : scale;
return cfgFlush();
}
bool touchPrefsGetTbKeypad() {
if (!s_begun) touchPrefsBegin();
return s_cfg.tb_keypad != 0;
}
bool touchPrefsSetTbKeypad(bool on) {
if (!s_begun) touchPrefsBegin();
s_cfg.tb_keypad = on ? 1 : 0;
return cfgFlush();
}
// Store ALL device data (identity, prefs, contacts, channels) on the SD card
// under /meshcomod instead of internal SPIFFS. Read at boot (main.cpp) BEFORE
// the data loads, so changing it needs a reboot. Key "use_sd" in the "touch"
+15
View File
@@ -103,6 +103,21 @@ bool touchPrefsSetMapShowContacts(bool on);
* Default false = the compact grid (T-Deck 4 cols / Heltec V4 3 cols). */
bool touchPrefsGetAppGridLarge();
bool touchPrefsSetAppGridLarge(bool on);
/** Idle light-sleep feature: when ON, the device enters light sleep while the
* screen is off and all activity gates pass. Default OFF. */
bool touchPrefsGetSleepIdle();
bool touchPrefsSetSleepIdle(bool on);
/* UI resolution scale (Tanmatsu): 0=100% (native 800x480), 1=150%, 2=200%. Applied at boot —
* LVGL renders at a lower resolution and the flush upscales to the panel. Reboot to apply. */
uint8_t touchPrefsGetUiScale();
bool touchPrefsSetUiScale(uint8_t scale);
/* T-Deck trackball interaction mode: false (default) = the trackball drives a soft
* mouse cursor; true = D-pad focus navigation (↑↓←→ move focus, ball click = enter),
* like the Tanmatsu. Applied live (no reboot). */
bool touchPrefsGetTbKeypad();
bool touchPrefsSetTbKeypad(bool on);
/** Hide the device/profile name in the status bar and move the clock to the
* left where the name used to be. Default false (name shown). */
+9
View File
@@ -177,6 +177,11 @@ bool wifiConfigConsumeApplyRequest() {
}
void wifiConfigApply() {
#if defined(HAS_TANMATSU)
printf("[WIFI] apply radio_en=%d hasRuntime=%d mode=%d status=%d\n",
(int)wifiConfigGetRadioEnabled(), (int)wifiConfigHasRuntime(),
(int)WiFi.getMode(), (int)WiFi.status());
#endif
if (!wifiConfigGetRadioEnabled()) {
WiFi.disconnect(true);
delay(50);
@@ -188,9 +193,13 @@ void wifiConfigApply() {
char pwd[WIFI_CONFIG_PWD_MAX];
wifiConfigGetSsid(ssid, sizeof(ssid));
wifiConfigGetPwd(pwd, sizeof(pwd));
WiFi.mode(WIFI_STA); // esp_wifi_remote needs an explicit STA mode before begin()
WiFi.disconnect();
delay(100);
WiFi.begin(ssid, pwd[0] ? pwd : nullptr);
#if defined(HAS_TANMATSU)
printf("[WIFI] begin ssid='%s' pwlen=%d\n", ssid, (int)strlen(pwd));
#endif
}
#endif
+8
View File
@@ -12,6 +12,7 @@
#include "helpers/esp32/SdNvsPrefs.h" // route prefs to file storage (SD/SPIFFS), off NVS
// (quoted: use wadamesh's src/ copy, not the lib's stale one)
#include "wadamesh_mark_rgb.h" // anti-aliased mesh-mark (RGB565) for the pre-LVGL boot screen
#include "ui-touch/TouchSleep.h" // idle light-sleep controller (loopEnd called at end of loop())
#endif
// Believe it or not, this std C function is busted on some platforms!
@@ -672,4 +673,11 @@ void loop() {
#if defined(ESP32_PLATFORM)
board.pollHttpOtaReboot();
#endif
#if defined(HAS_TOUCH_UI)
// Idle light-sleep gate: evaluated every loop tick. g_enabled is false by
// default (Task 1 is inert); Task 2 sets it from the NVS pref and wires
// the real predicates so the gate can actually pass and arm light sleep.
touchSleep::loopEnd(millis());
#endif
}
+78
View File
@@ -0,0 +1,78 @@
#include "TouchSleep.h"
#include <esp_timer.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
namespace touchSleep {
namespace {
// When the gate passes (device parked: screen off, no companion client, WiFi+BLE
// off, on battery, mesh idle) we THROTTLE the main loop with vTaskDelay instead of
// entering esp_light_sleep.
//
// Why not real light sleep: manual esp_light_sleep_start() from the Arduino loop
// task trips the Interrupt Watchdog in this build (dual-core, no PM framework,
// INT_WDT on both cores @ 300 ms, always-on companion/LVGL/USB tasks) — reliably,
// regardless of duration or wake-source handling. vTaskDelay is a standard scheduler
// yield: the FreeRTOS idle task halts the CPU (WFI) between 1 ms ticks, trimming the
// idle busy-spin draw, with zero watchdog/hang risk. The radio stays in RX; an
// incoming packet is serviced on the next loop iteration (<= THROTTLE_MS late).
constexpr uint32_t THROTTLE_MS = 50;
uint64_t g_acc_idle_us = 0; // cumulative time spent parked/throttled
Hooks g_hooks = {};
TransitionCb g_transition = nullptr;
bool g_enabled = false; // default OFF; driven from NVS via touchPrefsGetSleepIdle() at init
bool g_asleep_regime = false;
uint32_t g_cycle_count = 0; // throttle cycles while parked (liveness counter)
WakeReason g_last_reason = WakeReason::None;
bool gatePasses() {
if (!g_enabled) return false;
if (!g_hooks.screenOff || !g_hooks.screenOff()) return false;
if (!g_hooks.noClient || !g_hooks.noClient()) return false;
if (!g_hooks.wifiOff || !g_hooks.wifiOff()) return false;
if (!g_hooks.bleOff || !g_hooks.bleOff()) return false;
if (!g_hooks.onBattery || !g_hooks.onBattery()) return false;
if (!g_hooks.meshIdle || !g_hooks.meshIdle()) return false;
return true;
}
void emitTransition(bool entering) {
if (g_transition && g_hooks.epochNow) g_transition(g_hooks.epochNow(), entering);
}
} // namespace
void begin(const Hooks& hooks) { g_hooks = hooks; }
void onTransition(TransitionCb cb) { g_transition = cb; }
void setEnabled(bool on) { g_enabled = on; }
bool enabled() { return g_enabled; }
void loopEnd(uint32_t /*now_ms*/) {
const bool pass = gatePasses();
if (!pass) {
if (g_asleep_regime) { g_asleep_regime = false; emitTransition(false); } // sun — resumed activity
return;
}
if (!g_asleep_regime) { g_asleep_regime = true; emitTransition(true); } // moon — parked
// Yield the CPU to the idle task for THROTTLE_MS (replaces esp_light_sleep_start —
// see the note above). Standard FreeRTOS call: cannot starve a watchdog or hang.
const uint64_t t0 = esp_timer_get_time();
vTaskDelay(pdMS_TO_TICKS(THROTTLE_MS));
g_acc_idle_us += (uint64_t)(esp_timer_get_time() - t0);
g_cycle_count++;
g_last_reason = WakeReason::Timer; // a throttle is a timed yield
}
bool isSleeping() { return g_asleep_regime; } // true while parked/throttling
uint32_t wakeCount() { return g_cycle_count; } // throttle cycles while parked
WakeReason lastWakeReason() { return g_last_reason; }
uint8_t pctAsleep() {
const uint64_t up = (uint64_t)esp_timer_get_time();
if (up == 0) return 0;
uint64_t pct = (g_acc_idle_us * 100ULL) / up;
return pct > 100 ? 100 : (uint8_t)pct;
}
} // namespace touchSleep
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include <stdint.h>
// Idle power-save controller for the T-Deck (ESP32-S3). When the device is parked
// (screen off, on battery, standalone) it throttles the main loop with vTaskDelay
// so the FreeRTOS idle task can halt the CPU between ticks. (Real esp_light_sleep
// trips the Interrupt Watchdog in this dual-core / no-PM build — see TouchSleep.cpp
// and .notes/2026-06-18-light-sleep-rx-design.md for the full story.)
//
// Decoupled from UI/mesh by predicate hooks supplied by the integration layer
// (UITask.cpp), so this module pulls in no UI/mesh/LVGL headers.
namespace touchSleep {
enum class WakeReason : uint8_t { None, Timer, Packet, Touch, Button, Other };
struct Hooks {
bool (*screenOff)(); // display is off
bool (*noClient)(); // no companion client on any transport
bool (*wifiOff)(); // WiFi fully off
bool (*bleOff)(); // BLE fully off
bool (*onBattery)(); // running on battery (no USB / charge source)
bool (*meshIdle)(); // radio not mid-RX AND send queue empty
// ms until the soonest wake-forcing deadline (advert / clock alarm),
// or UINT32_MAX when nothing must wake us:
uint32_t (*nextWakeForcingDueMs)(uint32_t now_ms);
uint32_t (*epochNow)(); // wall-clock epoch seconds (for the event log)
};
// regime transition: entering==true -> went to sleep (moon)
// entering==false -> woke to activity (sun)
using TransitionCb = void (*)(uint32_t epoch, bool entering);
void begin(const Hooks& hooks); // install hooks; arm static GPIO wake config once
void onTransition(TransitionCb cb);
void setEnabled(bool on); // feature toggle (driven by the NVS pref)
bool enabled();
void loopEnd(uint32_t now_ms); // call at the very END of the main loop()
// instrumentation (read by the settings / diag UI)
bool isSleeping(); // currently in the asleep regime
uint32_t wakeCount(); // cumulative meaningful wakes
WakeReason lastWakeReason();
uint8_t pctAsleep(); // % wall-time asleep since boot (0..100)
} // namespace touchSleep
+1484 -187
View File
File diff suppressed because it is too large. Load diff
+148
View File
@@ -0,0 +1,148 @@
/*******************************************************************************
* Size: 16 px
* Bpp: 4
* Opts: --no-compress --bpp 4 --size 16 --font /Applications/MeshCore.app/Wrapper/Runner.app/Frameworks/App.framework/flutter_assets/packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf -r 0xF185 -r 0xF186 --format lvgl --lv-font-name sleepicons_font -o src/ui-touch/sleepicons_font.c
******************************************************************************/
#include "lvgl.h"
#ifndef SLEEPICONS_FONT
#define SLEEPICONS_FONT 1
#endif
#if SLEEPICONS_FONT
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+F185 "" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x8, 0xa1, 0x0, 0x1a, 0x80,
0x0, 0x0, 0x0, 0x0, 0xc, 0xfe, 0x55, 0xef,
0xc0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xff, 0xff,
0xff, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xff,
0xff, 0xff, 0xf4, 0x0, 0x0, 0x8, 0xcf, 0xff,
0xd5, 0x11, 0x5d, 0xff, 0xfc, 0x80, 0xa, 0xff,
0xfd, 0x18, 0xee, 0x81, 0xdf, 0xff, 0xa0, 0x1,
0xef, 0xf5, 0x8f, 0xff, 0xf8, 0x5f, 0xfe, 0x10,
0x0, 0x5f, 0xf1, 0xef, 0xff, 0xfe, 0x1f, 0xf5,
0x0, 0x0, 0x5f, 0xf1, 0xef, 0xff, 0xfe, 0x1f,
0xf5, 0x0, 0x1, 0xef, 0xf5, 0x8f, 0xff, 0xf8,
0x5f, 0xfe, 0x10, 0xa, 0xff, 0xfd, 0x18, 0xee,
0x81, 0xdf, 0xff, 0xa0, 0x8, 0xcf, 0xff, 0xd5,
0x11, 0x5d, 0xff, 0xfc, 0x80, 0x0, 0x0, 0x4f,
0xff, 0xff, 0xff, 0xf4, 0x0, 0x0, 0x0, 0x0,
0xf, 0xff, 0xff, 0xff, 0xf0, 0x0, 0x0, 0x0,
0x0, 0xc, 0xfe, 0x55, 0xef, 0xc0, 0x0, 0x0,
0x0, 0x0, 0x8, 0xa1, 0x0, 0x1a, 0x80, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
/* U+F186 "" */
0x0, 0x1, 0x7c, 0xee, 0x0, 0x0, 0x0, 0x4,
0xef, 0xfe, 0x30, 0x0, 0x0, 0x5, 0xff, 0xff,
0x20, 0x0, 0x0, 0x1, 0xff, 0xff, 0x80, 0x0,
0x0, 0x0, 0x7f, 0xff, 0xf2, 0x0, 0x0, 0x0,
0xc, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xef,
0xff, 0xf1, 0x0, 0x0, 0x0, 0xe, 0xff, 0xff,
0x40, 0x0, 0x0, 0x0, 0xcf, 0xff, 0xfc, 0x0,
0x0, 0x0, 0x7, 0xff, 0xff, 0xf8, 0x0, 0x0,
0x0, 0x1e, 0xff, 0xff, 0xfa, 0x20, 0x0, 0x0,
0x4f, 0xff, 0xff, 0xff, 0xdc, 0xb0, 0x0, 0x4e,
0xff, 0xff, 0xff, 0xf5, 0x0, 0x0, 0x17, 0xce,
0xfc, 0x81, 0x0
};
/*---------------------
* GLYPH DESCRIPTION
*--------------------*/
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
{.bitmap_index = 0, .adv_w = 256, .box_w = 18, .box_h = 18, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 162, .adv_w = 192, .box_w = 13, .box_h = 14, .ofs_x = 0, .ofs_y = -1}
};
/*---------------------
* CHARACTER MAPPING
*--------------------*/
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
{
.range_start = 61829, .range_length = 2, .glyph_id_start = 1,
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
}
};
/*--------------------
* ALL CUSTOM DATA
*--------------------*/
#if LVGL_VERSION_MAJOR == 8
/*Store all the custom data of the font*/
static lv_font_fmt_txt_glyph_cache_t cache;
#endif
#if LVGL_VERSION_MAJOR >= 8
static const lv_font_fmt_txt_dsc_t font_dsc = {
#else
static lv_font_fmt_txt_dsc_t font_dsc = {
#endif
.glyph_bitmap = glyph_bitmap,
.glyph_dsc = glyph_dsc,
.cmaps = cmaps,
.kern_dsc = NULL,
.kern_scale = 0,
.cmap_num = 1,
.bpp = 4,
.kern_classes = 0,
.bitmap_format = 0,
#if LVGL_VERSION_MAJOR == 8
.cache = &cache
#endif
};
/*-----------------
* PUBLIC FONT
*----------------*/
/*Initialize a public general font descriptor*/
#if LVGL_VERSION_MAJOR >= 8
const lv_font_t sleepicons_font = {
#else
lv_font_t sleepicons_font = {
#endif
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
.line_height = 18, /*The maximum line height required by the font*/
.base_line = 3, /*Baseline measured from the bottom of the line*/
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
.subpx = LV_FONT_SUBPX_NONE,
#endif
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
.underline_position = -1,
.underline_thickness = 1,
#endif
.dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9
.fallback = NULL,
#endif
.user_data = NULL,
};
#endif /*#if SLEEPICONS_FONT*/