The Pikachus of the Clock page are icons

Stored as icons at boot from the firmware's bitmaps (cut to 80 x 19), so
they can be edited; the firmware copies stay the fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-16 22:18:58 +02:00
1 parent 51b8d87e86
commit 839e86e8f1
17 files changed
+68 -6

No files matched your search

+2
View File
@@ -423,6 +423,8 @@ const char *appBitmapToIcon(const char *name)
const char *appIconToBitmap(const char *name)
{
if (builtinIconName(name))
return "Built-in icons stay icons (the firmware has them as icons)";
Icon icon;
if (!loadIcon(name, icon))
return "Icon not found";
+1 -1
View File
@@ -118,7 +118,7 @@ const uint32_t SETUP_AP_DELAY = 20000; // open the AP when WiFi fails thi
const uint32_t WIFI_CONNECT_TIMEOUT = 30000;
const uint32_t WIFI_RESTART_TIME = 10UL * 60 * 1000;
#define FIRMWARE_VERSION "2.11.0"
#define FIRMWARE_VERSION "2.12.0"
// Home Assistant device id (do not change: Home Assistant's entities are linked to it)
const uint8_t HA_DEVICE_ID[] = {0x34, 0x86, 0x5d, 0x3a, 0x08, 0x28};
+1 -2
View File
@@ -16,8 +16,7 @@ struct BuiltinBitmap
static const BuiltinBitmap BUILTIN[] = {
{"Nintendo", nintendo_bitmap_Nintendo_new},
{"Pikachu", pikachu_clock_bitmap},
{"Pikachu yawning", pikachu_yawning_clock_bitmap},
// The Pikachus of the Clock page are icons (display/icon_store)
};
static bool mounted = false;
+52 -1
View File
@@ -6,11 +6,45 @@
#include <strings.h>
#include "display/bitmap_store.h"
#include "display/bitmaps.h"
static const char *DIR = "/icons";
// Written when the bitmaps were moved (not a valid name, so never listed)
static const char *MIGRATED = "/icons/.migrated";
// Built into the firmware (full screen bitmaps, cut to icons): stored at boot so they can
// be edited, and the fallback when they are not stored
struct BuiltinIcon
{
const char *name;
const uint8_t *bitmap; // PROGMEM, BITMAP_BYTES
};
static const BuiltinIcon BUILTIN[] = {
{"Pikachu", pikachu_clock_bitmap},
{"Pikachu yawning", pikachu_yawning_clock_bitmap},
};
static bool builtinIcon(const char *name, Icon &icon)
{
for (const BuiltinIcon &b : BUILTIN)
if (strcasecmp(b.name, name) == 0)
{
uint8_t bitmap[BITMAP_BYTES];
memcpy_P(bitmap, b.bitmap, BITMAP_BYTES);
iconFromBitmap(bitmap, icon);
return true;
}
return false;
}
bool builtinIconName(const char *name)
{
for (const BuiltinIcon &b : BUILTIN)
if (strcasecmp(b.name, name) == 0)
return true;
return false;
}
static uint32_t revision = 0;
// Counted once (listing the directory is slow), then kept up to date; -1: not yet
static int count = -1;
@@ -37,9 +71,24 @@ static bool ensureDir()
return LittleFS.exists(DIR) || LittleFS.mkdir(DIR);
}
void iconStoreBegin()
{
for (const BuiltinIcon &b : BUILTIN)
{
Icon icon;
if (!bitmapStorageAvailable() || LittleFS.exists(path(b.name)) || !builtinIcon(b.name, icon))
continue;
const char *error = saveIcon(b.name, icon);
Serial.printf("Icon \"%s\" stored from the firmware: %s\n", b.name, error ? error : "ok");
}
}
std::vector<String> listIcons()
{
std::vector<String> names = storedNames(DIR, ".bin");
for (const BuiltinIcon &b : BUILTIN) // not stored (no storage)
if (std::none_of(names.begin(), names.end(), [&b](const String &n) { return strcasecmp(n.c_str(), b.name) == 0; }))
names.push_back(b.name);
std::sort(names.begin(), names.end(),
[](const String &a, const String &b) { return strcasecmp(a.c_str(), b.c_str()) < 0; });
return names;
@@ -59,7 +108,7 @@ bool iconExists(const char *name)
bool loadIcon(const char *name, Icon &icon)
{
if (!bitmapStorageAvailable() || !validBitmapName(name))
return false;
return builtinIcon(name, icon);
auto cached = cache.find(name);
if (cached != cache.end())
{
@@ -88,6 +137,8 @@ bool loadIcon(const char *name, Icon &icon)
file.close();
}
}
if (!ok)
ok = builtinIcon(name, loaded);
if (cache.size() >= CACHE_MAX)
cache.clear();
CachedIcon &entry = cache[name];
+6
View File
@@ -12,6 +12,12 @@
const int MAX_ICONS = 200;
// At boot: stores the icons built into the firmware (the Pikachus of the Clock page) when
// they are not stored, so they can be edited. The built-in ones stay the fallback.
void iconStoreBegin();
// One of them (it always exists)
bool builtinIconName(const char *name);
// Sorted without case
std::vector<String> listIcons();
// Changes each time an icon is saved or deleted
+2
View File
@@ -7,6 +7,7 @@
#include "app.h"
#include "config.h"
#include "display/bitmap_store.h"
#include "display/icon_store.h"
#include "display/page_store.h"
#include "logic/status_led.h"
#include "nano_link/nano_flash_bridge.h"
@@ -34,6 +35,7 @@ void setup()
BootMode boot_mode = firmwareGuardBegin();
bitmapStoreBegin();
iconStoreBegin(); // the Pikachus as icons that can be edited
pageStoreBegin(); // the Clock page as a page that can be edited
appLoadSettings();