Fixes and new apps (#30)

- M5 Unit Modules library + M5 Unit Test app
- Minor fixes for TodoList, TwoEleven, Snake, Brainfuck and Breakout
- Fixed SerialConsole to use the uart controller as it was broken in one of the many updates
- Fixed keyboard input in TwoEleven, Breakout, Magic8Ball and Snake
- Bluetooth Media Keys app, supports pressing physical keys to trigger the corresponding buttonmatrix button
- Epub Reader app
This commit is contained in:
Shadowtrance
2026-06-07 23:56:32 +10:00
committed by GitHub
parent 1f959c8bbf
commit dbf850c434
127 changed files with 11156 additions and 174 deletions
+129
View File
@@ -0,0 +1,129 @@
"""
gen_fonts.py - regenerate all LVGL binary font files for EpubReader.
Prerequisites:
node / npm must be on PATH
npm install -g lv_font_conv
Usage (from any directory):
python Apps/EpubReader/gen_fonts.py
Output: .bin files written to Apps/EpubReader/assets/
Output font filenames are font-family-agnostic: font_<size>pt_<variant>.bin
The app loader expects exactly these names - swap in any 4-weight typeface by
editing the FONTS dict and placing the .ttf files in Apps/EpubReader/fonts/.
Default: Noto Serif (4 weights) https://fonts.google.com/noto/specimen/Noto+Serif
NotoSerif-Medium.ttf → regular
NotoSerif-MediumItalic.ttf → italic
NotoSerif-Bold.ttf → bold
NotoSerif-BoldItalic.ttf → bold italic
"""
import shutil
import subprocess
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
# ── Latin content fonts (4 weights) ───────────────────────────────────────────
FONTS = {
"regular": SCRIPT_DIR / "fonts" / "NotoSerif-Medium.ttf",
"italic": SCRIPT_DIR / "fonts" / "NotoSerif-MediumItalic.ttf",
"bold": SCRIPT_DIR / "fonts" / "NotoSerif-Bold.ttf",
"bold_italic": SCRIPT_DIR / "fonts" / "NotoSerif-BoldItalic.ttf",
}
# ── Unicode ranges ─────────────────────────────────────────────────────────────
# Unicode coverage - adjust if your chosen font supports different blocks:
LATIN_TEXT_RANGES = [
"-r", "0x20-0x7E", # Basic Latin
"-r", "0xA0-0x024F", # Latin-1 Supplement + Latin Extended-A/B
"-r", "0x0370-0x03FF", # Greek (π, Δ, Σ, Ω etc.)
"-r", "0x1E9E", # ẞ (capital sharp-s, German)
"-r", "0x2010-0x2026", # General Punctuation (hyphens, dashes, ellipsis)
"-r", "0x2070-0x209F", # Superscripts and Subscripts (⁰⁴⁵⁶⁷⁸⁹ ₀₁₂₃₄₅₆₇₈₉)
"-r", "0x20A0-0x20CF", # Currency Symbols (€ ₹ ₽ ₩ ₪ ₴ etc.)
"-r", "0x2100-0x214F", # Letterlike Symbols (™ ℃ ℉ etc.)
"-r", "0x2150-0x218F", # Number Forms (⅓ ⅔ ⅛ ⅜ ⅝ ⅞ etc.)
]
# ── Sizes to generate ──────────────────────────────────────────────────────────
SIZES = [16, 20, 28, 36]
# ── Rendering options ──────────────────────────────────────────────────────────
BPP = 2 # 2-bit anti-aliasing; use 4 for smoother rendering (~2× larger)
FORMAT = "bin" # "lvgl" = C struct output; "bin" = binary blob
COMPRESS = False # True = enable RLE compression (smaller binary, slightly slower)
# ── Output directory ───────────────────────────────────────────────────────────
OUT_DIR = SCRIPT_DIR / "assets"
OUT_DIR.mkdir(parents=True, exist_ok=True)
# ── Locate lv_font_conv (handles Windows .cmd npm wrappers) ───────────────────
def _find_tool() -> str:
for name in ("lv_font_conv", "lv_font_conv.cmd"):
found = shutil.which(name)
if found:
return found
print("ERROR: lv_font_conv not found. Install with: npm install -g lv_font_conv", file=sys.stderr)
sys.exit(1)
LV_FONT_CONV = _find_tool()
USE_SHELL = sys.platform == "win32"
def _run(cmd: list) -> bool:
if USE_SHELL:
result = subprocess.run(subprocess.list2cmdline(cmd), capture_output=True, text=True, shell=True)
else:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f" ERROR: {result.stderr.strip()}", file=sys.stderr)
return False
return True
def generate_font(font_path: Path, ranges: list, size: int, symbol: str) -> bool:
"""Convert a single .ttf to an LVGL binary font file."""
if not font_path.exists():
print(f" SKIP {symbol} (font not found: {font_path.name})")
return False
out = OUT_DIR / f"{symbol}.bin"
cmd = [LV_FONT_CONV,
"--font", str(font_path)] + ranges + [
"--size", str(size),
"--bpp", str(BPP),
"--format", FORMAT,
"--lv-font-name", symbol,
"-o", str(out),
]
if not COMPRESS:
cmd.append("--no-compress")
print(f" {symbol:<36} size={size}{font_path.name}")
return _run(cmd)
def main():
print(f"lv_font_conv → {OUT_DIR}\n")
generated = 0
skipped = 0
# ── Content font variants (Latin text + symbols) ───────────────────────────
for size in SIZES:
print(f"── {size}pt {'' * 52}")
for font_key in FONTS.keys():
symbol = f"font_{size}pt_{font_key}"
ok = generate_font(FONTS[font_key], LATIN_TEXT_RANGES, size, symbol)
if ok: generated += 1
else: skipped += 1
print(f"\nDone. {generated} generated, {skipped} skipped.")
if __name__ == "__main__":
main()