Compare commits

..
31 Commits
Author SHA1 Message Date
Kaj SchittecatandClaude Opus 5.5 9b720401f8 release: beta_89 notes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 23:44:50 +02:00
Kaj SchittecatandClaude Opus 5.5 662665e6de touch: beta_89, the UI overhaul
Every screen redesigned to the Oct 2026 mockups, on one design system:
palette, type roles (generated Montserrat faces with Lucide line icons on the
LV_SYMBOL codepoints), a theme apply-cb that styles every LVGL widget, button
kinds, list rows, sheets as action lists, toasts and key-glyph avatars.

Rebuilt: status bar, Chats (filters, + and menu), conversation (route header,
hop meta, "Heard by N repeaters", delivery states, pill composer), lock screen
(mark, clock, 24 h activity, unread cards), Constellation screensaver, Home
launcher and launcher sheet, Status page, grouped Settings home, animated boot
splash, and the message glance as a notification card.

New: optional PIN lock (salted SHA-256, keyboard / touch pad / number pad per
board, backoff that survives reboot), screensaver on battery with its own
duration, T-Deck text sizes, trackball hold to switch Scroll and Cursor (Scroll
default, prefs v69/v70), hold Settings tab for the control panel. M9 bottom
notices removed. Every screen measured for the Large and Huge text sizes.

Fix: adverts over 2/3-byte path hashes never reached the recently heard table
(packed path_len compared as a byte count); ADVERT_PATH replies now carry the
whole multi-byte path.

English placeholders for the 204 untranslated keys in every .lang file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 23:26:41 +02:00
Kaj SchittecatandClaude Opus 5 d6e696d580 release: beta_88 notes
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 13:44:42 +02:00
Kaj SchittecatandClaude Opus 5 a8f813c5f9 Merge PR #599 (oumike): battery saver, Condense Nav, Pager keyboard nav
Closes #591, #592, #593 and the M9 Bluetooth status-bar overlap museifu696
reported. Also the T-Pager battery saver zomgugoff asked for and the status-bar
clock fix.

Review notes, since this landed on top of a day of P4 and M9 work:

* The prefs change is done the right way. condense_nav is appended at the TAIL,
  CURRENT_VERSION goes 65 -> 68 with v66/v67/v68 each described, and the
  trailing static_assert moves to the new last field. v67 and v68 are one-time
  per-board resets with no new field, which is the pattern v63 and v65 used.
* Six conflicts, all in test_touch_prefs_schema.cpp, all the same shape: we had
  both repaired the same broken sums. His version is a superset (it adds
  condense_nav on top of the report_ping/report_done_n I added), so his wins.
  Verified afterwards that both sides survived rather than trusting the merge:
  the channel-send guard, the P4 tile-FS fix, statusPctOverflow, m9NoticeSet,
  m9DiscoveredUnseen and TDP4_C6_ID are all still present, as are his kbd_nav
  and Condense Nav work. Host suite 17 pass / 0 fail at v68.

Also here: PUBLISH the translations the PR added.

It added five strings to all fourteen language files with real translations, but
left "# ver: 22" alone and did not touch langs.json. Devices fetch lang/<ver>/,
so the version is the only thing that makes one re-download: without a bump the
work sits in the repo and reaches nobody, and every non-English device shows
those strings in English. The baked-in table was regenerated, so compiled-in
languages were fine -- it is the downloaded ones that would have silently
stayed behind. Bumped to 23, snapshotted into lang/23/, and langs.json points
at it. deploy-apps.sh validates: 14 languages and 14 apps, every referenced
file present.

That store still has to be pushed with the release; editing deploy/apps/ is not
publishing it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 12:30:55 +02:00
Kaj 127ece5b88 Merge pull request #590 from jcyrio/fix/layout-switch-hint-physical-keyboards
touch: the Secondary keyboards hint named a key the Pager does not have
2026-10-05 12:24:25 +02:00
Kaj 02454f8142 Merge pull request #587 from jcyrio/fix/keep-cleared-dm-in-inbox
touch: "Delete history" on a DM no longer makes the chat vanish from Chats
2026-10-05 12:24:12 +02:00
Kaj SchittecatandClaude Opus 5 820bc21f46 build: verify the P4 artifacts, and make the host tests runnable
The T-Display P4 ships EIGHT images: panel (AMOLED/LCD) x radio (SX1262/LR2021)
x C6 stack (esp-hosted/ESP-AT). The C6 axis is new and it is the dangerous one,
because the two builds are identical apart from the artifact name they carry,
and flashing the wrong one at an older unit used to boot-loop the board.

scripts/build/p4-release.sh builds all eight and VERIFIES each image against the
OTA name baked into it before accepting it. That is the same string the device
builds its self-update URL from, so a disagreement with the filename is exactly
the failure that bricks people -- the T-Deck Max self-updating to Pro firmware,
and this table has been wrong three times. A mismatch aborts the release.

It earned that check immediately: building the eight by hand produced two
mislabelled images, and every mechanism meant to prevent it was itself broken.

  * build.sh fullclean has NEVER worked from a plain shell. It ran `cmake -E
    remove_directory` above the esp-idf/export.sh that puts cmake on PATH, so it
    printed that it had removed the tree, exited 0, and removed nothing. Now a
    plain rm -rf, and it verifies the directory is gone instead of assuming.
    This matters beyond tidiness: the hosted and ESP-AT builds SHARE a build dir
    (the C6 axis does not change P4_BUILD_TARGET), the backend is read from the
    environment at CMake CONFIGURE time, and CMake does not reconfigure because
    an env var changed -- so a clean that silently did nothing yields an
    incremental build of the OTHER variant, with no configure output to show for
    it. That is precisely how the two mislabelled images were produced.
  * Deleting CMakeCache.txt alone is not a clean either; it leaves CMakeFiles/
    behind and the next configure dies in CMakeDetermineCCompiler.
  * Detecting failure by grepping a log for "error:" misses a failed CMake
    configure, which prints "Configuring incomplete" and no such line -- and the
    previous application.bin is still sitting in the directory to be picked up.
    Exit codes now.

gen-flasher-meta.py gains the four ESP-AT entries, labelled "older unit, ESP-AT
C6": someone choosing in the web flasher has no serial log and needs to pick on
something they can check.

scripts/run-host-tests.sh runs test/ as one command. Nothing ever did, which is
why test_touch_prefs_schema had been failing to COMPILE since v64 appended
report_ping and report_done_n without updating its sums -- and a static_assert
that does not compile is not a guard. That one exists to catch the mid-struct
insert that broke v44 and shipped the poisoned beta_57 layout. Repaired, with a
note where the next person appending a field will read it. 17 pass, 0 fail, 2
skipped with their reasons stated rather than quietly dropped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 11:31:36 +02:00
Kaj SchittecatandClaude Opus 5 88e36e9499 touch: a private channel send must never fall out onto public (#589)
Three fixes.

#589/#588, and this one is a disclosure rather than a glitch. TippyLion28 had a
channel go invalid (#588), then sent to it from the web UI; the messages went
out on the PUBLIC channel with nothing on screen to say so. The web path did
resolve the channel by name at TX, with a comment citing the stale-slot trap --
and then threw that away:

    if (slot < 0) slot = threadMeshChannelSlot(tidx);   // fall back to cached

A name that does not resolve means the channel is gone. The cached slot then
points at some OTHER still-valid channel, in practice slot 0, public. It now
refuses and says so, which is what the touch composer already did ("Channel not
found"); the web UI had no business being the laxer path.

termDoSend() also only checked that the slot resolved to A channel, never that
it was the RIGHT one, so a stale slot sailed through it. Every caller already
passes the channel the user chose, so it compares now -- enforced at the one
point every sender passes through, not only in the callers.

#583, T-LoRa Pager losing ~30 min/day while powered. jcyrio read the source and
called it exactly: HardwareRtcClock copies the chip into the ESP32 clock once at
boot and getCurrentTime() never touches I2C again, so the software clock
free-runs at the RC oscillator's ~2% while an accurate crystal sits unread on
the bus. Keeping I2C out of the hot path is right -- it runs on every message,
UI tick and advert -- but once an hour is not that path. tick() now re-reads the
chip hourly and pulls the software clock back. Only when a chip was adopted at
boot (a read refused at boot is not a trustworthy source and must not drag a
good clock backwards), only past a 2 s threshold, and it corrects the software
clock alone: calling setCurrentTime() would write the chip's own value back to
itself and a read-back failure would then mark a healthy retention path broken.

P4 tile cache on a board with no SD card. The fallback set s_tile_fs = &FFat,
but main.cpp mounts that partition with LittleFS -- 'storage' was converted from
FAT and this line was not updated with it. The tile cache was handed an FS
object nothing had mounted, so every tile failed with "File system is not
mounted" as the only clue. Uses the filesystem that is actually mounted.

Compile-checked on T-Deck, T-LoRa Pager and ThinkNode M9.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 11:31:16 +02:00
Kaj SchittecatandClaude Opus 5 b52c71f4a5 p4: stop retrying a C6 that is never coming back
7ce1b3f turned off CONFIG_ESP_HOSTED_TRANSPORT_RESTART_ON_FAILURE so a wrong-
firmware C6 could not reboot the board. It stopped the boot loop and I called
it fixed on a 45 s capture. That was the wrong test. Disabling the restart did
not stop esp-hosted, it only removed its exit: sdio_read_task posts
ESP_HOSTED_EVENT_TRANSPORT_FAILURE and then `continue`s straight back into a
register read that is going to fail again. Measured on the same board:

  731 failed register reads in 45 s, ~2.2 KB/s of log, forever

A boot loop and an unbounded retry storm are the same mistake: an unreachable
co-processor is a PERMANENT condition and the driver treats it as transient.

* The rx task now parks after the failure event is posted (build-time patch to
  the esp-hosted fork, same idempotent mechanism build.sh already uses for
  libsodium and the SD_MMC pull-ups; it fails loudly if upstream moves that
  code rather than silently not applying). No reboot, no retry: Wi-Fi and BLE
  are unavailable and everything else carries on.

* hostedInitBLE() no longer runs unconditionally. It is a full esp-hosted
  handshake and it ran on every boot even though the banner advertises lazy
  initialisation -- so on a mismatched unit it started a transport that can
  never come up, for a radio the user may have had switched off. It now
  honours the same stored intent beginBle() already checked.

* The keyboard hot-plug probe backs off from 1.5 s to 15 s once the expansion
  has been absent a few times, and returns to the fast rate the moment one
  succeeds. The probe is a real I2C transaction and on an empty bus Arduino's
  Wire layer logs two [E] lines per attempt: a permanent drip down the USB-CDC
  the companion app talks over, for an accessory most people do not own.

Measured on an ESP-AT unit running the HOSTED image, 150 s captures:

                      shipped   7ce1b3f only   this
  resets                 5           0           0
  host restarts         yes         yes          0
  failed reg reads       -        731/45s        1
  steady log rate        -        2.2 KB/s    ~14 B/s
  UI                   never      appears     stays up

The remaining ~14 B/s is the backed-off keyboard probe on a board with no
expansion attached, which is the expected floor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 10:26:17 +02:00
Kaj SchittecatandClaude Opus 5 7ce1b3f48c p4: an unreachable C6 must not take the device down with it
beta_86 flipped the T-Display P4's default C6 backend to esp-hosted. Current
V1 units ship the C6 with esp-hosted firmware and are fine; OLDER units still
carry the factory ESP-AT firmware, and on those the SDIO stream never frames.
That turned into a board that will not boot at all, reported by three people
so far (#600 jalevdev, myshoeisonfire on Discord, and Kaj's own unit), each
with the same signature:

  H_SDIO_DRV: len[25970]>max[1524] OR offset[25697] != exp[12], Drop

Two separate things made a co-processor mismatch fatal, and both had to go.

1. The UI was behind the C6. hostedInitBLE() ran unconditionally at setup, a
   hosted handshake, with ui_task.begin() ~80 lines later. The LEGACY backend
   always started its worker AFTER the UI, which is exactly why a dead C6 used
   to cost Wi-Fi and nothing else; the hosted path was wired in ahead of it.
   Both backends now start at the end of setup, after the UI, with a note
   saying nothing C6-dependent may move above it.

2. esp-hosted resets the host. CONFIG_ESP_HOSTED_HOST_RESTART_NO_COMMUNICATION_
   WITH_SLAVE=n covers one path; the one that actually fires is
   H_TRANSPORT_RESTART_ON_FAILURE in sdio_drv.c, about 10 s after the connect
   attempt, and it is on by default. Now off. The failure still arrives as
   ESP_HOSTED_EVENT_TRANSPORT_FAILURE, which is what an application should act
   on instead of the transport rebooting the board underneath it.

Measured on an ESP-AT unit, 45 s captures, same hardware:

  as shipped       5 resets, host restarts, UI never appears
  reorder only     2 resets, host restarts, UI appears then dies
  reorder + config 0 resets, 0 restarts, UI stays up

The C6 is still unreachable on that unit, as it must be - it is the wrong
firmware for it. It now costs Wi-Fi and BLE and nothing else.

Also here, both needed before this can ship:

* The ESP-AT build gets its own artifact name (tdisplay-p4-at, and the matching
  -lcd/-lr2021 combinations). Both backends used to answer "tdisplay-p4", so an
  ESP-AT unit's self-update would have fetched the hosted bin and put itself
  straight back into the loop, recoverable only over USB. That is the T-Deck
  Max bug again (a Max self-updating to Pro firmware) and would have been the
  fourth time this table shipped wrong.

* The keyboard expansion probe prints a one-shot bus scan. "It does not work"
  has three causes that are indistinguishable from outside: not clipped on,
  clipped on but unpowered, or present and failing bring-up. An unpowered board
  does not NACK cleanly either - its pull-ups are dead, the lines sit low
  through its ESD diodes, and every transaction returns ESP_ERR_INVALID_STATE,
  which reads like a driver fault. The scan says which it is in one line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 10:03:19 +02:00
Michael A. Cojocari 535f69c2ee Merge branch 'feat/web-transfer-backup-restore' into general-tweaks
# Conflicts:
#	src/helpers/esp32/WebFileTransferPage.h
2026-10-04 20:05:39 -04:00
Michael A. CojocariandCopilot 6c85948002 fix(boot): boot logo follows the saved orientation
The logo is painted before SPIFFS/SD mount, so it read rotation from the
legacy NVS settings copy, not the real file-backed settings. Any device
whose two copies disagreed (the Attaky after its v68 landscape reset, or
anyone who changed orientation since settings moved to files) showed the
logo in the old orientation until the UI took over.

Keep a raw-NVS boot copy of the rotation (touch/boot_rot): written by
touchPrefsSetUiRotation() and re-synced after touchPrefsReload() every
boot, so schema resets and restored backups catch up too. The logo reads
it, falling back to the old read when absent (first boot after update,
or Launcher installs without NVS). On the Attaky any landscape value is
treated as rotation 1, as UI init does.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-04 19:34:01 -04:00
Michael A. CojocariandCopilot 3a9b3ce055 File Transfer page: browser Backup and Restore buttons
Backup runs the same export as Settings > Backups > Export into a hidden
temp file and streams it to the browser as meshcore-<date>.json.
Restore uploads a backup .json through the existing chunked/CRC path,
then applies it via the shared backupImportAndReboot() helper (same
sections/replace flags as Import backup) and reboots. Temp files holding
key material are removed after use and on page open.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-04 19:25:58 -04:00
Michael A. CojocariandCopilot fe8fdd33cf fix(attaky): turn the panel back to portrait at UI init
Portrait came up as a shrunken landscape screen. The boot logo takes its
rotation from the settings copy in legacy NVS, read before main.cpp moves
settings to SPIFFS. On the Attaky that copy says landscape (the new
default, and the v68 reset), so the logo turned the panel to rotation 1.
The real setting, loaded from SPIFFS afterwards, said portrait: LVGL was
built at 240x320, but nothing turned the panel back, so the UI was drawn
upright for landscape, 240 px wide, with its bottom cut off.

UI init now turns the panel to the build portrait rotation
(DISPLAY_ROTATION, 2) when the UI is portrait, as the CrowPanel already
does for the same reason. Portrait there needs no touch transform, as on
the V4: the Attaky's landscape touch map is the V4's, so its touch frame
is too. The D-pad remap from the orientation commit follows from the
same map.

The boot logo still follows the NVS copy, so in portrait it shows in
landscape for a moment before the UI takes over.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-04 17:29:00 -04:00
Michael A. CojocariandClaude Opus 5.5 01346777ed feat(attaky): File Transfer on the Mesh Deck, to internal storage
File Transfer needed a removable card, and the Mesh Deck has no card
slot. Give it an internal backend (WADA_WEB_FILE_TRANSFER_INTERNAL):

- Files go to /transfer on the internal LittleFS "tiles" partition, never
  SPIFFS, which holds the settings, contacts and chats.
- The page shows "Internal storage  used / total". An upload that would
  leave less than the map tile cache's 320 KB floor is refused up front
  ("not enough free internal storage") instead of failing part-way.
- Map tile folders are refused ("map tiles need an SD card"): the map
  only reads uploaded tile libraries from a card.
- If the partition did not mount, opening the app says so instead of
  asking for an SD card.
- The browser page now says files go to "the device's storage" rather
  than "the device's SD card", which is right on every board.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019M5VJ43X3nUULPqVcgrAH2
2026-10-04 15:47:12 -04:00
Michael A. CojocariandClaude Opus 5.5 32d6c0d56d feat(attaky): Orientation setting, so the Mesh Deck can run in portrait
The Mesh Deck UI was pinned to landscape (LV_DISP_ROT_90), and the
build without a keyboard is held upright, so it needs portrait too.

- CAP_ROTATABLE on the Attaky: Settings > Display > Orientation toggles
  landscape (ROT_90, panel rotation 1) and portrait (the panel's
  DISPLAY_ROTATION=2, no touch transform, as on the V4).
- Landscape stays the default: DEFAULT_UI_ROTATION is 1 on this board,
  and schema v68 (no new field) resets the stored value once, since the
  UI ignored it until now. The pre-LVGL boot logo already follows the
  setting, so it is upright in both.
- The front D-pad is named for landscape, so in portrait a press turns
  with the screen (right -> up, down -> right, left -> down, up -> left).
- The terminal (Console Mode) keeps the panel's build orientation,
  whatever the UI orientation is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019M5VJ43X3nUULPqVcgrAH2
2026-10-04 15:47:12 -04:00
Michael A. CojocariandClaude Opus 5.5 6d655b8301 feat(pager): keyboard navigation and remappable tab keys, as on the T-Deck (#591)
The wheel is the Pager's only way to move the highlight, which is slow
and imprecise. Bring the T-Deck's keyboard navigation to it:

- With no text field focused, programmable letters move the highlight
  (default I up, K down, J left, L right) and scroll (U / O). Select
  and Back default to no letter, since Enter and Backspace-hold (the
  wheel's click / long-press) already do them. On the Map the letters
  move the highlight; W/A/X/D stay the map pan.
- The tab keys are programmable like the T-Deck's, defaulting to the
  Pager's M/C/H/A/S, and the bar's labels show whatever is bound.
- Settings > Keyboard gets the same "Keyboard navigation" switch (on by
  default here) and the same tap-a-row-then-press-a-key remap rows; the
  T-Deck's rows move into settingsNavKeyRows() so both boards share them.
  The Pager's switch only turns the letters on or off; it never empties
  the focus group, which the wheel always needs.
- A remap refuses letters the Pager uses for fixed actions (Q/E slider
  nudge, W/A/X/D map pan; A stays allowed as a tab key).
- In wheel edit mode on a slider, J/L preview like a turn and the
  select key closes the edit, matching the wheel.

Settings: the Pager's kbd_nav and key bindings were never shown on that
board, so they held the T-Deck defaults. Schema v67 (no new field) puts
the Pager defaults in once on that board only. Two new strings,
translated in every deploy/apps/lang file; i18n_builtin.h regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019M5VJ43X3nUULPqVcgrAH2
2026-10-04 15:06:40 -04:00
Michael A. Cojocari 8adea3558e Merge branch 'fix/594-statusbar-clock-overlap' into fix/touch-ui-followups-2 2026-10-04 14:56:28 -04:00
Michael A. CojocariandClaude Opus 5.5 d7c3844ef2 fix(ui): keep the status-bar clock off the Bluetooth icon (#594)
On the M9, V4-R8 and Pager the text presets grow the status-bar offset
ladder with the font (SBX), and the builder places the Bluetooth glyph
with it. The clock placement then re-aligned the clock with plain SC(),
which leaves it at its 100% spot, so at Large / Huge the Bluetooth glyph
moved left onto the time. The charging slide also used raw 100% offsets,
so plugging in or unplugging snapped the cluster back under the grown
glyphs.

- The wide-bar clock uses SBX() like the icons it sits beside.
- The charging slide uses exactly the builder's offsets (SBX plus the
  battery-% overflow); the narrow V4 portrait bar keeps its raw values.
- Safety net for every board: after placing the clock, if it overlaps a
  visible icon on its row (Bluetooth, DND, sleep, Wi-Fi, signal,
  battery), move it just left of the leftmost one. Covers the 12-hour
  clock, the Bluetooth-keyboard glyph and the centred hide-name clock.
  Re-checked only when something that moves or resizes them changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019M5VJ43X3nUULPqVcgrAH2
2026-10-04 14:56:28 -04:00
Michael A. CojocariandClaude Opus 5.5 08750ab3a1 feat(ui): Condense Nav, a landscape nav rail with icon-only Home launchers (#592)
On short landscape panels (the T-Pager is 480x222) the bottom nav bar
spends a full-width strip on five icons. New Settings > Display switch
"Condense Nav", off by default, applies after a restart:

- The nav bar becomes a rail on the right edge (LV_DIR_RIGHT tabview).
  Pager cells show the key letter under the icon; the Tanmatsu keeps its
  coloured F-key shapes; the T-Deck / P4 key hints sit under the icons.
- Home's launcher column (Advert, Terminal, Files, Apps, Control) shows
  icons only; the buttons keep their height and spacing, and the status
  text, chart and info card widen into the freed space.
- The active-tab marker turns vertical beside the active cell, the
  unread and update badges move to the top-right of their cells, and the
  rail stays solid (and the marker visible) on the Map.
- Content that kept the bottom bar visible now stops at the rail
  instead: tabContentW() subtracts the rail, and the map canvas, Settings
  landing grid, chat-list rows, Mentions and the app drawer use it.
  Swipe-left on the rail opens the drawer (swipe-up on the bottom bar).

Portrait layouts and the M9 are unchanged: the mode is decided once in
buildUiTree from the pref and the orientation at boot, the switch is
greyed out in portrait and absent on the M9.

TABBAR_H becomes a runtime value (0 with the rail) next to a new
TABBAR_W. The pref is a new trailing field, condense_nav (schema v66);
the schema test's layout asserts now count the v64 fields they missed.
Strings added to every deploy/apps/lang file and i18n_builtin.h
regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019M5VJ43X3nUULPqVcgrAH2
2026-10-04 13:47:46 -04:00
Michael A. CojocariandClaude Opus 5.5 567c6599e5 fix(pager): click the wheel on a slider to adjust it with the wheel
A plain wheel turn moves focus, and a click sends Enter, which a slider
ignores, so no slider on the Pager (Screen timeout, Brightness, the
Control Center sliders, Map zoom) could be adjusted with the wheel; only
the hidden Q/E keys moved them.

Clicking the wheel (or Enter) on a focused slider now enters an edit
mode, shown with a bigger contrasting knob. Turns move the value with a
live preview (VALUE_CHANGED) and save nothing. Clicking again saves
(RELEASED, the same commit a touch drag ends with) and returns the wheel
to focus navigation. Back (long-press or Backspace-hold) restores the
original value without saving, as does the screen timing out or focus
leaving the slider. Q/E preview instead of saving while in edit mode.

The slider step moves out of navMoveDir() into navSliderNudge() so the
wheel and Q/E share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019M5VJ43X3nUULPqVcgrAH2
2026-10-04 13:35:42 -04:00
Michael A. CojocariandClaude Opus 5.5 988498b267 feat(ui): battery saver switch on every touch board (#593)
The idle power-saver (touchSleep) already ran on every touch build: the
hooks are installed in UITask::begin and main.cpp calls loopEnd() every
tick, and its gates are board-neutral. Only the Settings switch, its
callback and the amber battery tint were behind a T-Deck / V4-R8 / M9
allowlist, so on every other board (T-Pager, CrowPanel, T-Display P4,
T-Deck Pro, ...) the pref defaulted OFF with no way to turn it on --
the same bug the M9 had until 2026-09-02.

Drop the allowlist from the switch row, sleepIdleToggleCb, applyBattColor
and the status-bar re-tint. Also rejoin the lockOnScreenOffToggleCb
comment the old #if had split in two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019M5VJ43X3nUULPqVcgrAH2
2026-10-04 13:18:52 -04:00
Kaj SchittecatandClaude Opus 5 66e1f37d2c harness: model the filesystem, and stop apps running other apps' tests
Two things made a full harness run untrustworthy, and together they produced a
false accusation against a community app.

wada.fs was stubs. append threw the data away, remove always said true, and
read, write and list were not there at all — although the firmware registers
all five (LuaAppHost.cpp). Any app that writes a file and reads it back
therefore failed here while working perfectly on device. wardrive 1.2 is
exactly that app: it errors on a nil fs.read at its first log flush, long
before it renders anything, and the emoji scenario then reported the repeater
name as unrendered. Read as "1.2 regressed the beta_70 emoji fix", which it
did not. It passes untouched once fs is real.

wada.fs is now an in-memory filesystem following LuaAppHost's semantics: read
returns the data plus the file's TOTAL size so a caller can window a growing
log, an offset at or past the end gives an empty string rather than nil, a
missing file gives nil, and list is sorted so a hash order cannot make a test
flaky.

The second: the gpscompass scenario list was acting as the dispatch default,
so an app with no branch of its own was run against tests written for a
different app. Six store apps have no branch (2048, airtime, monitor, nearby,
sdktest, snake), so a full sweep came back with six red apps as its normal
state — and a real regression would not have stood out against that. Those
apps now say they have no scenarios and run none. They are untested, which is
worth saying out loud, but that is not the same as broken.

A sweep of every store app is now 8 apps passing their own scenarios, 6
reported untested, 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-04 12:42:12 +02:00
Kaj SchittecatandClaude Opus 5 ace73911ad touch(m9): bottom notices carry a count and stop flashing
The M9 is the one board with no bottom bar (TABBAR_H == 0, tab_btns hidden
outright), so it never got the numeric badges the others have. beta_75 put
three glyph labels on layer_top across the bottom instead, OVER page content,
and blinked two of them at 1 Hz. With nothing else down there to read them
against, that came back reported as the navigation icons flashing.

They now carry a count and are shown only while they mean something:

* The person glyph was driven by discoveredCount(), a standing total that is
  non-zero on any live mesh from the first advert heard and only ever falls
  when you add or purge. It therefore blinked forever. It now counts what has
  not been looked at: watermarked at boot, because a ring restored from flash
  is not news, and again whenever the Discovered list is opened, with a clamp
  so purge, adding to Contacts and ring eviction cannot leave the next real
  arrival reading as nothing new.
* It also blinked while you were standing on Contacts with that list open.
  Suppressed there, the way the mail glyph already was on the inbox tab.
* Two style writes and a z-reorder ran on every 250 ms tick regardless of
  whether anything was pending, invalidating the strip for nothing.
  m9NoticeSet() only touches LVGL on a real change, and takes a refront flag
  so a live notice still lifts over the app drawer, which is the one overlay
  notices are allowed to sit on top of.

Also: "100%" was clipped by the signal bars. The percentage is right-anchored
at SBX(22) and grows left, the bars are pinned at SBX(54), so the text gets
SBX(32) and no more; "100%" is a whole glyph wider than "99%", and the bars
are created after the label so they paint over the leading 1. Reported by
buko84 and confirmed by jd1227, both on the M9, which is a board whose text
presets grow the font and leave the geometry at 100%, so it runs out of room
first. statusPctOverflow() measures the real width and slides the bars, the
Wi-Fi glyph and the SD dot left by the overflow, capped, and gated to those
font-only-preset boards. Zero where the text already fits, so no board moves
that did not have to, and the clock, the DND moon and the BLE glyph never
move. The round panel already solved this with statusClusterStart(); this is
the same idea for the single-row ladder.

Built for the M9, the V4-R8, the Pager and the T-Deck. The T-Deck binary is
byte-identical, which is the check that the ladder change reaches nothing
outside its gate. Not run on hardware: needs an M9 tester.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-04 12:36:53 +02:00
jcyrioandClaude Fable 5.1 5249901555 touch: the Secondary keyboards hint named a key the Pager does not have
Switching on a secondary layout changes nothing until you cycle to it with
double-tap SPACE, and the sentence under "Secondary keyboards" is where that
is explained. It was gated on HAS_TDECK_KEYBOARD || HAS_M9_KEYBOARD, so the
T-LoRa Pager, T-Deck Pro and T-Deck Max (HAS_PAGER_KEYBOARD) fell through to
the on-screen wording and told the user to tap an "EN" key their physical
keyboard does not show, while their keys go through the same handleHwKey()
double-space path as the T-Deck. The setting read as broken.

Gate the hint on CAP_KEYBOARD instead, which is exactly the set of boards
whose keys reach that handler (the Attaky module keyboard has its own loop
and really does type next to the on-screen EN key, so it keeps the touch
wording). And make the "on" toast say how to switch, since "Russian: on"
alone was the moment the user was left guessing; "off" keeps the short form.
The cycle itself is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: jcyrio <50239349+jcyrio@users.noreply.github.com>
2026-10-03 14:13:56 -07:00
Kaj SchittecatandClaude Opus 5 1348910415 release: beta_87 notes say what happened and what fixed it
Dropped the fault framing. The notes describe the mechanism and the change,
not whose code it was.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 19:49:22 +02:00
Kaj SchittecatandClaude Opus 5 2576b7e369 release: beta_87 notes
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 19:42:58 +02:00
Kaj SchittecatandClaude Opus 5 7e1f5af904 lua: wada.ui.emoji(cb) for apps that draw their own text entry
beta_86 put the emoji picker on the long-press menu, which covers the firmware's
own field and so covers wada.ui.input(). It does not cover an app that builds its
own buffer from key events, because there is no textarea to long-press -- and
that is exactly the case Jade asked about: their composer shows a pad-bytes
counter the firmware's field cannot, so it takes the custom path and the menu
never applies to it.

wada.ui.emoji(cb) opens the picker and hands the glyph back as UTF-8, nil if
dismissed. Same discipline as wada.ui.input's callback: one ref, one call,
released either way, refused if one is already open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 19:41:51 +02:00
Kaj SchittecatandClaude Opus 5 defbd1d478 lua: wada.ui.clear() leaked a closure per button and per list row
Reported by Jade as "screen memory errors, worse the longer the app has been
open", with an app that rebuilds its screens on a 120 ms debounce.

Button and list-row callbacks live in one registry table keyed by the LVGL
object POINTER. clear() calls lv_obj_clean(), which frees every one of those
objects, and bumps s_ui_gen so stale handles go inert, but it never touched the
table. So each rebuild added an entry and kept the Lua closure, with its
upvalues, alive forever. Unbounded, and worst for exactly the apps that are
built properly: a list of twenty contacts redrawn on a timer leaks twenty
closures a go.

It was also a correctness bug in waiting. LVGL reuses freed addresses, so a new
button created WITHOUT a callback could land on an address a dead one left
behind and fire that stale closure on the next tap.

clear() now replaces the table. Every entry in it describes an object
lv_obj_clean just destroyed, so there is nothing there worth keeping.

Found by reading the host after Jade asked what the memory budget was; the
budget was never the problem.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 19:33:33 +02:00
Kaj SchittecatandClaude Opus 5 890a250a02 release: cumulative stable notes for beta_85 (beta_84..85)
Curated from the two beta notes. Leads with the two things a user must know
before this reaches their device: do not switch a T-Display P4 to landscape on
this build (fixed in beta_86), and a T-Deck Max on beta_84 must be updated over
USB rather than from the device.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 19:14:07 +02:00
jcyrioandClaude Fable 5.1 3eb3fa8c71 touch: "Delete history" on a DM no longer makes the chat vanish from Chats
The confirm says "The chat itself stays", and beta_38's notes say the
contact stays, but on a DM the row disappeared: the inbox lists a DM only
while threadHasMessageHistory() is true, and refreshThreadsFromMesh() (every
4 s) reclaims any DM slot with no history, so the emptied thread was hidden
and then freed. Channels were unaffected because the inbox lists them
regardless.

The thread record had nothing to tell an emptied chat from a contact never
chatted with, so this adds keep_when_empty: set by clearThreadHistory() on
a DM, honoured by getCombinedInboxCount(), getUnreadTotal() and the reclaim
pass, cleared when the slot is reused, removed with the thread by "Delete
chat". It is appended to UiHistoryThread; thread_rec_size already lets older
records read it back as 0 and older builds skip it, so the history version
is not bumped (a bump would make a downgrade quarantine both history files).

Only threads the user explicitly emptied are pinned, so contacts without a
conversation still stay out of the inbox and clearThreadHistory() still
removes every message.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: jcyrio <50239349+jcyrio@users.noreply.github.com>
2026-10-03 09:34:47 -07:00
91 changed files with 70271 additions and 2977 deletions

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: bg
# ver: 22
# ver: 23
# name: Български
# base: bg
(device behind) (устройството изостава)
@@ -34,9 +34,14 @@ Accent popups: off Изскачащи акценти: изкл
Accent popups: on Изскачащи акценти: вкл
Accent colour Акцентен цвят
Appearance Изглед
Condense Nav (restart to apply) Компактна навигация (рестарт за прилагане)
Condense Nav saved — restart to apply Компактната навигация е запазена — рестарт за прилагане
Day Ден
High contrast Висок контраст
High contrast - restarting to apply it… Висок контраст - рестартиране за прилагане…
Keyboard nav: on Клавиатурна навиг.: вкл.
Landscape only Само в хоризонтална ориентация
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Буквите местят маркера, когато няма активно текстово поле. Enter избира; задръжте Backspace за назад.
Night Нощ
Theme save failed Неуспешно запазване на темата
Ack Ack
@@ -1282,3 +1287,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+205 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: cs
# ver: 22
# ver: 23
# name: Čeština
# base: cs
(device behind) (zařízení za)
@@ -34,7 +34,12 @@ Accent popups: off Vyskakovací okna akcentu: vypnuto
Accent popups: on Vyskakovací okna akcentu: zapnuto
Accent colour Barva akcentu
Appearance Vzhled
Condense Nav (restart to apply) Kompaktní navigace (pro použití restartujte)
Condense Nav saved — restart to apply Kompaktní navigace uložena — pro použití restartujte
Day Den
Keyboard nav: on Navigace klávesnicí: zapnuta
Landscape only Pouze na šířku
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Písmena posouvají zvýraznění, když není aktivní textové pole. Enter vybírá; podržením Backspace se vrátíte zpět.
Night Noc
Theme save failed Uložení motivu selhalo
Ack Potvrzení
@@ -1304,3 +1309,202 @@ Snake Snake
held by held by
© OpenStreetMap © OpenStreetMap
© OpenTopoMap © OpenTopoMap
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: de
# ver: 22
# ver: 23
# name: Deutsch
# base: de
(device behind) (Gerät geht nach)
@@ -34,9 +34,14 @@ Accent popups: off Akzent-Popups: aus
Accent popups: on Akzent-Popups: ein
Accent colour Akzentfarbe
Appearance Erscheinungsbild
Condense Nav (restart to apply) Kompakte Navigation (Neustart nötig)
Condense Nav saved — restart to apply Kompakte Navigation gespeichert — Neustart zum Anwenden
Day Tag
High contrast Hoher Kontrast
High contrast - restarting to apply it… Hoher Kontrast - Neustart zum Anwenden…
Keyboard nav: on Tastaturnav.: an
Landscape only Nur im Querformat
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Buchstaben bewegen die Markierung, wenn kein Textfeld aktiv ist. Enter wählt aus; Rücktaste halten geht zurück.
Night Nacht
Theme save failed Design konnte nicht gespeichert werden
Ack Ack
@@ -1278,3 +1283,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: el
# ver: 22
# ver: 23
# name: Ελληνικά
# base: el
(device behind) (συσκευή πίσω)
@@ -34,9 +34,14 @@ Accent popups: off Αναδυόμενα τόνων: όχι
Accent popups: on Αναδυόμενα τόνων: ναι
Accent colour Χρώμα έμφασης
Appearance Εμφάνιση
Condense Nav (restart to apply) Συμπαγής πλοήγηση (επανεκκίνηση για εφαρμογή)
Condense Nav saved — restart to apply Η συμπαγής πλοήγηση αποθηκεύτηκε — επανεκκίνηση για εφαρμογή
Day Ημέρα
High contrast Υψηλή αντίθεση
High contrast - restarting to apply it… Υψηλή αντίθεση - επανεκκίνηση για εφαρμογή…
Keyboard nav: on Πλοήγηση πληκτρ.: ανοιχτό
Landscape only Μόνο σε οριζόντιο προσανατολισμό
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Τα γράμματα μετακινούν την επισήμανση όταν δεν είναι ενεργό πεδίο κειμένου. Το Enter επιλέγει· κρατήστε το Backspace για επιστροφή.
Night Νύχτα
Theme save failed Αποτυχία αποθήκευσης θέματος
Ack Ack
@@ -1282,3 +1287,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: es
# ver: 22
# ver: 23
# name: Español
# base: es
(device behind) (dispositivo atrasado)
@@ -34,9 +34,14 @@ Accent popups: off Ventanas de acentos: desactivado
Accent popups: on Ventanas de acentos: activado
Accent colour Color de acento
Appearance Apariencia
Condense Nav (restart to apply) Navegación compacta (reiniciar para aplicar)
Condense Nav saved — restart to apply Navegación compacta guardada — reinicia para aplicar
Day Día
High contrast Alto contraste
High contrast - restarting to apply it… Alto contraste - reiniciando para aplicarlo…
Keyboard nav: on Nav. teclado: activ.
Landscape only Solo en horizontal
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Las letras mueven el resaltado cuando no hay un campo de texto activo. Intro selecciona; mantén Retroceso para volver.
Night Noche
Theme save failed No se pudo guardar el tema
Ack Ack
@@ -1282,3 +1287,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: fr
# ver: 22
# ver: 23
# name: Français
# base: fr
(device behind) (appareil en retard)
@@ -34,9 +34,14 @@ Accent popups: off Fenêtres d'accents : désactivé
Accent popups: on Fenêtres d'accents : activé
Accent colour Couleur d'accent
Appearance Apparence
Condense Nav (restart to apply) Navigation compacte (redémarrer pour appliquer)
Condense Nav saved — restart to apply Navigation compacte enregistrée — redémarrez pour appliquer
Day Jour
High contrast Contraste élevé
High contrast - restarting to apply it… Contraste élevé - redémarrage pour l'appliquer…
Keyboard nav: on Nav. clavier : activé
Landscape only Paysage uniquement
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Les lettres déplacent la sélection quand aucun champ de texte n'est actif. Entrée sélectionne ; maintenez Retour arrière pour revenir.
Night Nuit
Theme save failed Échec de l’enregistrement du thème
Ack Accusé
@@ -1281,3 +1286,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+206 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: hu
# ver: 22
# ver: 23
# name: Magyar
# base: hu
(device behind) (készülék hátulján)
@@ -34,9 +34,14 @@ Accent popups: off Accent popups: kikapcsolva
Accent popups: on Accent popups: bekapcsolva
Accent colour Kiemelőszín
Appearance Megjelenés
Condense Nav (restart to apply) Tömör navigáció (újraindítás az alkalmazáshoz)
Condense Nav saved — restart to apply Tömör navigáció mentve – indítsa újra az alkalmazáshoz
Day Nappali
High contrast Nagy kontraszt
High contrast - restarting to apply it… Nagy kontraszt - újraindítás az alkalmazáshoz…
Keyboard nav: on Billentyűzetnavigáció: bekapcsolva
Landscape only Csak fekvő tájolásban
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. A betűk mozgatják a kijelölést, ha nincs aktív szövegmező. Az Enter kiválaszt; a Backspace nyomva tartása visszalép.
Night Éjszakai
Theme save failed Téma mentése sikertelen
Ack Ack
@@ -1298,3 +1303,203 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: it
# ver: 22
# ver: 23
# name: Italiano
# base: it
(device behind) (dispositivo indietro)
@@ -34,9 +34,14 @@ Accent popups: off Popup accenti: disattivi
Accent popups: on Popup accenti: attivi
Accent colour Colore accento
Appearance Aspetto
Condense Nav (restart to apply) Navigazione compatta (riavvia per applicare)
Condense Nav saved — restart to apply Navigazione compatta salvata — riavvia per applicare
Day Giorno
High contrast Contrasto elevato
High contrast - restarting to apply it… Contrasto elevato - riavvio per applicarlo…
Keyboard nav: on Nav. tastiera: on
Landscape only Solo in orizzontale
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Le lettere spostano l'evidenziazione quando nessun campo di testo è attivo. Invio seleziona; tieni premuto Backspace per tornare indietro.
Night Notte
Theme save failed Salvataggio tema non riuscito
Ack Ack
@@ -1281,3 +1286,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: nl
# ver: 22
# ver: 23
# name: Nederlands
# base: nl
(device behind) (apparaat loopt achter)
@@ -34,9 +34,14 @@ Accent popups: off Accent-popups: uit
Accent popups: on Accent-popups: aan
Accent colour Accentkleur
Appearance Weergave
Condense Nav (restart to apply) Compacte navigatie (herstart om toe te passen)
Condense Nav saved — restart to apply Compacte navigatie opgeslagen — herstart om toe te passen
Day Dag
High contrast Hoog contrast
High contrast - restarting to apply it… Hoog contrast - opnieuw opstarten om toe te passen…
Keyboard nav: on Toetsenbordnav.: aan
Landscape only Alleen liggend
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Letters verplaatsen de markering als er geen tekstveld actief is. Enter selecteert; houd Backspace ingedrukt om terug te gaan.
Night Nacht
Theme save failed Thema opslaan mislukt
Ack Ack
@@ -1279,3 +1284,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: pt-br
# ver: 22
# ver: 23
# name: Português (BR)
# base: pt-br
(device behind) (dispositivo atrasado)
@@ -34,9 +34,14 @@ Accent popups: off Pop-ups de acento: desligados
Accent popups: on Pop-ups de acento: ligados
Accent colour Cor de destaque
Appearance Aparência
Condense Nav (restart to apply) Navegação compacta (reinicie para aplicar)
Condense Nav saved — restart to apply Navegação compacta salva — reinicie para aplicar
Day Dia
High contrast Alto contraste
High contrast - restarting to apply it… Alto contraste - reiniciando para aplicar…
Keyboard nav: on Nav. teclado: ligado
Landscape only Somente em paisagem
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. As letras movem o destaque quando nenhum campo de texto está ativo. Enter seleciona; segure Backspace para voltar.
Night Noite
Theme save failed Falha ao salvar o tema
Ack Ack
@@ -1281,3 +1286,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: ro
# ver: 22
# ver: 23
# name: Română
# base: ro
(device behind) (dispozitiv în urmă)
@@ -34,9 +34,14 @@ Accent popups: off Popupuri accente: oprite
Accent popups: on Popupuri accente: pornite
Accent colour Culoare de accent
Appearance Aspect
Condense Nav (restart to apply) Navigare compactă (repornire pentru aplicare)
Condense Nav saved — restart to apply Navigare compactă salvată — reporneşte pentru aplicare
Day Zi
High contrast Contrast ridicat
High contrast - restarting to apply it… Contrast ridicat - repornire pentru aplicare…
Keyboard nav: on Navigare tastatură: pornită
Landscape only Doar în modul peisaj
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Literele mută selecția când niciun câmp de text nu este activ. Enter selectează; ține apăsat Backspace pentru înapoi.
Night Noapte
Theme save failed Salvarea temei a eșuat
Ack Ack
@@ -1283,3 +1288,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: ru
# ver: 22
# ver: 23
# name: Русский
# base: ru
(device behind) (устройство отстаёт)
@@ -34,9 +34,14 @@ Accent popups: off Подсказки акцентов: выкл
Accent popups: on Подсказки акцентов: вкл
Accent colour Акцентный цвет
Appearance Оформление
Condense Nav (restart to apply) Компактная навигация (перезапуск для применения)
Condense Nav saved — restart to apply Компактная навигация сохранена — перезапуск для применения
Day День
High contrast Высокая контрастность
High contrast - restarting to apply it… Высокая контрастность - перезагрузка для применения…
Keyboard nav: on Навигация клавиатурой: вкл.
Landscape only Только в альбомной ориентации
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Буквы перемещают выделение, когда не активно текстовое поле. Enter выбирает; удерживайте Backspace, чтобы вернуться.
Night Ночь
Theme save failed Не удалось сохранить тему
Ack Подтв.
@@ -1282,3 +1287,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: sr
# ver: 22
# ver: 23
# name: Српски
# base: sr
(device behind) (уређај касни)
@@ -34,9 +34,14 @@ Accent popups: off Искачући акценти: искљ
Accent popups: on Искачући акценти: укљ
Accent colour Акцентна боја
Appearance Изглед
Condense Nav (restart to apply) Компактна навигација (рестарт за примену)
Condense Nav saved — restart to apply Компактна навигација сачувана — рестарт за примену
Day Дан
High contrast Висок контраст
High contrast - restarting to apply it… Висок контраст - рестартовање ради примене…
Keyboard nav: on Навиг. тастатуром: укљ.
Landscape only Само у хоризонталном положају
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Слова померају истицање када ниједно текстуално поље није активно. Enter бира; држите Backspace за назад.
Night Ноћ
Theme save failed Чување теме није успело
Ack Ack
@@ -1282,3 +1287,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+211 -1
View File
@@ -1,7 +1,7 @@
# wadamesh language file
# canonical source - translators: edit this file and PR it
# code: uk
# ver: 22
# ver: 23
# name: Українська
# base: uk
(device behind) (пристрій відстає)
@@ -34,9 +34,14 @@ Accent popups: off Підказки акцентів: вимк
Accent popups: on Підказки акцентів: увімк
Accent colour Акцентний колір
Appearance Вигляд
Condense Nav (restart to apply) Компактна навігація (перезапуск для застосування)
Condense Nav saved — restart to apply Компактну навігацію збережено — перезапуск для застосування
Day День
High contrast Висока контрастність
High contrast - restarting to apply it… Висока контрастність - перезавантаження для застосування…
Keyboard nav: on Навігація клавіатурою: увімк.
Landscape only Лише в альбомній орієнтації
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Літери переміщують виділення, коли не активне текстове поле. Enter вибирає; утримуйте Backspace, щоб повернутися.
Night Ніч
Theme save failed Не вдалося зберегти тему
Ack Підтв.
@@ -1282,3 +1287,208 @@ No known Windows malware found. No known Windows malware found.
This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row. This firmware can find these but not remove them. Update wadamesh, or format the card: in Files, hold the SD card row.
SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row. SD Scan only removes files it recognises. If the card may be infected, the safest fix is to copy off what you need and format it completely: in Files, hold the SD card row.
and %d more and %d more
# beta_89: new strings, English placeholders until translated (value = key)
%d nodes heard today %d nodes heard today
%lu packets heard %lu packets heard
1 node heard today 1 node heard today
1 packet heard 1 packet heard
1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge: 1. Connect this device to the computer with a USB cable.\n2. Open this address in Chrome or Edge:
3. Click Connect and choose this device. 3. Click Connect and choose this device.
4 numbers or letters 4 numbers or letters
A day A day
A week A week
Add a channel Add a channel
Add, download or delete files on this device from a computer. Add, download or delete files on this device from a computer.
Advertise Advertise
Always on when connected to power Always on when connected to power
Anything worth saying (optional) Anything worth saying (optional)
App drawer App drawer
App icons App icons
Append timestamp Append timestamp
Apr Apr
April April
Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it. Asks for 4 numbers or letters to unlock, and locks whenever the screen goes off. If you forget it, the only way back in is to reset the device, which erases everything on it.
Aug Aug
August August
Blocked Blocked
Change PIN Change PIN
Channel Channel
Channels Channels
Chat Chat
Chat icon reset Chat icon reset
Chats Chats
Choose a PIN Choose a PIN
Clock not set Clock not set
Cmdr Cmdr
Commander Commander
Companion Companion
Connected to the computer Connected to the computer
Could not send the report. Try again later. Could not send the report. Try again later.
Count this device Count this device
Current PIN Current PIN
Cursor Cursor
Dec Dec
December December
Delete this conversation? Delete this conversation?
Device id Device id
Direct Direct
Direct message Direct message
Direct, no repeaters Direct, no repeaters
Discover Discover
Double-tap SPACE while typing to switch Double-tap SPACE while typing to switch
Emoji Emoji
Enter PIN Enter PIN
Enter it again Enter it again
Export Backup Export Backup
Feb Feb
February February
Fri Fri
Friday Friday
Has a problem Has a problem
Heard by %u repeaters Heard by %u repeaters
Heard by 1 repeater Heard by 1 repeater
Hold the trackball to switch Hold the trackball to switch
Home button opens Home button opens
I hit a problem I hit a problem
I understand I understand
If you forget your PIN, you have to reset the device If you forget your PIN, you have to reset the device
Internal Internal
It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you. It is the only way back in, and it erases everything on it: contacts, channels, messages and settings. Nobody can unlock it for you.
Jan Jan
January January
Join # channel Join # channel
Join Public channel Join Public channel
Jul Jul
July July
Jun Jun
June June
Keyboard and input Keyboard and input
Keyboard attached.\nRestarting in landscape in %d s. Keyboard attached.\nRestarting in landscape in %d s.
Large Large
Launcher Launcher
Launcher layout Launcher layout
Locks when screen off\n(hold the BOOT button to unlock) Locks when screen off\n(hold the BOOT button to unlock)
Map and GPS Map and GPS
Map tiles Map tiles
Mar Mar
March March
Mark all as read Mark all as read
May May
Mentions Mentions
Mentions muted Mentions muted
Mentions unmuted Mentions unmuted
Mesh · 24 h Mesh · 24 h
Message %s Message %s
Message text on lock screen Message text on lock screen
Messages muted Messages muted
Messages unmuted Messages unmuted
Minutes Minutes
Mon Mon
Monday Monday
Mute mentions Mute mentions
Mute messages Mute messages
Navigate Navigate
Never Never
New PIN New PIN
No backups available to restore. No backups available to restore.
No backups yet. Use Export Backup above to create one. No backups yet. Use Export Backup above to create one.
No channels yet No channels yet
No direct chats yet No direct chats yet
Normal\nLarge text\nHuge text Normal\nLarge text\nHuge text
Not delivered, tap to resend Not delivered, tap to resend
Nov Nov
November November
Oct Oct
October October
On On
On battery, show it for On battery, show it for
PIN changed PIN changed
PIN lock PIN lock
PIN lock off PIN lock off
PIN lock on PIN lock on
Phone-app link Phone-app link
Pick the numbers, or type the PIN Pick the numbers, or type the PIN
Preview screensaver Preview screensaver
Radio and messaging Radio and messaging
Reboot Now Reboot Now
Received %s Received %s
Receiving %s Receiving %s
Region not saved: use a-z, 0-9 and - Region not saved: use a-z, 0-9 and -
Remote Remote
Repeater Repeater
Report a bug Report a bug
Reset chat icon Reset chat icon
Room Room
Routed Routed
Running it for Running it for
Running it for: Running it for:
SD card SD card
SD unavailable - insert a FAT32 card and retry SD unavailable - insert a FAT32 card and retry
Sat Sat
Saturday Saturday
Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account. Say whether this build works on your board, so a stable release is promoted on evidence rather than on nobody having complained. A bug report opens as a QR: scan it and file it from your phone, under your own GitHub account.
Screen timeout Screen timeout
Screensaver Screensaver
Scroll Scroll
Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor. Scroll moves the screen; Cursor points and clicks; Navigate (experimental) moves a selection. Hold the trackball to switch between Scroll and Cursor.
Send Send
Send a test report Send a test report
Send another test report Send another test report
Sending %s Sending %s
Sending your report... Sending your report...
Sensor Sensor
Sep Sep
September September
Share my contact Share my contact
Spectrum Spectrum
Stop USB Files Stop USB Files
Storage and SD Storage and SD
Sun Sun
Sunday Sunday
Tap the EN key on the keyboard to switch Tap the EN key on the keyboard to switch
Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else. Tells the matrix how many devices run this build, so a board with no reports can be told apart from a board nobody owns. Sends the board and the version, nothing else.
Terminal Terminal
Test report: %s Test report: %s
Thank you. Your report is on wadamesh.com/beta Thank you. Your report is on wadamesh.com/beta
The PINs did not match The PINs did not match
The mesh around you instead of a dark screen. Every ring is a real packet. The mesh around you instead of a dark screen. Every ring is a real packet.
This is everything that leaves the device. No name, no contacts, no position. This is everything that leaves the device. No name, no contacts, no position.
Thu Thu
Thursday Thursday
Tick only what you actually used: Tick only what you actually used:
Too many chat icons Too many chat icons
Too many tries. Wait %u s Too many tries. Wait %u s
Trackball Trackball
Trackball: scroll Trackball: scroll
Transfer Transfer
Tue Tue
Tuesday Tuesday
Turn Wi-Fi on to send a report Turn Wi-Fi on to send a report
Type 4 numbers or letters Type 4 numbers or letters
USB Files USB Files
USB Files: low memory USB Files: low memory
Unmute mentions Unmute mentions
Unmute messages Unmute messages
Upload of %s stopped Upload of %s stopped
Used Used
VNC VNC
Waiting for the computer Waiting for the computer
Web Web
Wed Wed
Wednesday Wednesday
While this screen is open, the companion app cannot use USB. While this screen is open, the companion app cannot use USB.
Works Works
Wrong PIN Wrong PIN
You: You:
a week or more a week or more
about a day about a day
heard %s ago heard %s ago
hold the BOOT button to unlock hold the BOOT button to unlock
hop away hop away
hops away hops away
minutes minutes
new new
node node
nodes nodes
now now
routed routed
+14 -14
View File
@@ -3,72 +3,72 @@
{
"code": "bg",
"name": "Български",
"ver": "22"
"ver": "23"
},
{
"code": "de",
"name": "Deutsch",
"ver": "22"
"ver": "23"
},
{
"code": "el",
"name": "Ελληνικά",
"ver": "22"
"ver": "23"
},
{
"code": "es",
"name": "Español",
"ver": "22"
"ver": "23"
},
{
"code": "fr",
"name": "Français",
"ver": "22"
"ver": "23"
},
{
"code": "hu",
"name": "Magyar",
"ver": "22"
"ver": "23"
},
{
"code": "it",
"name": "Italiano",
"ver": "22"
"ver": "23"
},
{
"code": "nl",
"name": "Nederlands",
"ver": "22"
"ver": "23"
},
{
"code": "pt-br",
"name": "Português (BR)",
"ver": "22"
"ver": "23"
},
{
"code": "ro",
"name": "Română",
"ver": "22"
"ver": "23"
},
{
"code": "cs",
"name": "Čeština",
"ver": "22"
"ver": "23"
},
{
"code": "ru",
"name": "Русский",
"ver": "22"
"ver": "23"
},
{
"code": "sr",
"name": "Српски",
"ver": "22"
"ver": "23"
},
{
"code": "uk",
"name": "Українська",
"ver": "22"
"ver": "23"
}
]
}
+1
View File
@@ -489,6 +489,7 @@ end</code></pre>
<tr><td>wada.sys.random(n)</td><td>Integer in 1..n.</td></tr>
<tr><td>wada.sys.epoch()</td><td>Unix time in seconds, or <code>nil</code> when the clock has not been set yet (no GPS fix and no NTP). Always handle the <code>nil</code>.</td></tr>
<tr><td>wada.sys.datetime()</td><td><code>{year, month, day, hour, min, sec, wday}</code> in local time. <code>wday</code> is 0 for Sunday.</td></tr>
<tr><td>wada.ui.emoji(cb)</td><td>Open the emoji picker and call <code>cb(glyph)</code> with the chosen emoji as a UTF-8 string, or <code>cb(nil)</code> if it was dismissed. For an app that draws its own text entry and keeps the buffer itself; if you use <code>wada.ui.input()</code> you do not need this, the picker is already on its long-press menu.</td></tr>
<tr><td colspan="2" style="color:var(--mut)">Text entry you open with <code>wada.ui.input()</code> is the firmware's own field, so it comes with the firmware's editing: long-press it for Cut, Copy, Paste, All, Sym (special characters) and Emoji. You do not need to build any of that yourself, and anything copied from a chat pastes straight in.</td></tr>
<tr><td>wada.sys.tr(s)</td><td>Translate <code>s</code> through the device's active language, using the same table the firmware's own interface uses. Returns <code>s</code> unchanged when there is no translation, so it is always safe to wrap a string. Add your keys to <code>deploy/apps/lang/&lt;code&gt;.lang</code> alongside the firmware's. Older firmware has no <code>sys.tr</code>: write <code>local tr = sys.tr or function(x) return x end</code> and call <code>tr()</code>.</td></tr>
<tr><td>wada.sys.beep()</td><td>Short beep on boards with a buzzer; silent elsewhere, and silent when the user has sound off.</td></tr>
+13 -21
View File
@@ -53,27 +53,19 @@
#define LV_USE_LOG 0
#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1
/* Larger Montserrat sizes for crisp UI/text-size presets at native resolution. The P4 boards scale
* text and geometry; V4-R8 uses the same fonts without geometry scaling. Other flash-tighter S3
* builds don't pay for fonts they never use. (Match board macros because device_caps.h isn't here.) */
#if defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8)
#define LV_FONT_MONTSERRAT_18 1
#define LV_FONT_MONTSERRAT_20 1
#define LV_FONT_MONTSERRAT_24 1
#endif
/* T-Deck-only, unrelated to the Large/Huge UI-scale block above: an experiment
* to shrink the "at a glance" notification's message body from 28px to 20px
* on this board only (see atGlanceEnsureFont() in UITask.cpp). Easy to revert
* by dropping this block + the T-Deck branch in atGlanceEnsureFont(). */
#if defined(HAS_TDECK_GT911)
#define LV_FONT_MONTSERRAT_20 1
#endif
/* 28 px Montserrat for the boot splash title — keeps the rest of the UI on
* the smaller fonts so the .data cost stays modest. */
#define LV_FONT_MONTSERRAT_28 1
/* The UI faces are generated (scripts/build/gen-touch-fonts.sh -> src/ui-touch/ui_font_NN.c)
* under LVGL's own names, lv_font_montserrat_NN: the same Montserrat Medium text LVGL ships,
* with Lucide line icons on the LV_SYMBOL_* codepoints instead of FontAwesome, so every icon in
* the firmware and in LVGL's widgets is drawn in one consistent line style. LVGL's built-in
* copies (LV_FONT_MONTSERRAT_NN) stay off, and no env may force them on with -D: they would
* collide with the generated ones at link time. Which boards carry the bigger sizes is set
* in the generator, on board macros. */
#define LV_FONT_CUSTOM_DECLARE \
LV_FONT_DECLARE(lv_font_montserrat_12) LV_FONT_DECLARE(lv_font_montserrat_14) \
LV_FONT_DECLARE(lv_font_montserrat_16) LV_FONT_DECLARE(lv_font_montserrat_18) \
LV_FONT_DECLARE(lv_font_montserrat_20) LV_FONT_DECLARE(lv_font_montserrat_24) \
LV_FONT_DECLARE(lv_font_montserrat_28)
#define LV_FONT_DEFAULT &lv_font_montserrat_14
/* Pixelated UNSCII bitmap font for the boot splash. Matches the retro/
* pixelated wordmark the bootloader paints, so the splash reads as a
* direct continuation of the boot sequence rather than a different style
+16 -31
View File
@@ -263,13 +263,9 @@ build_flags =
${env:heltec_v4_tft_companion_radio_usb_tcp_touch.build_flags}
; --- V4-R8 deltas (later -D wins on the GCC command line) ---
-D HELTEC_LORA_V4_R8
; The text-size presets use these. Forced here, not just in include/lv_conf.h:
; the vendored .pio/libdeps/<env>/MeshCore/include/lv_conf.h wins the include
; order for src/ compiles (see the T-Deck env), so UITask.cpp would not see
; them and the build fails on lv_font_montserrat_18/20/24.
-D LV_FONT_MONTSERRAT_18=1
-D LV_FONT_MONTSERRAT_20=1
-D LV_FONT_MONTSERRAT_24=1
; The text-size presets use Montserrat 18/20/24. Those are generated fonts now
; (src/ui-touch/ui_font_NN.c, gated on this board's macro in the generator), not
; LVGL's: never force LV_FONT_MONTSERRAT_NN=1 here, it would link LVGL's copy too.
; Boot at the S3's full clock. The base env's ESP32_CPU_FREQ=80 is applied in
; ESP32Board::begin(), so ALL of setup() — radio init, the SD mount ladder, the
; store load, mesh begin, Wi-Fi bring-up — ran at a third speed until UITask::begin
@@ -631,15 +627,14 @@ build_flags =
-D HAS_TDECK_GT911=1
-D HAS_TDECK_TRACKBALL=1
-D HAS_TDECK_KEYBOARD=1
; Forced here (not just in lv_conf.h) because .pio/libdeps/<env>/MeshCore/include/lv_conf.h
; -- a stale snapshot vendored by the pinned core tag -- sits earlier in the -I search
; order than this repo's own include/lv_conf.h, so LV_CONF_INCLUDE_SIMPLE's quoted
; #include "lv_conf.h" silently resolves to that stale copy for src/ compiles (though
; library .c files, e.g. lvgl's own, still see the right one -- inconsistent per TU). A
; -D here is immune to that: it's set before any header is processed and isn't
; overridden by a plain #define. Only 20px is forced, for the "at a glance" experiment
; below (atGlanceEnsureFont() in UITask.cpp) -- not 18/24, which this board doesn't use.
-D LV_FONT_MONTSERRAT_20=1
; Montserrat 20 for the "at a glance" body (atGlanceEnsureFont() in UITask.cpp) is a
; generated font, gated on HAS_TDECK_GT911 in scripts/build/gen-touch-fonts.sh. It is
; not switched in lv_conf.h because .pio/libdeps/<env>/MeshCore/include/lv_conf.h -- a
; stale snapshot vendored by the pinned core tag -- sits earlier in the -I search order
; than this repo's own include/lv_conf.h, so LV_CONF_INCLUDE_SIMPLE's quoted #include
; "lv_conf.h" resolves to that stale copy for src/ compiles (though library .c files,
; e.g. lvgl's own, still see the right one -- inconsistent per TU). Never force
; LV_FONT_MONTSERRAT_NN=1: it would link LVGL's own copy beside the generated one.
-D ENABLE_ADVERT_ON_BOOT=0
-D PIN_TB_UP=15
-D PIN_TB_DOWN=3
@@ -1491,9 +1486,6 @@ build_flags =
; --- T-LoRa Pager board ---
-I variants/lilygo_tlora_pager
-D TLORA_PAGER=1
-D LV_FONT_MONTSERRAT_18=1
-D LV_FONT_MONTSERRAT_20=1
-D LV_FONT_MONTSERRAT_24=1
-D BOARD_HAS_PSRAM=1
; 0 = silence ARDUHAL [E] spam, same rationale as the other two envs.
-D CORE_DEBUG_LEVEL=0
@@ -1726,9 +1718,6 @@ build_flags =
; --- T-LoRa Pager board (SX1262 variant) ---
-I variants/lilygo_tlora_pager
-D TLORA_PAGER=1
-D LV_FONT_MONTSERRAT_18=1
-D LV_FONT_MONTSERRAT_20=1
-D LV_FONT_MONTSERRAT_24=1
-D BOARD_HAS_PSRAM=1
; 0 = silence ARDUHAL [E] spam, same rationale as the other envs.
-D CORE_DEBUG_LEVEL=0
@@ -1927,13 +1916,9 @@ build_flags =
-I"${platformio.libdeps_dir}/${this.__env__}/MicroNMEA/src"
-Wno-deprecated-declarations -Wno-unused-parameter -Wno-cpp -Wno-overflow -Wno-return-type -std=gnu++17 -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
-D MC_VENDORED_TOUCH_APP
; The text-size presets use these. Forced here, not just in include/lv_conf.h:
; the vendored .pio/libdeps/<env>/MeshCore/include/lv_conf.h wins the include
; order for src/ compiles, so UITask.cpp would not see them and the build fails
; on lv_font_montserrat_18/20/24.
-D LV_FONT_MONTSERRAT_18=1
; (20px is already forced further down for the at-a-glance body font.)
-D LV_FONT_MONTSERRAT_24=1
; The text-size presets use Montserrat 18/20/24. Those are generated fonts now
; (src/ui-touch/ui_font_NN.c, gated on this board's macro in the generator), not
; LVGL's: never force LV_FONT_MONTSERRAT_NN=1 here, it would link LVGL's copy too.
-D LORA_FREQ=869.618
-D LORA_BW=62.5
-D LORA_SF=8
@@ -2115,8 +2100,8 @@ build_flags =
-D FIRMWARE_OTA_ENV='"ThinkNode_M9_companion_radio_touch"'
; 20px Montserrat for the at-a-glance body (atGlanceEnsureFont). 28px filled
; this 240x320 panel and crowded out the message (#446); the T-Deck already
; dropped to 20 for the same reason. Only 20px is forced, nothing else.
-D LV_FONT_MONTSERRAT_20=1
; dropped to 20 for the same reason. The font is generated and gated on this
; board in scripts/build/gen-touch-fonts.sh, so nothing is forced here.
-D ENABLE_ADVERT_ON_BOOT=0
-D LV_CONF_PATH=lv_conf_m9.h
-D LV_CONF_INCLUDE_SIMPLE=1
+36
View File
@@ -0,0 +1,36 @@
# beta_85 promoted to STABLE - cumulative over beta_84..beta_85 (previous stable: beta_83)
# One user-facing note per non-blank, non-# line; # lines are section comments.
# Curated from the two beta notes: test-build framing dropped, and the one thing
# that got worse called out at the top rather than left for a P4 owner to find.
# --- Read this first if you have a T-Display P4 --------------------------------------------------
Do not switch the screen to landscape on this build. Landscape was added in beta_85 and the touch screen comes back rotated and offset, so the control to switch back cannot be hit and the only way out is reflashing over USB. It is fixed in the beta_86 test build. Portrait is the default and is unaffected.
# --- Read this first if you have a T-Deck Max ----------------------------------------------------
A Max running beta_84 that updates itself installs T-Deck Pro firmware, because it is the firmware you are running that picks the file to download. Update to this one over USB from wadamesh.com rather than from the device. From beta_85 onwards it is fixed.
# --- Tell us whether a build works, from the device -----------------------------------------------
Settings, About has "Send a test report". Say whether it works on your board, tick what you actually used, say how long you have run it, and send. What comes in is at wadamesh.com/beta, and that is what the next stable release gets promoted on, instead of a build going out because nobody complained. It needs Wi-Fi.
The report carries the board, the version, your ticks and the free memory. No name, no contacts, no position, and the device lists exactly what it will send before it sends it. Your device is identified by a salted hash of its public key, so a second report replaces your first rather than counting twice.
"Report a bug" on the same page draws a QR code. Scan it with your phone and a GitHub issue form opens with your board, version and diagnostics already filled in, so you only type what went wrong, and it is filed under your own account.
Optional and off by default: "Count this device" lets the update check say which board and build it is running, so a board nobody has reported on can be told apart from a board nobody owns.
# --- New boards ------------------------------------------------------------------------------------
The LilyGo T-Deck Max, contributed and tested by pelgraine. It is the T-Deck Pro's hardware with the peripheral power rails and resets behind an I/O expander and a fuel gauge for the battery. The e-paper screen gets its own handling: no fades, no black frame left on the glass when it sleeps, tap to wake, and the keyboard backlight on a both-Shift chord. Experimental.
# --- Things you can do now ---------------------------------------------------------------------------
High contrast day and night themes, for sunlight where the normal palette washes out. Settings, Display, Theme.
Channel regions can be discovered instead of typed: the region page scans for repeaters you can hear and lists the regions they allow, and channel scope offers them in a picker.
Text size presets on the ThinkNode M9: Settings, Display, UI size. It grows the text and leaves the layout alone, so nothing becomes unreachable. Your current setting is untouched by the update.
Coordinates in a chat message can be opened on the map. The message menu gains Map, tapping the message does the same, and the spot is marked so you can find it again or remove it. By TerryTel93.
Czech, contributed by brebtatv. Settings, Language.
# --- Fixes ---------------------------------------------------------------------------------------------
Radio settings save again when the region field holds a name the new rules reject, such as one with capitals. Only the region is skipped now, instead of the frequency, bandwidth and power going with it.
The Show button on telemetry shows its label in Russian, Ukrainian, Bulgarian, Serbian and Greek instead of empty boxes.
Scanning for regions no longer leaves nameless entries behind in Contacts.
The V4-R8 image fits again: it no longer carries the compiled-in translations it inherited from the V4, which it never needed.
Wio Tracker L2: the SD card gets a power cycle and a second try when it does not mount at boot.
ThinkNode M9: with the home drawer locked, Back stays on it instead of escaping to the tabs behind it. The Language settings cursor returns where it was after visiting the Store, and a channel keeps its unread colour.
An M5Stack CardKB can be plugged into the Heltec V4's I2C bus and used as a keyboard. It is detected when present, so nothing changes without one.
V4-R8: bigger text sizes to choose from, and the top row of the screen no longer swallows taps meant for the page.
+13
View File
@@ -0,0 +1,13 @@
# beta_87 - test build. Stable is beta_85.
# One user-facing note per non-blank, non-# line; # lines are section comments.
# --- A memory leak in Lua apps -------------------------------------------------------------------
Lua apps leaked memory every time they redrew a screen, and the more a screen had on it the faster it went. Clearing a page destroyed the buttons and list rows but kept the code attached to each one, so an app that rebuilds a list of twenty contacts left twenty of those behind on every redraw, until it ran out of memory and could not build a screen at all. Clearing a page now releases that code along with the widgets it belonged to.
It could also have made a button do the wrong thing. The firmware matched the leftover code to a button by its address, and addresses get reused, so a new button with nothing attached to it could have run something left behind by a screen you had already closed. That cannot happen now that nothing is left behind.
If you write apps and have seen one get slower, or start failing to draw, the longer it ran, this is why. Reported by Jade while writing a one-time-pad app.
# --- For people writing Lua apps -------------------------------------------------------------------
New: wada.ui.emoji(cb) opens the emoji picker and gives you the chosen emoji back, so an app that draws its own text entry can offer them. If you use wada.ui.input() you do not need it: the picker is already on that field's long-press menu, along with Cut, Copy, Paste and Sym.
# --- For testers ---------------------------------------------------------------------------------------
This build is two fixes for Lua apps and nothing else. If you do not write apps or run them, beta_86 is the same firmware for you.
+17
View File
@@ -0,0 +1,17 @@
# beta_88 - user-facing notes. One note per non-blank, non-# line.
# Order: things that stopped a device working, then things people reported, then new.
If you have a T-Display P4 that would not boot on beta_86 or beta_87, this is the build that fixes it. Older P4 units carry different Wi-Fi co-processor firmware, and the image we shipped could not talk to it, which left the screen dark and the board restarting every ten seconds. The display, touch and LoRa now come up regardless, and there is a separate download for those units that restores Wi-Fi as well.
A message sent to a channel could go out on the public channel instead. If a channel went missing while you had it open, the web interface fell back to the first channel on the device rather than refusing, and sent there with nothing on screen to say so. It now refuses, the way the on-device composer already did. Reported by TippyLion28.
Deleting a chat's history no longer makes the chat itself disappear from Chats. The confirmation always promised the chat would stay; now it does, and Delete chat remains the way to remove it. By jcyrio.
The T-LoRa Pager no longer loses about half an hour a day. The clock was running off the processor's own timer and drifting, while the accurate clock chip next to it went unread. It is now read back every hour. Diagnosed from the source by jcyrio.
On the ThinkNode M9 the icons along the bottom have stopped flashing. They now carry a count and appear only when they mean something, instead of blinking over whatever is on screen. The one for newly discovered nodes used to blink forever, because it counted every node ever heard rather than the ones you had not looked at.
At 100 percent battery the leading 1 was hidden behind the signal bars on the ThinkNode M9 and the Heltec V4-R8. There is room for it now, at every text size, and it no longer jumps back when you plug in a charger. Reported by buko84 and jd1227.
The status-bar clock no longer sits on top of the Bluetooth icon.
Map tiles cache again on a T-Display P4 with no SD card. The cache was pointed at a filesystem that was never mounted, so every tile quietly failed.
The Secondary keyboards page no longer tells T-LoRa Pager, T-Deck Pro and T-Deck Max owners to tap a key their keyboard does not have. It names double-tap SPACE, which is what actually switches layout there, and turning a layout on now says so. By jcyrio.
New: a battery saver switch on every touch board.
New: Condense Nav. In landscape the main navigation moves to a rail on the right edge and the Home launchers shrink to icons, which gives the content the full width. Off by default.
New: keyboard navigation on the T-LoRa Pager, with remappable tab keys, the same as the T-Deck has. Clicking the wheel on a slider now adjusts it with the wheel.
New on the Attaky Mesh Deck: an Orientation setting so it can run in portrait, File Transfer to internal storage, and Backup and Restore buttons on the File Transfer page in your browser.
Thirteen languages gained translations for the new settings. If your device is not in English it will fetch them on its next language check.
+34
View File
@@ -0,0 +1,34 @@
# beta_89 - the UI overhaul: every screen redesigned. A BIG CHANGE: TEST BUILD ONLY.
# One user-facing note per non-blank, non-# line; # lines are section comments.
# Order: the warning first, then the redesign, then what is new, then fixes, then what is not done yet.
WARNING - This is the biggest change wadamesh has ever had. The whole interface has been redesigned, and many of the base features underneath it were reworked to fit: the status bar, Chats, the conversation screen, the lock screen, the screensaver, Home, Settings, notifications and the way text scales. Expect rough edges, and expect things to have moved.
WARNING - Only install this beta if you want to test. If you rely on your device every day, stay on the stable build (beta_85) until this has had a few rounds of fixes. If you do install it, please report what you find: every report goes straight into those fixes.
WARNING - It was built and run on a LilyGo T-Deck. Every other board builds, and every screen was checked at each board's resolution and text size, but it has not run on the other hardware yet. If you own one of those boards, you are exactly who this test needs.
Text that is new in this build shows in English for now. The translations follow once the wording has settled.
# --- The redesign ---
One design across the whole firmware: a single dark palette, a clear type scale (a light clock, SemiBold names, quieter secondary and meta text), line icons everywhere, and one style for every switch, text field, dropdown, checkbox, slider and button. Settings pages no longer show white boxes on a black screen, and the main action on a page is a solid teal button with dark text.
The status bar is calmer: the clock in the middle, signal and battery on the right, unread messages on the left, Wi-Fi and Bluetooth small and grey. The scrolling node name stays on the Home tab.
Chats has a new header with All, Channels and Direct filters, a + to add a channel, and a menu with Mark all as read and Share my contact. Every chat gets an avatar drawn from its key, a square for a channel and a circle for a person, instead of coloured initials. The gear on every row is gone: hold a chat, or open the menu in the conversation header, for its options, which are now a plain list of actions (mute messages, mute mentions, reset the chat icon, mark as read, delete history, delete the chat).
The conversation screen has a header with the avatar and how the last message reached you (direct, a number of hops, or how many nodes a channel heard today). Bubbles are neutral and the sender's colour is on the name only. Under each message: how many hops it came over, or for your own channel posts "Heard by N repeaters", counting the repeats as they come back. A direct message shows Sent and Delivered, and one that was not delivered says so and resends with a tap. The composer is a pill with a teal send button.
A new lock screen: the wadamesh mark, a large light clock and the date, a line of the mesh activity over the last 24 hours with the number of packets heard, up to two unread conversations, and how to unlock along the bottom beside the battery. A wallpaper you chose still shows, dimmed behind it. The view moves a few pixels every minute so nothing burns into the panel. Settings > Lock screen > Message text on lock screen hides the message text in those unread cards.
A new screensaver, Constellation: your node in the middle and the nodes you have heard placed around it, a ring wherever a packet actually came from, light running along the routes of nodes heard in the last ten minutes, and nodes you have not heard from in a while fading out. Every movement on it is something that happened on the radio. The clock and the status move to another corner every two minutes. Turn it on, and preview it, in Settings > Display.
Home opens on the launcher, grouped by kind. Hold the Home tab for the launcher settings: whether the Home button opens the Commander or the App drawer, and the icon size. The Cmdr tile opens the old command centre, rebuilt as a Status page.
Settings opens on a grouped list (Mesh, Connections, Device, System) with an icon for every page.
The boot screen is animated: the mark lights up node by node and the wordmark rises into place.
A message that arrives while the screen is dark now shows as a notification card: the avatar, the conversation and who sent it, how many hops it came over, the time, how many have arrived, and the message itself. On a dark screen the time sits above it in large type. Over the lock screen it sits under the lock screen's own clock, and over the screensaver it takes the side of the screen the clock is not on.
# --- New ---
Optional PIN lock. Settings > Lock screen > PIN lock asks for 4 numbers or letters to unlock, and locks the device at boot and whenever the screen goes off. A forgotten PIN cannot be recovered: the only way back in is to reset the device, which erases everything on it, and you have to accept a warning that says so before the PIN is set. Boards with a keyboard just type it, touch-only boards get an on-screen pad, and boards without a touchscreen (ThinkNode M9, T-LoRa Pager, Tanmatsu) get a number pad you move through with the arrows, the d-pad or the wheel. The keyboard backlight comes on while you enter it. Wrong tries slow down: every fifth miss makes you wait, from 30 seconds up to 4 minutes, and the count survives a reboot. Available on every board with a lock screen; the Heltec V4, Heltec V4-R8, Attaky and CrowPanel have none.
The screensaver now also runs on battery. Settings > Display > On battery, show it for sets how long it stays up after the screen timeout before the screen goes dark: from off up to a minute, 15 seconds by default. Always on when connected to power keeps it up for as long as the device is plugged in. The old Only while charging option is gone.
Text sizes on the T-Deck: Settings > Display > UI size now offers Normal, Large text and Huge text (it applies after a restart). Every screen was rechecked at the bigger sizes on every board that has them.
T-Deck trackball: Scroll is the new default, and if you were on Cursor this build moves you to Scroll once. Hold the trackball still for about half a second to switch between Scroll and Cursor; a card in the middle of the screen shows which one you are in.
Hold the Settings tab to open the control panel (brightness and the quick switches) straight away.
The ThinkNode M9 no longer shows notice icons along the bottom of the screen. The status bar already shows unread messages, so the icons only repeated it over whatever was on screen.
# --- Fixes ---
Nodes whose adverts came in over 2 or 3 byte path hashes never appeared among the recently heard nodes, because the packed path length was compared as if it were a byte count. They show up now (on the screensaver too), and the phone app's advert path request returns their whole path instead of a cut-off one.
# --- Not done yet ---
Some less used pages and dialogs still have the old look; they move over in the next betas. A red night vision theme and the rest of the motion design are still to come.
+11
View File
@@ -52,6 +52,17 @@ BOARDS = {
"manifest-tdisplay-p4-lr2021.json": ("wadamesh - LilyGo T-Display P4 (AMOLED, LR2021)", "wadamesh-tdisplay-p4-lr2021-merged.bin", "ESP32-P4"),
"manifest-tdisplay-p4-lcd.json": ("wadamesh - LilyGo T-Display P4 (LCD, SX1262)", "wadamesh-tdisplay-p4-lcd-merged.bin", "ESP32-P4"),
"manifest-tdisplay-p4-lcd-lr2021.json": ("wadamesh - LilyGo T-Display P4 (LCD, LR2021)", "wadamesh-tdisplay-p4-lcd-lr2021-merged.bin", "ESP32-P4"),
# The T-Display P4 ships with one of TWO C6 co-processor firmwares and the
# image has to match. Current V1 units carry esp-hosted (the four entries
# above); OLDER units still have the factory ESP-AT, and on those the
# esp-hosted image cannot reach the C6 at all. Picking the wrong one is not
# subtle -- before b52c71f it boot-looped the board, and it still means no
# Wi-Fi and no BLE. The label has to let someone choose without a serial
# log, so it says what they can actually check: when it was bought.
"manifest-tdisplay-p4-at.json": ("wadamesh - LilyGo T-Display P4 (AMOLED, SX1262) - older unit, ESP-AT C6", "wadamesh-tdisplay-p4-at-merged.bin", "ESP32-P4"),
"manifest-tdisplay-p4-lr2021-at.json": ("wadamesh - LilyGo T-Display P4 (AMOLED, LR2021) - older unit, ESP-AT C6", "wadamesh-tdisplay-p4-lr2021-at-merged.bin", "ESP32-P4"),
"manifest-tdisplay-p4-lcd-at.json": ("wadamesh - LilyGo T-Display P4 (LCD, SX1262) - older unit, ESP-AT C6", "wadamesh-tdisplay-p4-lcd-at-merged.bin", "ESP32-P4"),
"manifest-tdisplay-p4-lcd-lr2021-at.json": ("wadamesh - LilyGo T-Display P4 (LCD, LR2021) - older unit, ESP-AT C6", "wadamesh-tdisplay-p4-lcd-lr2021-at-merged.bin", "ESP32-P4"),
}
for fn, (name, binf, chip) in BOARDS.items():
# A board that joined the matrix after this tag has no image in this feed
+233
View File
@@ -48,6 +48,21 @@ verify_sha256() {
}
montserrat="$work_dir/Montserrat-Medium.ttf"
# Light weight, for the large screensaver / lock-screen clock only. LVGL ships just
# Medium, so this one comes from the Montserrat project itself, pinned to a TAG
# (not master) so the bytes and therefore the generated font cannot drift.
montserrat_light="$work_dir/Montserrat-Light.ttf"
# SemiBold for names, titles and figures, Bold for the small count badges: the
# weight steps the redesigned UI leans on, from the same pinned release.
montserrat_semibold="$work_dir/Montserrat-SemiBold.ttf"
montserrat_bold="$work_dir/Montserrat-Bold.ttf"
# Lucide line icons (ISC, plus MIT for the icons it took from Feather). They
# replace the FontAwesome symbols LVGL bakes into its Montserrat, see
# generate_ui below and scripts/build/lucide_font.py.
lucide_tgz="$work_dir/lucide-static-1.52.0.tgz"
lucide="$work_dir/lucide.ttf"
lucide_cp="$work_dir/lucide-codepoints.json"
lucide_py="$repo_root/scripts/build/lucide_font.py"
noto_sans_zip="$work_dir/NotoSans-v2.015.zip"
noto_sans="$work_dir/NotoSans-Regular.ttf"
noto_arabic_zip="$work_dir/NotoSansArabicUI-v2.011.zip"
@@ -59,6 +74,27 @@ download \
"https://raw.githubusercontent.com/lvgl/lvgl/v8.4.0/scripts/built_in_font/Montserrat-Medium.ttf" \
"$montserrat" \
"421f26b23e2be6b98373d32acd3cb2897b154d4bf0a77d26534ce476e4cbed53"
download \
"https://raw.githubusercontent.com/JulietaUla/Montserrat/v7.222/fonts/ttf/Montserrat-Light.ttf" \
"$montserrat_light" \
"558059a690df85c773135e5f58582897238d595cdec7f86765e6db10df368937"
download \
"https://raw.githubusercontent.com/JulietaUla/Montserrat/v7.222/fonts/ttf/Montserrat-SemiBold.ttf" \
"$montserrat_semibold" \
"49fbfce003ad1692d7c9a6502791577088c12c50088d4caa27dbbfe540ad9d13"
download \
"https://raw.githubusercontent.com/JulietaUla/Montserrat/v7.222/fonts/ttf/Montserrat-Bold.ttf" \
"$montserrat_bold" \
"4e6d93bc38122c371acb8dc0dbefbf2649c235191e1be136bb5720546d719808"
download \
"https://registry.npmjs.org/lucide-static/-/lucide-static-1.52.0.tgz" \
"$lucide_tgz" \
"4d2aa079171e4eba4c704844b8b0c9edb6e4cfb2fb8bd86f7b14353994f04a87"
tar -xzf "$lucide_tgz" -C "$work_dir" package/font/lucide.ttf package/font/codepoints.json
mv "$work_dir/package/font/lucide.ttf" "$lucide"
mv "$work_dir/package/font/codepoints.json" "$lucide_cp"
verify_sha256 "$lucide" \
"124ecc64b91a158fc519eefdaaead89a2d9e2c39b6f9c8429c5584c93a0be451"
download \
"https://github.com/notofonts/latin-greek-cyrillic/releases/download/NotoSans-v2.015/NotoSans-v2.015.zip" \
"$noto_sans_zip" \
@@ -229,6 +265,175 @@ generate_star() {
"Noto Sans Symbols 2.008"
}
# The ambient clock: digits and the colon only, in Montserrat Light. Eleven glyphs
# is what keeps a 48 px face to a few KB -- the screensaver and lock screen need a
# large, quiet clock, and the biggest face every board already carries is 28 px
# Medium, which reads as a heading rather than a clock.
generate_clock() {
local size=$1
local guard_expr=${2:-}
local guard_label=${3:-$guard_expr}
local raw="$work_dir/clock_font_${size}.c"
local output="$stage/src/ui-touch/clock_font_${size}.c"
"${font_conv[@]}" \
--size "$size" \
--bpp 4 \
--no-compress \
--format lvgl \
--font "$montserrat_light" \
-r 0x30-0x3A \
--lv-font-name "clock_font_${size}" \
-o "$raw"
normalise "$raw" "$output" \
"Montserrat Light v7.222 (digits and colon)" \
"$guard_expr" "$guard_label"
}
# The UI faces, under LVGL's own font names so the theme, every widget and the
# firmware pick them up unchanged: Montserrat Medium text, exactly as LVGL builds
# its built-in fonts, with Lucide line icons on the LV_SYMBOL_* codepoints where
# LVGL puts FontAwesome. include/lv_conf.h switches LVGL's copies off and says
# which sizes a board carries (WADA_UI_FONT_NN).
generate_ui() {
local size=$1
local guard_expr=${2:-}
local guard_label=${3:-$guard_expr}
local text_raw="$work_dir/ui_text_${size}.c"
local raw="$work_dir/ui_font_${size}.c"
local output="$stage/src/ui-touch/ui_font_${size}.c"
local metrics icons arg
local -a ranges
# Text alone first, for the line metrics the icons must not change.
"${font_conv[@]}" --no-compress --no-prefilter --bpp 4 --size "$size" --format lvgl \
--font "$montserrat" -r 0x20-0x7F,0xB0,0x2022 \
--lv-font-name "lv_font_montserrat_${size}" -o "$text_raw"
metrics=$(python3 "$lucide_py" metrics "$text_raw")
ranges=()
while IFS= read -r arg; do ranges+=("$arg"); done < <(python3 "$lucide_py" ranges "$lucide_cp" lv_symbols)
icons=$(python3 "$lucide_py" count "$lucide_cp" lv_symbols)
"${font_conv[@]}" --no-compress --no-prefilter --bpp 4 --size "$size" --format lvgl \
--font "$montserrat" -r 0x20-0x7F,0xB0,0x2022 \
--font "$lucide" "${ranges[@]}" \
--lv-font-name "lv_font_montserrat_${size}" -o "$raw"
# shellcheck disable=SC2086
python3 "$lucide_py" patch "$raw" "$icons" $metrics
# lv_font_conv guards the body with the font's own macro, LV_FONT_MONTSERRAT_NN,
# which is LVGL's switch for its own copy and stays off, so the body would
# compile to nothing. Which boards carry a size is the board guard below.
perl -pi -e "s/\\bLV_FONT_MONTSERRAT_${size}\\b/WADA_UI_FONT_${size}_BODY/g" "$raw"
normalise "$raw" "$output" \
"Montserrat Medium (LVGL v8.4.0), Lucide 1.52.0 on the LV_SYMBOL codepoints" \
"$guard_expr" "$guard_label"
}
# A heavier cut for names, titles and figures. Latin with the accents European
# names use; anything else falls through to the Medium chain at run time.
generate_semibold() {
local size=$1
local raw="$work_dir/ui_semibold_${size}.c"
local output="$stage/src/ui-touch/ui_semibold_${size}.c"
"${font_conv[@]}" --bpp 4 --size "$size" --format lvgl \
--font "$montserrat_semibold" -r 0x20-0x7E,0xA0-0x17F,0x2022,0x2026 \
--lv-font-name "ui_semibold_${size}" -o "$raw"
normalise "$raw" "$output" "Montserrat SemiBold v7.222"
}
# Count badges: digits, "+" and "!" in Bold, at the one size the pills use.
generate_badge() {
local size=$1
local raw="$work_dir/ui_badge_${size}.c"
local output="$stage/src/ui-touch/ui_badge_${size}.c"
"${font_conv[@]}" --no-compress --bpp 4 --size "$size" --format lvgl \
--font "$montserrat_bold" -r 0x20-0x21,0x2B,0x30-0x39 \
--lv-font-name "ui_badge_${size}" -o "$raw"
normalise "$raw" "$output" "Montserrat Bold v7.222 (digits, + and !)"
}
# App and launcher icons on Lucide's own codepoints (names in ui_icons.h).
generate_icons() {
local size=$1
local guard_expr=${2:-}
local guard_label=${3:-$guard_expr}
local raw="$work_dir/ui_icons_${size}.c"
local output="$stage/src/ui-touch/ui_icons_${size}.c"
local text_raw="$work_dir/ui_icons_${size}_text.c"
local metrics icons arg
local -a ranges
# The text these icons sit beside or in place of: its line box, so an icon
# label lines up with a text label, and its capitals, to centre the icons on.
"${font_conv[@]}" --no-compress --no-prefilter --bpp 4 --size "$size" --format lvgl \
--font "$montserrat" -r 0x20-0x7F --lv-font-name "ui_icons_${size}_text" -o "$text_raw"
metrics=$(python3 "$lucide_py" metrics "$text_raw")
ranges=()
while IFS= read -r arg; do ranges+=("$arg"); done < <(python3 "$lucide_py" ranges "$lucide_cp" app)
icons=$(python3 "$lucide_py" count "$lucide_cp" app)
"${font_conv[@]}" --bpp 4 --size "$size" --format lvgl \
--font "$lucide" "${ranges[@]}" \
--lv-font-name "ui_icons_${size}" -o "$raw"
# shellcheck disable=SC2086
python3 "$lucide_py" patch "$raw" "$icons" $metrics
normalise "$raw" "$output" "Lucide 1.52.0 (app icons)" "$guard_expr" "$guard_label"
}
# The small fallback icon fonts the UI chains behind the text (TOUCH_SYM_* in
# UITask.cpp): same names and codepoints as the FontAwesome cuts they replace.
generate_fallback_icons() {
local name=$1
local size=$2
local set=$3
local raw="$work_dir/${name}.c"
local output="$stage/src/ui-touch/${name}.c"
local text_raw="$work_dir/${name}_text.c"
local metrics icons arg
local -a ranges
"${font_conv[@]}" --no-compress --no-prefilter --bpp 4 --size "$size" --format lvgl \
--font "$montserrat" -r 0x41 --lv-font-name "${name}_text" -o "$text_raw"
metrics=$(python3 "$lucide_py" metrics "$text_raw")
ranges=()
while IFS= read -r arg; do ranges+=("$arg"); done < <(python3 "$lucide_py" ranges "$lucide_cp" "$set")
icons=$(python3 "$lucide_py" count "$lucide_cp" "$set")
"${font_conv[@]}" --no-compress --bpp 4 --size "$size" --format lvgl \
--font "$lucide" "${ranges[@]}" \
--lv-font-name "$name" -o "$raw"
# shellcheck disable=SC2086
python3 "$lucide_py" patch "$raw" "$icons" $metrics
normalise "$raw" "$output" "Lucide 1.52.0 (${set} icons)"
}
# Which boards carry which size is decided here, on board macros, and not in
# lv_conf.h: src/ compiles resolve "lv_conf.h" to a stale copy vendored with the
# core (see the T-Deck env in platformio.ini), so a switch there would be seen by
# some translation units and not others. 18/20/24 are the bigger UI-size presets
# (every board with a text-size setting; the V4 has none, its flash is full).
# DOC_CAPTURE builds carry them all: they render other boards' sizes and scales for review.
ui_big="defined(HAS_TANMATSU) || defined(HAS_TDISPLAY_P4) || defined(HELTEC_LORA_V4_R8) || defined(TLORA_PAGER) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911) || defined(DOC_CAPTURE)"
for size in 12 14 16 28; do
generate_ui "$size"
done
generate_ui 18 "$ui_big"
generate_ui 20 "$ui_big"
generate_ui 24 "$ui_big"
for size in 12 14 16; do
generate_semibold "$size"
done
generate_badge 11
generate_icons 16
generate_icons 20
python3 "$lucide_py" header "$lucide_cp" > "$stage/src/ui-touch/ui_icons.h"
generate_fallback_icons person_font 16 person
generate_fallback_icons person_font14 14 person
generate_fallback_icons zoom_font 16 zoom
generate_fallback_icons sleepicons_font 16 sleep
generate_fallback_icons cc_icons_16 16 cc
for size in 12 14 16; do
generate_extras "$size"
done
@@ -247,8 +452,33 @@ generate_latin_extras 28 "" "" 1 \
for size in 14 28; do
generate_star "$size"
done
# 40 px for the screensaver and 48 px for the lock screen, everywhere (the P4
# renders at half its panel resolution, so these are twice that in physical
# pixels there); the Tanmatsu's 800x480 logical screen needs the 96 px face.
generate_clock 40
generate_clock 48
generate_clock 96 "defined(HAS_TANMATSU)" "HAS_TANMATSU"
files=(
src/ui-touch/ui_font_12.c
src/ui-touch/ui_font_14.c
src/ui-touch/ui_font_16.c
src/ui-touch/ui_font_18.c
src/ui-touch/ui_font_20.c
src/ui-touch/ui_font_24.c
src/ui-touch/ui_font_28.c
src/ui-touch/ui_semibold_12.c
src/ui-touch/ui_semibold_14.c
src/ui-touch/ui_semibold_16.c
src/ui-touch/ui_badge_11.c
src/ui-touch/ui_icons_16.c
src/ui-touch/ui_icons_20.c
src/ui-touch/ui_icons.h
src/ui-touch/person_font.c
src/ui-touch/person_font14.c
src/ui-touch/zoom_font.c
src/ui-touch/sleepicons_font.c
src/ui-touch/cc_icons_16.c
src/ui-touch/extras_font_12.c
src/ui-touch/extras_font_14.c
src/ui-touch/extras_font_16.c
@@ -259,6 +489,9 @@ files=(
src/ui-touch/extras_lat_28.c
src/ui-touch/star_font_14.c
src/ui-touch/star_font_28.c
src/ui-touch/clock_font_40.c
src/ui-touch/clock_font_48.c
src/ui-touch/clock_font_96.c
)
if [[ $mode == check ]]; then
+199
View File
@@ -0,0 +1,199 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-or-later
"""Lucide line icons for the touch UI fonts (used by gen-touch-fonts.sh).
lucide_font.py ranges <codepoints.json> <set>
Print lv_font_conv range arguments ("-r", "0xE0F5=>0xF015", ...) that pull
the icons of <set> out of lucide.ttf and place them on their target
codepoints.
lucide_font.py header <codepoints.json>
Print ui_icons.h: a UI_ICON_* string literal per app icon.
lucide_font.py metrics <text.c>
Print "<line_height> <base_line> <cap_height>" of a Montserrat cut: its
line metrics, and the height of its capitals (the box of H, or of A when
the cut has only that letter).
lucide_font.py patch <font.c> <icons> <line_height> <base_line> <cap_height>
Fix up a font lv_font_conv generated with <icons> Lucide glyphs (always
the highest codepoints, so the last glyph ids; all of them in an icon-only
face): centre every icon's drawing on the middle of the capitals, so icons
share one centre line with each other and with the text beside them, and
set the text's line metrics, which the taller icon boxes would otherwise
grow by a pixel and shift every layout.
Sets:
lv_symbols LVGL's LV_SYMBOL_* codepoints, which every widget and most of the
firmware use for its icons. Drawing Lucide there re-skins all of
them without touching a call site.
app The app and launcher icons, on Lucide's own codepoints (U+E000
and up, clear of the FontAwesome range above).
person, zoom, sleep, cc
The codepoints of the small icon fonts the UI chains in as
fallbacks (TOUCH_SYM_* in UITask.cpp).
"""
import json
import re
import sys
LV_SYMBOLS = {
0xF001: "music", 0xF008: "video", 0xF00B: "list", 0xF00C: "check",
0xF00D: "x", 0xF011: "power", 0xF013: "settings", 0xF015: "house",
0xF019: "download", 0xF01C: "hard-drive", 0xF021: "refresh-cw",
0xF026: "volume-x", 0xF027: "volume-1", 0xF028: "volume-2",
0xF03E: "image", 0xF043: "droplet", 0xF048: "skip-back", 0xF04B: "play",
0xF04C: "pause", 0xF04D: "square", 0xF051: "skip-forward", 0xF052: "eject",
0xF053: "chevron-left", 0xF054: "chevron-right", 0xF067: "plus",
0xF068: "minus", 0xF06E: "eye", 0xF070: "eye-off", 0xF071: "triangle-alert",
0xF074: "shuffle", 0xF077: "chevron-up", 0xF078: "chevron-down",
0xF079: "repeat", 0xF07B: "folder", 0xF093: "upload", 0xF095: "phone",
0xF0C4: "scissors", 0xF0C5: "copy", 0xF0C7: "save", 0xF0C9: "menu",
0xF0E0: "mail", 0xF0E7: "zap", 0xF0EA: "clipboard", 0xF0F3: "bell",
0xF11C: "keyboard", 0xF124: "navigation", 0xF15B: "file", 0xF1EB: "wifi",
# FontAwesome has five battery levels, Lucide four: 3/4 and 1/2 share one.
0xF240: "battery-full", 0xF241: "battery-medium", 0xF242: "battery-medium",
0xF243: "battery-low", 0xF244: "battery", 0xF287: "usb",
0xF293: "bluetooth", 0xF2ED: "trash-2", 0xF304: "pencil",
0xF55A: "delete", 0xF7C2: "card-sim", 0xF8A2: "corner-down-left",
}
# Launcher tiles, tab bar, chat chrome. Kept on Lucide's own codepoints; the
# firmware names them in ui_icons.h, which this list must stay in step with.
APP = [
"message-square", "user", "users", "at-sign", "map-pin", "map", "radio",
"signal", "activity", "audio-lines", "square-terminal", "monitor", "cast",
"sliders-horizontal", "settings", "house", "folder", "layout-grid",
"gamepad-2", "globe", "radar", "shield-check", "usb", "qr-code", "power",
"compass", "satellite", "wifi", "bluetooth", "lock", "bell", "bell-off",
"sun", "moon", "search", "arrow-up", "check", "check-check",
"chevron-left", "chevron-right", "x", "plus", "ellipsis", "hash", "mail",
"refresh-cw", "trash-2", "pencil", "zap", "battery-charging",
"thermometer", "gauge", "info", "link", "star", "keyboard", "file-text",
"store", "waves", "antenna", "route", "radio-tower", "crosshair",
"languages", "palette", "timer", "history", "copy", "eye", "smartphone",
"chart-no-axes-column", "message-square-text", "reply", "share-2",
"smile", "send-horizontal", "arrow-left", "battery-full", "battery-medium",
"battery-low", "battery", "image", "lock-open", "bell-ring", "circle-alert",
"arrow-down-wide-narrow", "user-plus", "list-checks", "ban",
]
FA_PERSON = {0xF007: "user", 0xF519: "radio-tower", 0xF0C0: "users"}
FA_ZOOM = {0xF002: "search"}
FA_SLEEP = {0xF185: "sun", 0xF186: "moon"}
FA_CC = {0xF023: "lock", 0xF0F3: "bell", 0xF1F6: "bell-off"}
def icon_map(codepoints, which):
"""target codepoint -> Lucide codepoint, for one set."""
def cp(name):
if name not in codepoints:
sys.exit(f"lucide_font.py: no Lucide icon named {name!r}")
return codepoints[name]
if which == "lv_symbols":
return {t: cp(n) for t, n in LV_SYMBOLS.items()}
if which == "app":
return {cp(n): cp(n) for n in APP}
named = {"person": FA_PERSON, "zoom": FA_ZOOM, "sleep": FA_SLEEP, "cc": FA_CC}
if which in named:
return {t: cp(n) for t, n in named[which].items()}
sys.exit(f"lucide_font.py: unknown icon set {which!r}")
def cmd_ranges(cp_path, which):
codepoints = json.load(open(cp_path, encoding="utf-8"))
out = []
for target, src in sorted(icon_map(codepoints, which).items()):
out += ["-r", f"0x{src:X}=>0x{target:X}" if src != target else f"0x{src:X}"]
print("\n".join(out))
def cmd_count(cp_path, which):
codepoints = json.load(open(cp_path, encoding="utf-8"))
print(len(icon_map(codepoints, which)))
def cmd_patch(path, icons, line_height, base_line, cap_height):
icons, line_height, base_line = int(icons), int(line_height), int(base_line)
cap = int(cap_height)
src = open(path, encoding="utf-8").read()
start = src.index("glyph_dsc[] = {")
end = src.index("};", start)
body = src[start:end]
entries = list(re.finditer(r"\{\.bitmap_index = [^}]*\}", body))
if icons > len(entries) - 1:
sys.exit(f"lucide_font.py: {path} has fewer glyphs than {icons} icons")
# Each Lucide drawing fills a different part of its 24-unit square, so one
# common shift leaves them scattered by a pixel or two (the status bar's
# battery sat 2 px above the Wi-Fi beside it). lv_font_conv crops every glyph
# to its ink, so placing the box itself puts the drawing exactly: its middle
# on the middle of the capitals, rounding half a pixel down.
pieces, last = [], 0
for m in entries[len(entries) - icons:]:
e = m.group(0)
box_h = int(re.search(r"\.box_h = (\d+)", e).group(1))
oy = (cap - box_h) // 2
pieces.append(body[last:m.start()])
pieces.append(re.sub(r"\.ofs_y = -?\d+", f".ofs_y = {oy}", e))
last = m.end()
pieces.append(body[last:])
src = src[:start] + "".join(pieces) + src[end:]
src, n1 = re.subn(r"\.line_height = \d+,", f".line_height = {line_height},", src, count=1)
src, n2 = re.subn(r"\.base_line = -?\d+,", f".base_line = {base_line},", src, count=1)
if n1 != 1 or n2 != 1:
sys.exit(f"lucide_font.py: no line metrics in {path}")
open(path, "w", encoding="utf-8").write(src)
def cmd_header(cp_path):
codepoints = json.load(open(cp_path, encoding="utf-8"))
print("// SPDX-License-Identifier: GPL-3.0-or-later")
print("// Generated by scripts/build/gen-touch-fonts.sh (lucide_font.py header).")
print("// The app icons in ui_icons_16 / ui_icons_20, as UTF-8 string literals:")
print("// Lucide 1.52.0, on Lucide's own codepoints.")
print("#pragma once")
print()
for name in APP:
c = codepoints[name]
utf8 = "".join(f"\\x{b:02X}" for b in chr(c).encode("utf-8"))
macro = "UI_ICON_" + re.sub(r"[^A-Z0-9]", "_", name.upper())
print(f'#define {macro:<32} "{utf8}" /* U+{c:04X} {name} */')
def glyph_box_h(src, cp):
"""box_h of codepoint cp in a generated font, or None when it has no such glyph."""
entries = re.findall(r"\{\.bitmap_index = [^}]*\}", src[src.index("glyph_dsc[] = {"):])
for m in re.finditer(r"\.range_start = (\d+), \.range_length = (\d+), "
r"\.glyph_id_start = (\d+),[^}]*?\.type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY", src):
start, length, gid = (int(v) for v in m.groups())
if start <= cp < start + length:
return int(re.search(r"\.box_h = (\d+)", entries[gid + cp - start]).group(1))
return None
def cmd_metrics(path):
src = open(path, encoding="utf-8").read()
lh = re.search(r"\.line_height = (\d+),", src).group(1)
bl = re.search(r"\.base_line = (-?\d+),", src).group(1)
cap = glyph_box_h(src, ord("H"))
if cap is None:
cap = glyph_box_h(src, ord("A"))
if cap is None:
sys.exit(f"lucide_font.py: no H or A in {path} to measure the capitals")
print(lh, bl, cap)
if __name__ == "__main__":
if len(sys.argv) >= 2 and sys.argv[1] == "ranges" and len(sys.argv) == 4:
cmd_ranges(sys.argv[2], sys.argv[3])
elif len(sys.argv) >= 2 and sys.argv[1] == "count" and len(sys.argv) == 4:
cmd_count(sys.argv[2], sys.argv[3])
elif len(sys.argv) >= 2 and sys.argv[1] == "patch" and len(sys.argv) == 7:
cmd_patch(*sys.argv[2:])
elif len(sys.argv) >= 2 and sys.argv[1] == "header" and len(sys.argv) == 3:
cmd_header(sys.argv[2])
elif len(sys.argv) >= 2 and sys.argv[1] == "metrics" and len(sys.argv) == 3:
cmd_metrics(sys.argv[2])
else:
sys.exit(__doc__)
+161
View File
@@ -0,0 +1,161 @@
#!/usr/bin/env bash
# Build every T-Display P4 release image and verify each one is what it claims.
#
# The P4 is not a PlatformIO env, so release.sh cannot build it: its bins are
# produced here and injected into the release tree afterwards. That used to be a
# handful of ad-hoc commands, which stopped being safe at EIGHT variants:
#
# panel AMOLED (RM69A10, default) or LCD (HI8561, WADA_P4_LCD=1)
# radio SX1262 (default) or LR2021 (WADA_P4_LR2021=1)
# C6 stack esp-hosted (default) or ESP-AT (WADA_P4_LEGACY_AT=1)
#
# The C6 axis is the dangerous one. Current V1 units ship the C6 with esp-hosted
# firmware; older units still carry the factory ESP-AT. Flash the wrong one and
# the C6 is unreachable -- which before b52c71f boot-looped the board, and still
# means no Wi-Fi and no BLE. The two builds are otherwise identical, so NOTHING
# about the binary tells you which it is except the artifact name it carries.
#
# TWO traps this script exists to close, both hit on 2026-10-05:
#
# 1. The CMake cache. Each variant has its own build dir, but a dir reused with
# different env vars keeps the OLD selection: a build asked for as hosted came
# out as ESP-AT, flag silently ignored. The cache is cleared per variant here.
# 2. Collecting from the wrong directory. build/tdisplay_p4 is only the AMOLED
# SX1262 variant; the others live in build/tdisplay_p4_lcd and friends.
# Copying the first one four times produces four identical "variants" that
# all look fine until someone flashes one.
#
# So every image is verified against the OTA name baked into it before it is
# accepted. That is the same string the device builds its self-update URL from,
# so if it disagrees with the filename the artifact is wrong in the way that
# matters -- a device would fetch the other variant's firmware. The T-Deck Max
# shipped exactly that bug (a Max self-updating to Pro firmware) and this table
# has been wrong three times. The check is cheap; a mismatch aborts the release.
#
# Usage:
# scripts/build/p4-release.sh beta_88 [outdir]
set -euo pipefail
TAG="${1:?usage: p4-release.sh <tag> [outdir]}"
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
OUT="${2:-$ROOT/out/p4_$TAG}"
P4="$ROOT/tdisplay_p4"
# esptool with ESP32-P4 support: the IDF env's, not whatever is on PATH.
IDFPY="$ROOT/tanmatsu/esp-idf-tools/python_env/idf5.5_py3.9_env/bin/python"
[ -x "$IDFPY" ] || { echo "ABORT: IDF python not found at $IDFPY" >&2; exit 1; }
mkdir -p "$OUT"
# name | build dir | env assignments ("-" = none)
VARIANTS=(
"wadamesh-tdisplay-p4|tdisplay_p4|-"
"wadamesh-tdisplay-p4-lr2021|tdisplay_p4_lr2021|WADA_P4_LR2021=1"
"wadamesh-tdisplay-p4-lcd|tdisplay_p4_lcd|WADA_P4_LCD=1"
"wadamesh-tdisplay-p4-lcd-lr2021|tdisplay_p4_lcd_lr2021|WADA_P4_LCD=1 WADA_P4_LR2021=1"
"wadamesh-tdisplay-p4-at|tdisplay_p4|WADA_P4_LEGACY_AT=1"
"wadamesh-tdisplay-p4-lr2021-at|tdisplay_p4_lr2021|WADA_P4_LR2021=1 WADA_P4_LEGACY_AT=1"
"wadamesh-tdisplay-p4-lcd-at|tdisplay_p4_lcd|WADA_P4_LCD=1 WADA_P4_LEGACY_AT=1"
"wadamesh-tdisplay-p4-lcd-lr2021-at|tdisplay_p4_lcd_lr2021|WADA_P4_LCD=1 WADA_P4_LR2021=1 WADA_P4_LEGACY_AT=1"
)
cd "$P4"
fail=0
for v in "${VARIANTS[@]}"; do
name="${v%%|*}"; rest="${v#*|}"
dir="${rest%%|*}"; envs="${rest#*|}"
echo "=== $name (build/$dir)"
# Trap 1: never inherit another variant's selection. Two dirs are shared by an
# esp-hosted and an ESP-AT build (the C6 axis does not change the dir name), so
# the previous occupant has to be cleared -- but ONLY by a real clean. Deleting
# CMakeCache.txt alone leaves CMakeFiles/ behind and the next configure dies in
# CMakeDetermineCCompiler with "No such file or directory". build.sh fullclean
# removes the tree properly and keeps the patched managed_components.
# A marker records what this dir last built, so re-running the same variant
# stays incremental instead of paying for a full rebuild every time.
# Two of these dirs are shared by an esp-hosted and an ESP-AT build, because the
# C6 axis does not change the dir name. The backend is read from the
# ENVIRONMENT at CMake CONFIGURE time (if(DEFINED ENV{WADA_P4_LEGACY_AT}) in
# main/CMakeLists.txt), and CMake does not reconfigure just because an env var
# changed -- so an incremental build in a dir that was configured the other way
# silently produces the OTHER variant, with no configure output to show for it.
# That is what made the first two runs of this script ship mislabelled images.
#
# So: always start these from nothing, and VERIFY the dir is gone rather than
# assuming the clean worked. The first version suppressed fullclean's output
# and trusted it; it had not run, and the only evidence was a missing configure
# line buried in a build log. A full rebuild per variant is the right price for
# a script that runs once per release.
if [ -d "build/$dir" ]; then
if [ "$envs" = "-" ]; then
env -u WADA_P4_LCD -u WADA_P4_LR2021 -u WADA_P4_LEGACY_AT ./build.sh fullclean >> "/tmp/p4rel_$name.clean.log" 2>&1 || true
else
env -u WADA_P4_LCD -u WADA_P4_LR2021 -u WADA_P4_LEGACY_AT $envs ./build.sh fullclean >> "/tmp/p4rel_$name.clean.log" 2>&1 || true
fi
fi
if [ -d "build/$dir" ]; then
echo " FAIL: build/$dir still present after fullclean; refusing to build on a stale tree"
echo " (see /tmp/p4rel_$name.clean.log)"
fail=$((fail+1)); continue
fi
# `env -u` every switch, then set only this variant's: an exported var in the
# caller's shell would otherwise leak in and silently change the build.
rc=0
if [ "$envs" = "-" ]; then
env -u WADA_P4_LCD -u WADA_P4_LR2021 -u WADA_P4_LEGACY_AT \
WADA_FW_TAG="$TAG" ./build.sh build > "/tmp/p4rel_$name.log" 2>&1 || rc=$?
else
env -u WADA_P4_LCD -u WADA_P4_LR2021 -u WADA_P4_LEGACY_AT \
$envs WADA_FW_TAG="$TAG" ./build.sh build > "/tmp/p4rel_$name.log" 2>&1 || rc=$?
fi
B="build/$dir"
app="$B/application.bin" # trap 2: this variant's dir, not the default one
# Check the EXIT CODE, not the log text. A failed CMake configure prints
# "Configuring incomplete, errors occurred!" and no "error:" line at all, so a
# grep for compiler errors waved it through -- and because the previous build's
# application.bin was still sitting in the dir, the next step happily read that
# instead. Only the id check downstream caught it.
if [ "$rc" -ne 0 ]; then
echo " FAIL: build exited $rc (see /tmp/p4rel_$name.log)"; fail=$((fail+1)); continue
fi
if [ ! -f "$app" ]; then
echo " FAIL: no application.bin (see /tmp/p4rel_$name.log)"; fail=$((fail+1)); continue
fi
# The verification that matters: what does the image say it is?
# NOTE: no `grep -q` and no `head` in these pipelines. Both exit as soon as
# they have what they need, which SIGPIPEs `strings`, and under `set -o
# pipefail` that non-zero status reads as "check failed" on a perfectly good
# image. Read the whole stream and decide afterwards.
ids="$(strings -a "$app" | grep -oE 'wadamesh-tdisplay-p4[a-z0-9-]*' | sort -u || true)"
got="$(printf '%s\n' "$ids" | sed -n '1p')"
if [ "$got" != "$name" ]; then
echo " FAIL: image identifies as '$got', expected '$name'"
echo " A device built from this would self-update to the WRONG variant."
fail=$((fail+1)); continue
fi
# And that it carries this release's tag, not a stale cached one.
tagged="$(strings -a "$app" | grep -cE "(^|[^0-9a-z])${TAG}([^0-9]|$)" || true)"
if [ "${tagged:-0}" = "0" ]; then
echo " FAIL: $TAG is not embedded (stale build dir?)"; fail=$((fail+1)); continue
fi
cp "$app" "$OUT/$name.bin"
"$IDFPY" -m esptool --chip esp32p4 merge_bin --flash_mode dio --flash_size 16MB --flash_freq 80m \
-o "$OUT/$name-merged.bin" \
0x2000 "$B/bootloader/bootloader.bin" \
0x8000 "$B/partition_table/partition-table.bin" \
0xf000 "$B/ota_data_initial.bin" \
0x20000 "$app" > /dev/null
echo " ok $(stat -f%z "$app") bytes id=$got"
done
echo
if [ "$fail" -ne 0 ]; then
echo "ABORT: $fail of ${#VARIANTS[@]} P4 variants failed. Nothing is safe to ship." >&2
exit 1
fi
echo "all ${#VARIANTS[@]} P4 variants built and verified -> $OUT"
echo "Copy both .bin and -merged.bin for each into releases/<CHANNEL>/$TAG/ AND latest[-beta]/."
+5
View File
@@ -53,10 +53,14 @@ while time.time() < deadline:
if b"WMTOUR START" in line:
started = True
print("tour started")
deadline = time.time() + 60 # from here on: give up only after 60 s without a frame
continue
if b"WMTOUR END" in line:
print("tour ended")
break
if line.startswith(b"DBG:"):
print(line.decode(errors="replace").rstrip()) # debug lines from a capture build
continue
m = hdr_re.search(line)
if not m:
continue
@@ -68,6 +72,7 @@ while time.time() < deadline:
continue
bmp = os.path.join(outdir, name + ".bmp")
png = os.path.join(outdir, name + ".png")
deadline = time.time() + 60 # the tour is still going
with open(bmp, "wb") as f:
f.write(data)
try:
+57 -2
View File
@@ -345,8 +345,49 @@ local function build_wada()
}
end
wada.fs.append = function(name, data) checkstr(name, "fs.append name"); checkstr(data, "fs.append data"); return true end
wada.fs.remove = function(name) checkstr(name, "fs.remove name"); return true end
-- wada.fs: a real in-memory filesystem, not a set of no-ops. append used to
-- throw the data away and read/write/list were missing outright, so any app
-- that wrote a file and read it back failed here while working perfectly on
-- device. That is what made wardrive 1.2 look like it had regressed the
-- emoji fix: it errored on a nil fs.read at the first flush, long before
-- anything was rendered, and the scenario reported the unrendered label.
-- Semantics follow LuaAppHost.cpp: read returns data plus the file's TOTAL
-- size so a caller can window a growing log, an offset at or past the end is
-- an empty string rather than nil, and a missing file is nil.
fsfiles = {}
wada.fs.write = function(name, data)
checkstr(name, "fs.write name"); checkstr(data, "fs.write data")
fsfiles[name] = data; return true
end
wada.fs.append = function(name, data)
checkstr(name, "fs.append name"); checkstr(data, "fs.append data")
fsfiles[name] = (fsfiles[name] or "") .. data; return true
end
wada.fs.read = function(name, off, want)
checkstr(name, "fs.read name")
local d = fsfiles[name]
if d == nil then return nil end
off = off or 0
if off < 0 then return nil end
local total = #d
if off >= total then return "", total end
want = want or (total - off)
if want < 0 then return nil end
return d:sub(off + 1, off + want), total
end
wada.fs.list = function()
local out, names = {}, {}
for n in pairs(fsfiles) do names[#names + 1] = n end
table.sort(names) -- deterministic: a hash order would make tests flaky
for _, n in ipairs(names) do out[#out + 1] = { name = n, size = #fsfiles[n] } end
return out
end
wada.fs.remove = function(name)
checkstr(name, "fs.remove name")
local had = fsfiles[name] ~= nil
fsfiles[name] = nil
return had
end
wada.store.get = function(k, d) assert(type(k) == "string"); local v = storekv[k]; if v == nil then return d end return v end
wada.store.set = function(k, v) assert(type(k) == "string", "store key must be a string")
@@ -2056,6 +2097,14 @@ scenarios.protreck_cost = function()
if app.on_close then guarded(BUDGET, app.on_close) end
end
-- True (and says so) when this app has no scenarios of its own. Returning a
-- truthy value makes the dispatch below pick an EMPTY list rather than running
-- someone else's tests against it.
local function UNTESTED_APPS()
io.write("== no scenarios for this app; nothing was tested\n")
return true
end
local order = APP_PATH:find("/sdscan/", 1, true)
and { "sdscan_real_m9", "sdscan_many", "sdscan_m9", "sdscan_layouts", "sdscan_paging_and_full", "sdscan_old_firmware", "sdscan_no_access",
"sdscan_no_card", "sdscan_remove_fails", "sdscan_portrait", "sdscan_cost" }
@@ -2071,6 +2120,12 @@ local order = APP_PATH:find("/sdscan/", 1, true)
and { "protreck_tdeck", "protreck_v4", "protreck_no_gps", "protreck_chrono", "protreck_timer", "protreck_alti", "protreck_cost" }
or APP_PATH:find("/ping/", 1, true)
and { "ping_tdeck", "ping_v4", "ping_no_contacts", "ping_round_trip", "ping_auto_reply", "ping_cost" }
-- The compass list below is gpscompass's own, NOT a default. An app with no
-- branch used to fall through to it and "fail" on tests written for a
-- different app -- six of the store apps do, so a full run came back red and
-- a real regression would not have stood out. Name them: they are untested,
-- which is worth saying out loud, but it is not a failure of the app.
or ((not APP_PATH:find("/gpscompass/", 1, true)) and UNTESTED_APPS() and {})
or { "declination", "align_nofix", "bearings_absolute", "m9", "r8", "v4", "pager", "pager_portrait_jumbo", "tanmatsu", "audio_api", "cost" }
for _, name in ipairs(order) do
if SCENARIO == "all" or SCENARIO == name then
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# Build and run every host test in test/.
#
# These are plain g++ binaries, no framework: each file documents its own build
# line in a comment and is compiled standalone. That is cheap and fast, but it
# also means nothing ever ran them all together -- so test_touch_prefs_schema
# sat broken for two schema versions (v64 appended two fields and its sums were
# not updated) without anyone noticing. A static_assert that does not compile is
# not a guard, and that particular one exists to catch the mid-struct insert
# that poisoned beta_44 and beta_57. One command, run it before a release.
#
# Usage: scripts/run-host-tests.sh
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
CXX="${CXX:-g++}"
STD="-std=c++17"
OUT="${TMPDIR:-/tmp}/wada-host-tests"
mkdir -p "$OUT"
# Tests needing more than `-I src`. Anything not listed builds with the default.
extra_flags() {
case "$1" in
# Its subject is a board variant header, not shared src/.
test_crowpanel_sd_codec) echo "-I variants/crowpanel_35" ;;
*) echo "" ;;
esac
}
# Not unit tests, with the reason. Kept visible rather than deleted: a silent
# skip list is how a suite quietly stops covering things.
skip_reason() {
case "$1" in
test_minimp3_decoder)
echo "takes an .mp3 path on argv; it is a decoder harness, not a unit test" ;;
test_reader_content)
# Fixing this means lifting ReaderContent out of UITask.cpp, which is a
# refactor of a 60k-line file, not a test change. Left failing-by-design
# and named, so it is a known gap rather than a mystery.
echo "subject (ReaderContent::htmlToText) is defined inside UITask.cpp, which cannot build on the host" ;;
*) echo "" ;;
esac
}
pass=0; fail=0; skip=0; failed=""
for t in test/test_*.cpp; do
n="$(basename "$t" .cpp)"
reason="$(skip_reason "$n")"
if [ -n "$reason" ]; then
printf 'SKIP %-34s %s\n' "$n" "$reason"; skip=$((skip+1)); continue
fi
# shellcheck disable=SC2046
if ! $CXX $STD -I src $(extra_flags "$n") -o "$OUT/$n" "$t" > "$OUT/$n.build" 2>&1; then
printf 'BUILD %-34s %s\n' "$n" "see $OUT/$n.build"
grep -m2 -E 'error:|Undefined' "$OUT/$n.build" | sed 's/^/ /'
failed="$failed $n"; fail=$((fail+1)); continue
fi
if ! "$OUT/$n" > "$OUT/$n.run" 2>&1; then
printf 'FAIL %-34s %s\n' "$n" "see $OUT/$n.run"
tail -3 "$OUT/$n.run" | sed 's/^/ /'
failed="$failed $n"; fail=$((fail+1)); continue
fi
printf 'ok %s\n' "$n"
pass=$((pass+1))
done
echo
echo "pass=$pass fail=$fail skip=$skip"
[ "$fail" -eq 0 ] || { echo "failing:$failed"; exit 1; }
+34 -4
View File
@@ -2969,7 +2969,11 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
// Add the accepted named advert to the session-local recent-heard cache.
// A monotonic sequence, not the remote advert timestamp or local RTC, defines
// order so an unset/adjusted clock cannot make a fresh node look stale.
if (path_len <= sizeof(AdvertPath::path) && (path_len == 0 || path)) {
// path_len is the packed byte (hop count in the low 6 bits, hash size above),
// not a byte count. Comparing it to the buffer size rejected every advert that
// came over 2- or 3-byte path hashes (0x41 and up), so those nodes never made
// the recently-heard table at all.
if (mesh::Packet::isValidPathLen(path_len) && (path_len == 0 || path)) {
const uint32_t recv_timestamp = getRTCClock()->getCurrentTime();
#if defined(ESP32)
portENTER_CRITICAL(&advert_paths_mux);
@@ -3002,7 +3006,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
p->type = contact.type;
p->used = true;
p->path_len = path_len;
if (path_len) memcpy(p->path, path, path_len);
if (path_len) mesh::Packet::writePath(p->path, path, path_len);
#if defined(ESP32)
portEXIT_CRITICAL(&advert_paths_mux);
#endif
@@ -3058,6 +3062,30 @@ int MyMesh::getRecentlyHeard(RecentlyHeardName dest[], int max_num) {
return max_num;
}
bool MyMesh::getAdvertPath(const uint8_t* pubkey_prefix, int prefix_len, uint8_t& path_len,
uint8_t* path, size_t path_cap) {
if (!pubkey_prefix || prefix_len <= 0) return false;
if (prefix_len > (int)sizeof(AdvertPath::pubkey_prefix)) prefix_len = sizeof(AdvertPath::pubkey_prefix);
bool found = false;
#if defined(ESP32)
portENTER_CRITICAL(&advert_paths_mux);
#endif
for (int i = 0; i < ADVERT_PATH_TABLE_SIZE; i++) {
const AdvertPath& p = advert_paths[i];
if (!p.used || memcmp(p.pubkey_prefix, pubkey_prefix, (size_t)prefix_len) != 0) continue;
const size_t bytes = (size_t)(p.path_len & 63) * (size_t)((p.path_len >> 6) + 1);
if (bytes > path_cap || bytes > sizeof(p.path) || (bytes && !path)) break;
path_len = p.path_len;
if (bytes) memcpy(path, p.path, bytes);
found = true;
break;
}
#if defined(ESP32)
portEXIT_CRITICAL(&advert_paths_mux);
#endif
return found;
}
void MyMesh::onContactPathUpdated(const ContactInfo &contact) {
out_frame[0] = PUSH_CODE_PATH_UPDATED;
memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE);
@@ -5318,11 +5346,13 @@ void MyMesh::handleCmdFrame(size_t len) {
portEXIT_CRITICAL(&advert_paths_mux);
#endif
if (has_found) {
// The packed length goes out as stored (identical to a byte count for
// 1-byte hashes); the path itself is hop count x hash size bytes.
out_frame[0] = RESP_CODE_ADVERT_PATH;
memcpy(&out_frame[1], &found.recv_timestamp, 4);
out_frame[5] = found.path_len;
memcpy(&out_frame[6], found.path, found.path_len);
_serial->writeFrame(out_frame, 6 + found.path_len);
const size_t path_bytes = mesh::Packet::writePath(&out_frame[6], found.path, found.path_len);
_serial->writeFrame(out_frame, 6 + path_bytes);
} else {
writeErrFrame(ERR_CODE_NOT_FOUND);
}
+5
View File
@@ -238,6 +238,11 @@ public:
void enterCLIRescue();
int getRecentlyHeard(RecentlyHeardName dest[], int max_num);
// The route the last advert from this node took to reach us, for the recently
// heard only: repeater hashes with the node's own neighbour first and ours
// last, `path_len` the packed hop-count/hash-size byte. False when unknown.
bool getAdvertPath(const uint8_t* pubkey_prefix, int prefix_len, uint8_t& path_len,
uint8_t* path, size_t path_cap);
bool uiIsMeshcomodRecipient(const uint8_t* pub_key_prefix_6) const {
return isMeshcomodRecipient(pub_key_prefix_6);
}
+42
View File
@@ -258,6 +258,48 @@ bool HardwareRtcClock::begin(TwoWire& wire, Chip chip) {
return true;
}
// Pull the software clock back to the chip, at most once an hour.
//
// #583: a Pager left powered loses ~30 min/day. getCurrentTime() deliberately
// never touches I2C (see the header), so nothing was ever correcting the ESP32
// clock's drift while an accurate crystal sat on the bus unread. This is the
// cheapest correction that keeps that rule intact: one validated read an hour.
//
// Only runs when a chip was ADOPTED at boot. If the boot read was refused (no
// ACK, stopped oscillator, implausible date) the chip is not a trustworthy
// source and must not be allowed to drag a good software clock backwards.
// readHardware() re-applies every one of those checks on each read, so a chip
// that fails later is ignored rather than believed.
//
// An external sync (NTP, GPS, companion) goes through setCurrentTime(), which
// writes through to the chip, so the two agree straight afterwards and this
// cannot fight it.
void HardwareRtcClock::resyncFromChip() {
if (_status != Status::Ok || !_present) return;
const uint32_t now_ms = millis();
// millis() wraps at ~49 days. Comparing as signed makes the wrap a no-op
// instead of parking the next resync 49 days out.
if (_next_resync_ms && (int32_t)(now_ms - _next_resync_ms) < 0) return;
_next_resync_ms = now_ms + kResyncIntervalMs;
uint32_t hw = 0;
Status why = Status::NotProbed;
if (!readHardware(hw, why)) return; // transient bus trouble: try again next hour
const uint32_t sw = _sw.getCurrentTime();
const int32_t delta = (int32_t)(hw - sw);
const int32_t mag = delta < 0 ? -delta : delta;
if (mag < kResyncMinDeltaS) return; // agreement within a couple of seconds: leave it alone
// Correct the software clock ONLY. Deliberately not setCurrentTime(): that
// would write the chip's own value back to the chip, and a read-back failure
// would then clear _write_ok and make a healthy retention path look broken.
_sw.setCurrentTime(hw);
Serial.printf("[rtc] resynced from %s: software clock was %+ld s off\n",
chipName(), (long)-delta);
}
void HardwareRtcClock::setCurrentTime(uint32_t time) {
_sw.setCurrentTime(time); // system clock first: the UI reads that
if (!_present) return;
+18 -1
View File
@@ -86,7 +86,15 @@ public:
// bootstrap). ClockFloorRTC has already range-checked the value.
void setCurrentTime(uint32_t time) override;
void tick() override { _sw.tick(); }
// The software clock free-runs between boots, and on some boards it runs
// SLOW: jcyrio measured ~30 min lost per day on a T-LoRa Pager that stayed
// powered the whole time (#583), which is the ~2% you get from the ESP32's
// internal RC oscillator across light sleep. Meanwhile an accurate crystal is
// sitting on the bus being ignored, because the shape note above (correctly)
// keeps I2C out of getCurrentTime(), which runs on every message, UI tick and
// advert. Once an hour is not that hot path. Re-read the chip here and pull
// the software clock back to it.
void tick() override { _sw.tick(); resyncFromChip(); }
bool present() const { return _present; } // a real, identified chip answered
Status status() const { return _status; } // why the boot read was/was not adopted
@@ -102,6 +110,14 @@ public:
bool readHardware(uint32_t& out_epoch, Status& why);
private:
// Rate-limited correction of the software clock from the chip. No-op unless a
// chip was adopted at boot. See tick().
void resyncFromChip();
// Hourly is far more often than a crystal needs, and far less often than the
// drift that prompted it (~30 min/day = ~75 s per hour, well over the floor).
static constexpr uint32_t kResyncIntervalMs = 60UL * 60UL * 1000UL;
static constexpr int32_t kResyncMinDeltaS = 2;
bool writeHardware(uint32_t epoch);
uint8_t timeBase() const; // first time register: 0x02 (8563) / 0x04 (85063) / 0x00 (DS3231)
uint8_t i2cAddr() const; // 0x51 for both PCF parts, 0x68 for the DS3231
@@ -117,4 +133,5 @@ private:
Status _status = Status::NotProbed;
bool _present = false;
bool _write_ok = false;
uint32_t _next_resync_ms = 0; // millis() of the next chip re-read (see tick)
};
+6 -2
View File
@@ -9,7 +9,7 @@
namespace TouchPrefsSchema {
static constexpr uint16_t MAGIC = 0x5743; // 'WC' (WadaCfg)
static constexpr uint8_t CURRENT_VERSION = 65; // v49: fem_lna default flip on the V4-R8; v50: map tile z/x/y line off by default (no new fields); v51: console_mode (boot into the text console; new trailing field, default OFF); v52: console_monitor (show incoming messages in the console, default ON); v54: boot_wifi_time + boot_wifi_open (cold-boot saved-Wi-Fi time sync, #383; both OFF); v55: loud_alerts (resonant-pitch chime, #388; OFF); v56: theme_mode (Night/Day, with high-contrast variants, #296/#544; default Night); v57: gps_fuzz_m (displace the advertised position, #399; 0 = off); v58: Attaky notification blink enable + room/DM color indexes (#423); v59: telem_loc_exact (answer position requests from chosen contacts with the REAL fix instead of the advert displacement; 0 = keep it displaced); v60: home_key_keeps_drawer (#491; default OFF); v61: ble_kbd_* (Bluetooth serves a keyboard instead of the phone app, its layout and the paired keyboard; default phone app); v62: ble_kbd_back (a second key that acts as Back; 0 = only Esc); v63: V4-R8 ui_scale reset before exposing font-only presets (#536); v64: report_ping + report_done_n (beta test reports: the opt-in install count and the tag this device already reported on; both default 0); v65: ThinkNode M9 ui_scale reset before exposing font-only presets, the same one-time correction v63 made for the V4-R8 (no new field)
static constexpr uint8_t CURRENT_VERSION = 70; // v49: fem_lna default flip on the V4-R8; v50: map tile z/x/y line off by default (no new fields); v51: console_mode (boot into the text console; new trailing field, default OFF); v52: console_monitor (show incoming messages in the console, default ON); v54: boot_wifi_time + boot_wifi_open (cold-boot saved-Wi-Fi time sync, #383; both OFF); v55: loud_alerts (resonant-pitch chime, #388; OFF); v56: theme_mode (Night/Day, with high-contrast variants, #296/#544; default Night); v57: gps_fuzz_m (displace the advertised position, #399; 0 = off); v58: Attaky notification blink enable + room/DM color indexes (#423); v59: telem_loc_exact (answer position requests from chosen contacts with the REAL fix instead of the advert displacement; 0 = keep it displaced); v60: home_key_keeps_drawer (#491; default OFF); v61: ble_kbd_* (Bluetooth serves a keyboard instead of the phone app, its layout and the paired keyboard; default phone app); v62: ble_kbd_back (a second key that acts as Back; 0 = only Esc); v63: V4-R8 ui_scale reset before exposing font-only presets (#536); v64: report_ping + report_done_n (beta test reports: the opt-in install count and the tag this device already reported on; both default 0); v65: ThinkNode M9 ui_scale reset before exposing font-only presets, the same one-time correction v63 made for the V4-R8 (no new field); v66: condense_nav (landscape nav rail + icon-only Home launchers, #592; default OFF); v67: T-Pager keyboard navigation (#591): kbd_nav ON and its own key bindings, a one-time reset on that board only (no new field); v68: Attaky Orientation setting: ui_rotation reset once to its landscape default, on that board only (no new field); v69: T-Deck trackball mode: tb_nav holds 0 cursor / 1 D-pad nav / 2 scroll, and scroll is the T-Deck default (a one-time flip of cursor installs; no new field); v70: T-Deck ui_scale reset before exposing the font-only text sizes, as v63/v65 did for the V4-R8 and M9 (no new field)
static constexpr uint8_t BROKEN_MID_INSERT_VERSION = 44;
// Persisted byte layout. New fields must be appended at the end: older blobs
@@ -136,6 +136,10 @@ struct __attribute__((packed)) Config {
// page can say so rather than asking twice.
uint8_t report_ping;
uint16_t report_done_n;
// v66 (#592): Condense Nav. In landscape the main nav bar moves from the
// bottom to a rail on the right edge and Home's launcher column shrinks to
// icons. Default OFF; portrait layouts and the M9 ignore it.
uint8_t condense_nav;
};
static constexpr size_t HEADER_SIZE = offsetof(Config, bright);
@@ -151,7 +155,7 @@ static_assert(offsetof(Config, web_mirror) == offsetof(Config, rx_queue) + sizeo
// whichever board is using the file backend. 163 bytes today.
static_assert(sizeof(Config) <= 2048,
"Config exceeds the SdNvsPrefs value cap; prefs would silently stop saving");
static_assert(offsetof(Config, report_done_n) + sizeof(Config::report_done_n) == sizeof(Config),
static_assert(offsetof(Config, condense_nav) + sizeof(Config::condense_nav) == sizeof(Config),
"new preference fields must remain trailing");
// lang_file must stay immediately before the tail, or a v48-era blob overlays
// onto the wrong bytes. Checking both ends means the next person to append is
+212 -15
View File
@@ -9,6 +9,8 @@
#include <Preferences.h>
#include <SPIFFS.h>
#include "esp_system.h" // esp_random()
#include "mbedtls/md.h" // SHA-256 for the unlock PIN
#include <stddef.h> // offsetof
#include <string.h> // memcpy
@@ -59,6 +61,8 @@ static const uint8_t DEFAULT_SIG_PROBE_EN = 1; // signal discover
static const uint16_t DEFAULT_SIG_POLL_MIN = 5; // minutes between probes
#if defined(HAS_CROWPANEL_35)
static const uint8_t DEFAULT_UI_ROTATION = 3; // upright ILI9488 landscape
#elif defined(ATTAKY_MESH_SERIES)
static const uint8_t DEFAULT_UI_ROTATION = 1; // landscape (LV_DISP_ROT_90), as before it was a setting
#else
static const uint8_t DEFAULT_UI_ROTATION = 0;
#endif
@@ -66,6 +70,17 @@ static const uint8_t DEFAULT_UI_ROTATION = 0;
using TouchCfg = TouchPrefsSchema::Config;
static TouchCfg s_cfg;
#if defined(TLORA_PAGER)
// The Pager's keyboard-navigation keys (#591): I up, K down, J left, L right, no
// Select/Back letter (Enter and Backspace-hold do those), U/O scroll. Clear of the
// Pager's fixed letters (Q/E slider nudge, W/A/X/D map pan) and of its tab keys.
static void touchPrefsPagerNavKeyDefaults(TouchCfg& c) {
const char* d = "ikjl";
for (int i = 0; i < 6; i++) c.nav_dir_keys[i] = (uint8_t)(i < 4 ? d[i] : 0);
c.nav_scroll_keys[0] = 'u';
c.nav_scroll_keys[1] = 'o';
}
#endif
static bool s_cfg_loaded = false;
// Legacy per-key names — only referenced by the one-time migration below.
@@ -121,17 +136,19 @@ static void cfgSetDefaults(TouchCfg& c) {
c.map_show_tilexyz = 0; // v50: the "z12 12/2105/1376" tile-path line is developer clutter on the map; opt-in via Map options
c.map_show_contacts = 1;
c.app_grid_large = 0; // default: compact app grid (T-Deck 4 cols / V4 3 cols)
#if defined(TLORA_PAGER) || defined(HELTEC_LORA_V4_R8) || defined(HAS_THINKNODE_M9)
#if defined(TLORA_PAGER) || defined(HELTEC_LORA_V4_R8) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911)
c.ui_scale = 0; // Font-only presets start at the existing typography.
#else
c.ui_scale = 1; // large-screen boards keep their existing 150% default
#endif
#if defined(HAS_TANMATSU)
c.kbd_nav = 1; // Tanmatsu: no touchscreen — keyboard nav is the only input, always on
#elif defined(TLORA_PAGER)
c.kbd_nav = 1; // Pager (#591): letter navigation on by default (I/K/J/L, U/O)
#else
c.kbd_nav = 0; // T-Deck / V4: keyboard navigation OFF by default (opt-in; persists once toggled on)
#endif
c.tb_nav = 0; // T-Deck trackball: soft-cursor by default. D-pad UI nav is EXPERIMENTAL (opt-in)
c.tb_nav = 2; // T-Deck trackball mode: 2 = scroll the screen (default), 0 = soft cursor, 1 = D-pad UI nav (experimental)
c.scope_direct = 0; // OFF: direct/login floods stay unscoped (cross-region safe). Opt-in per issue #64.
#if defined(HELTEC_LORA_V4_R8)
c.fem_lna = 1; // ON (v49): the V4-R8 is a V4.3.1-generation board with the KCT8103L FEM, whose
@@ -145,6 +162,7 @@ static void cfgSetDefaults(TouchCfg& c) {
c.local_adv_min = 0; // OFF: no periodic zero-hop self-advert
c.beta_updates = 0; // OFF: stable update channel (opt-in to beta/test firmware)
c.report_ping = 0; // OFF: no anonymous install count until asked for (v64)
c.condense_nav = 0; // OFF: bottom nav bar + labelled Home launchers (v66, #592)
c.report_done_n = 0; // no beta reported on yet
c.boot_advert = 0; // OFF: no automatic advert on boot — opt-in (#76)
c.console_mode = 0; // OFF: boot into the graphical UI (CONSOLE_MODE.md)
@@ -173,16 +191,24 @@ static void cfgSetDefaults(TouchCfg& c) {
c.app_hide = (1u << 12); // APPHIDE_MQTT: the MQTT bridge starts hidden (experimental + privacy)
memset(c.lang_file, 0, sizeof c.lang_file); // no file language: built-in ui_lang column
c.sleep_idle = 0; // default: idle light-sleep OFF
#if defined(TLORA_PAGER)
{ const char* d = "mchas"; for (int i = 0; i < 5; i++) c.nav_keys[i] = (uint8_t)d[i]; } // Pager tab mnemonics M/C/H/A/S (#591)
#else
{ const char* d = "ertui"; for (int i = 0; i < 5; i++) c.nav_keys[i] = (uint8_t)d[i]; } // default tab hotkeys E/R/T/U/I
#endif
c.map_zoom_buttons = 0; // default: map zoom = slider
#if defined(HAS_TANMATSU)
{ const char* d = "wxads"; for (int i = 0; i < 6; i++) c.nav_dir_keys[i] = (uint8_t)d[i]; } // Tanmatsu: W up/X down/A left/D right/S select; no Back letter (Esc/F-key), d[5]='\0'
#elif defined(TLORA_PAGER)
touchPrefsPagerNavKeyDefaults(c); // Pager (#591): I/K/J/L, no Select/Back letter, U/O scroll
#else
{ const char* d = "wzadsq"; for (int i = 0; i < 6; i++) c.nav_dir_keys[i] = (uint8_t)d[i]; } // default W/Z/A/D/S/Q
#endif
c.home_is_drawer = 0; // default: Home = Commander screen
#if defined(HAS_TANMATSU)
c.nav_scroll_keys[0] = 'f'; c.nav_scroll_keys[1] = 'v'; // Tanmatsu scroll-up F / scroll-down V
#elif defined(TLORA_PAGER)
// set with the direction keys above (touchPrefsPagerNavKeyDefaults)
#else
c.nav_scroll_keys[0] = 'f'; c.nav_scroll_keys[1] = 'c'; // default scroll-up F / scroll-down C
#endif
@@ -263,12 +289,34 @@ static void cfgLoadOrMigrate() {
#endif
// v50: map tile z/x/y overlay line OFF by default (one-time flip; the Map-options toggle persists afterwards).
if (stored_version < 50) s_cfg.map_show_tilexyz = 0;
// v69: the trackball gains a scroll mode and it is the T-Deck's default. Installs on
// the old default (the cursor, 0) move to it once; the D-pad choice (1) is kept, and
// the cursor stays one tap away in Settings.
if (stored_version < 69 && s_cfg.tb_nav == 0) s_cfg.tb_nav = 2;
if (stored_version < 26) s_cfg.msg_flash = 0;
if (stored_version < 27) { s_cfg.flood_adv_hrs = 0; s_cfg.local_adv_min = 0; }
if (stored_version < 28) s_cfg.beta_updates = 0;
// v64 new trailing fields. Forced off rather than inherited: a garbage 1
// would start sending an install count nobody opted into.
if (stored_version < 64) { s_cfg.report_ping = 0; s_cfg.report_done_n = 0; }
#if defined(ATTAKY_MESH_SERIES)
// v68: the Attaky gains the Orientation setting. Until now the UI ignored the
// stored value and always ran landscape, so whatever is stored (usually the old
// portrait default) was never seen. Start everyone on landscape once, as before.
if (stored_version < 68) s_cfg.ui_rotation = DEFAULT_UI_ROTATION;
#endif
#if defined(TLORA_PAGER)
// v67 (#591): the Pager gains the T-Deck's keyboard navigation. Its kbd_nav and key
// bindings were never shown on this board, so the stored values are just the T-Deck
// defaults (W/Z/A/D..., E/R/T/U/I, nav off), which collide with the Pager's own
// letters. Put this board's defaults in once; later choices persist. No new field.
if (stored_version < 67) {
s_cfg.kbd_nav = 1;
{ const char* d = "mchas"; for (int i = 0; i < 5; i++) s_cfg.nav_keys[i] = (uint8_t)d[i]; }
touchPrefsPagerNavKeyDefaults(s_cfg);
}
#endif
if (stored_version < 66) s_cfg.condense_nav = 0; // v66 new trailing field: never stored before
if (stored_version < 29 && s_cfg.ui_scale == 0) s_cfg.ui_scale = 1; // bump old 100% default -> Large (150%)
if (stored_version < 30) s_cfg.boot_advert = 0; // #76 new trailing field: advert-on-boot off by default
// v51 new trailing field. Anything older than 51 never stored it, so it
@@ -305,6 +353,12 @@ static void cfgLoadOrMigrate() {
// so without this every M9 in the field would come back from the update
// rendering Large text nobody asked for.
if (stored_version < 65) s_cfg.ui_scale = 0;
#endif
#if defined(HAS_TDECK_GT911)
// And the T-Deck in v70: since v29 the field has held the large-screen
// boards' Large default, which this board never applied. Reset it once,
// so the update that shows the selector does not enlarge anyone's text.
if (stored_version < 70) s_cfg.ui_scale = 0;
#endif
if (stored_version < 58) {
s_cfg.attaky_notify_enabled = 0;
@@ -1146,11 +1200,18 @@ bool touchPrefsSetKbdNav(bool on) {
}
bool touchPrefsGetTbNav() {
if (!s_begun) touchPrefsBegin();
return s_cfg.tb_nav != 0;
return s_cfg.tb_nav == TB_MODE_NAV;
}
bool touchPrefsSetTbNav(bool on) {
return touchPrefsSetTbMode(on ? TB_MODE_NAV : TB_MODE_CURSOR);
}
uint8_t touchPrefsGetTbMode() {
if (!s_begun) touchPrefsBegin();
s_cfg.tb_nav = on ? 1 : 0;
return s_cfg.tb_nav <= TB_MODE_SCROLL ? s_cfg.tb_nav : (uint8_t)TB_MODE_SCROLL;
}
bool touchPrefsSetTbMode(uint8_t mode) {
if (!s_begun) touchPrefsBegin();
s_cfg.tb_nav = mode <= TB_MODE_SCROLL ? mode : (uint8_t)TB_MODE_SCROLL;
return cfgFlush();
}
@@ -1409,6 +1470,17 @@ bool touchPrefsSetReportPing(bool on) {
s_cfg.report_ping = on ? 1 : 0;
return cfgFlush();
}
// Condense Nav (v66, #592): landscape nav rail + icon-only Home launchers.
// Read once when the UI is built; changing it restarts.
bool touchPrefsGetCondenseNav() {
if (!s_begun) touchPrefsBegin();
return s_cfg.condense_nav != 0;
}
bool touchPrefsSetCondenseNav(bool on) {
if (!s_begun) touchPrefsBegin();
s_cfg.condense_nav = on ? 1 : 0;
return cfgFlush();
}
uint16_t touchPrefsGetReportedBeta() {
if (!s_begun) touchPrefsBegin();
return s_cfg.report_done_n;
@@ -1454,12 +1526,19 @@ bool touchPrefsSetNavDirKey(int idx, uint8_t ch) {
return cfgFlush();
}
static void prefsPutUChar(const char* key, uint8_t v); // standalone keys (defined below)
// Since the launcher redesign Home opens on the app grid, unless the user has
// picked the command centre SINCE then (standalone key "home_cmdr"). The config
// field below still records the choice, but it held everyone's pre-redesign
// default of "command centre", so it no longer decides what Home shows.
bool touchPrefsGetHomeIsDrawer() {
if (!s_begun) touchPrefsBegin();
return s_cfg.home_is_drawer != 0;
return s_prefs.getUChar("home_cmdr", 0) == 0;
}
bool touchPrefsSetHomeIsDrawer(bool on) {
if (!s_begun) touchPrefsBegin();
prefsPutUChar("home_cmdr", on ? 0 : 1);
s_cfg.home_is_drawer = on ? 1 : 0;
return cfgFlush();
}
@@ -1637,12 +1716,39 @@ uint8_t touchPrefsGetUiRotation() {
return (s_cfg_loaded && r <= 3) ? r : DEFAULT_UI_ROTATION;
}
// The boot logo is painted before SPIFFS/SD mount, so it cannot see file-backed
// settings. Keep a raw-NVS copy of the rotation for it. Best effort: Launcher
// installs without usable NVS fall back to touchPrefsGetUiRotation().
static const char* KEY_BOOT_UI_ROTATION = "boot_rot";
static void bootUiRotationMirror(uint8_t rot) {
Preferences nvs;
if (!nvs.begin(TOUCH_NS, false)) return;
if (!nvs.isKey(KEY_BOOT_UI_ROTATION) || nvs.getUChar(KEY_BOOT_UI_ROTATION, 0xFF) != rot)
nvs.putUChar(KEY_BOOT_UI_ROTATION, rot);
nvs.end();
}
uint8_t touchPrefsGetBootUiRotation() {
Preferences nvs;
uint8_t r = 0xFF;
if (nvs.begin(TOUCH_NS, true)) {
if (nvs.isKey(KEY_BOOT_UI_ROTATION)) r = nvs.getUChar(KEY_BOOT_UI_ROTATION, 0xFF);
nvs.end();
}
return (r <= 3) ? r : touchPrefsGetUiRotation();
}
void touchPrefsSyncBootUiRotation() {
bootUiRotationMirror(touchPrefsGetUiRotation());
}
bool touchPrefsSetUiRotation(uint8_t rot) {
if (rot > 3) rot = 0;
if (!s_begun) touchPrefsBegin();
const uint8_t previous = s_cfg.ui_rotation;
s_cfg.ui_rotation = rot;
if (cfgFlush()) return true;
if (cfgFlush()) { bootUiRotationMirror(rot); return true; }
s_cfg.ui_rotation = previous;
return false;
}
@@ -2182,6 +2288,95 @@ bool touchPrefsGetEdgeScroll() { if (!s_begun) touchPrefsBegin(); return s_
void touchPrefsSetEdgeScroll(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("tb_edgesc", on ? 1 : 0); }
bool touchPrefsGetLockOnScreenOff() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("lock_off", 0) != 0; }
void touchPrefsSetLockOnScreenOff(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("lock_off", on ? 1 : 0); }
bool touchPrefsGetScreensaver() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("ssaver", 1) != 0; }
void touchPrefsSetScreensaver(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("ssaver", on ? 1 : 0); }
// ---- Unlock PIN ----------------------------------------------------------------
// Stored as an 8-byte random salt and SHA-256(salt || PIN), never the PIN itself.
// Four characters from 0-9 and A-Z, letters folded to upper case. (mbedtls_md, as
// MqttBridge uses: mbedtls_sha256's name differs between mbedTLS 2.x and 3.x.)
static const char* KEY_PIN_SALT = "pin_salt";
static const char* KEY_PIN_HASH = "pin_hash";
static bool pinNormalise(const char* in, char out[TOUCH_PIN_LEN]) {
if (!in) return false;
for (int i = 0; i < TOUCH_PIN_LEN; ++i) {
char c = in[i];
if (c >= 'a' && c <= 'z') c = (char)(c - 'a' + 'A');
if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'))) return false;
out[i] = c;
}
return in[TOUCH_PIN_LEN] == '\0';
}
static bool pinDigest(const uint8_t salt[8], const char pin[TOUCH_PIN_LEN], uint8_t out[32]) {
const mbedtls_md_info_t* md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (!md) return false;
uint8_t buf[8 + TOUCH_PIN_LEN];
memcpy(buf, salt, 8);
memcpy(buf + 8, pin, TOUCH_PIN_LEN);
return mbedtls_md(md, buf, sizeof buf, out) == 0;
}
bool touchPrefsPinIsSet() {
if (!s_begun) touchPrefsBegin();
return s_prefs.isKey(KEY_PIN_HASH) && s_prefs.isKey(KEY_PIN_SALT);
}
bool touchPrefsPinCheck(const char* pin) {
if (!s_begun) touchPrefsBegin();
char p[TOUCH_PIN_LEN];
if (!pinNormalise(pin, p) || !touchPrefsPinIsSet()) return false;
uint8_t salt[8], want[32], got[32];
if (s_prefs.getBytes(KEY_PIN_SALT, salt, sizeof salt) != sizeof salt) return false;
if (s_prefs.getBytes(KEY_PIN_HASH, want, sizeof want) != sizeof want) return false;
if (!pinDigest(salt, p, got)) return false;
uint8_t diff = 0;
for (int i = 0; i < 32; ++i) diff |= (uint8_t)(want[i] ^ got[i]);
return diff == 0;
}
bool touchPrefsPinSet(const char* pin) {
if (!s_begun) touchPrefsBegin();
uint8_t salt[8], hash[32];
const bool clear = !pin || !pin[0];
if (!clear) {
char p[TOUCH_PIN_LEN];
if (!pinNormalise(pin, p)) return false;
const uint32_t r0 = esp_random(), r1 = esp_random();
memcpy(salt, &r0, 4);
memcpy(salt + 4, &r1, 4);
if (!pinDigest(salt, p, hash)) return false;
}
// The namespace stays open read-only between writes; reopen it for this one, as
// prefsPutUChar does (a write on the read-only handle fails without a word).
s_prefs.end();
if (!s_prefs.begin(TOUCH_NS, false)) { s_begun = s_prefs.begin(TOUCH_NS, true); return false; }
bool ok;
if (clear) {
if (s_prefs.isKey(KEY_PIN_HASH)) s_prefs.remove(KEY_PIN_HASH);
if (s_prefs.isKey(KEY_PIN_SALT)) s_prefs.remove(KEY_PIN_SALT);
ok = !s_prefs.isKey(KEY_PIN_HASH) && !s_prefs.isKey(KEY_PIN_SALT);
} else {
ok = s_prefs.putBytes(KEY_PIN_SALT, salt, sizeof salt) == sizeof salt &&
s_prefs.putBytes(KEY_PIN_HASH, hash, sizeof hash) == sizeof hash;
}
s_prefs.end();
s_begun = s_prefs.begin(TOUCH_NS, true);
return ok && touchPrefsPinIsSet() == !clear;
}
uint8_t touchPrefsPinFails() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("pin_fail", 0); }
void touchPrefsSetPinFails(uint8_t n) {
if (!s_begun) touchPrefsBegin();
if (n == touchPrefsPinFails()) return; // a match on a clean count writes nothing
prefsPutUChar("pin_fail", n);
}
uint8_t touchPrefsGetScreensaverBatterySecs() {
if (!s_begun) touchPrefsBegin();
const uint8_t v = s_prefs.getUChar("ssaver_bat", 15); // absent until chosen: 15 s
return v > 60 ? 60 : v;
}
void touchPrefsSetScreensaverBatterySecs(uint8_t secs) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("ssaver_bat", secs > 60 ? 60 : secs); }
bool touchPrefsGetScreensaverAlwaysOnPower() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("ssaver_pwr", 1) != 0; }
void touchPrefsSetScreensaverAlwaysOnPower(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("ssaver_pwr", on ? 1 : 0); }
bool touchPrefsGetLockPreviews() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("lock_prev", 1) != 0; }
void touchPrefsSetLockPreviews(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("lock_prev", on ? 1 : 0); }
bool touchPrefsGetGlanceWhenLocked() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("glance_lck", 0) != 0; }
void touchPrefsSetGlanceWhenLocked(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("glance_lck", on ? 1 : 0); }
bool touchPrefsGetGlanceEnabled() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("glance_en", 1) != 0; }
@@ -2303,14 +2498,15 @@ void touchPrefsSetChannelMute(const char* name, uint8_t flags) {
s_begun = s_prefs.begin(TOUCH_NS, true);
}
// ---- Per-channel avatar emoji (chat-list avatar override) ----
// ---- Per-conversation avatar emoji (the avatar's override) ----
// Same keyed-blob pattern as the mute table above: 32-byte name + 16-byte UTF-8
// glyph (16 covers ZWJ sequences). No entry = auto (the two-letter avatar).
// 24 entries x 48 B = 1152 B, safely under the SdNvsPrefs 2048-byte blob cap.
// glyph (16 covers ZWJ sequences), keyed by the conversation's name, channel or
// direct chat. No entry = its generated glyph. 32 entries x 48 B = 1536 B, under
// the SdNvsPrefs 2048-byte blob cap.
static const int CHE_NAME = TOUCH_CHMUTE_NAME;
static const int CHE_GLYPH = 16;
static const int CHE_ENTRY = CHE_NAME + CHE_GLYPH;
static const int CHE_MAX = 24;
static const int CHE_MAX = 32;
static const char* KEY_CHE = "chemoji";
static uint8_t* s_che = (uint8_t*)tpPsAlloc(CHE_MAX * CHE_ENTRY);
static int s_che_n = -1; // -1 = not loaded yet
@@ -2341,17 +2537,17 @@ bool touchPrefsGetChannelEmoji(const char* name, char* out, size_t cap) {
out[n] = '\0';
return out[0] != '\0';
}
void touchPrefsSetChannelEmoji(const char* name, const char* utf8) {
if (!name || !name[0]) return;
bool touchPrefsSetChannelEmoji(const char* name, const char* utf8) {
if (!name || !name[0]) return false;
cheLoad();
int i = cheFind(name);
if (!utf8 || !utf8[0]) { // clear -> back to auto letters
if (i < 0) return;
if (!utf8 || !utf8[0]) { // clear -> back to the glyph
if (i < 0) return true;
for (int j = i; j + 1 < s_che_n; ++j) memcpy(&s_che[j * CHE_ENTRY], &s_che[(j + 1) * CHE_ENTRY], CHE_ENTRY);
--s_che_n;
} else {
if (i < 0) {
if (s_che_n >= CHE_MAX) return; // cap reached
if (s_che_n >= CHE_MAX) return false; // cap reached
i = s_che_n++;
memset(&s_che[i * CHE_ENTRY], 0, CHE_ENTRY);
strncpy((char*)&s_che[i * CHE_ENTRY], name, CHE_NAME - 1);
@@ -2366,6 +2562,7 @@ void touchPrefsSetChannelEmoji(const char* name, const char* utf8) {
s_prefs.end();
}
s_begun = s_prefs.begin(TOUCH_NS, true);
return true;
}
// Remembered repeater admin passwords --------------------------------------
+44 -5
View File
@@ -204,9 +204,14 @@ bool touchPrefsSetUiScale(uint8_t scale);
bool touchPrefsGetKbdNav();
bool touchPrefsSetKbdNav(bool on);
/* T-Deck trackball mode: 1 = drive the focus-group D-pad nav (up/down/left/right move the
* selection, centre click selects — same logic as the Tanmatsu keypad); 0 = the soft mouse
* cursor. Default 1. Independent of the keyboard ESDFX nav above. Applied live (no reboot). */
/* T-Deck trackball mode. TB_MODE_SCROLL (the default) scrolls whatever is under the middle
* of the screen and can move that way; TB_MODE_CURSOR is the soft mouse cursor (a click taps
* where it points); TB_MODE_NAV drives the focus-group D-pad nav (up/down/left/right move the
* selection, centre click selects; the same logic as the Tanmatsu keypad). Independent of the
* keyboard nav above. Applied live (no reboot). touchPrefsGetTbNav() = the mode is TB_MODE_NAV. */
enum : uint8_t { TB_MODE_CURSOR = 0, TB_MODE_NAV = 1, TB_MODE_SCROLL = 2 };
uint8_t touchPrefsGetTbMode();
bool touchPrefsSetTbMode(uint8_t mode);
bool touchPrefsGetTbNav();
bool touchPrefsSetTbNav(bool on);
/* Opt-in (issue #64): when true, direct/login/admin unicast floods are tagged with the
@@ -303,6 +308,12 @@ bool touchPrefsSetReportPing(bool on);
uint16_t touchPrefsGetReportedBeta();
bool touchPrefsSetReportedBeta(uint16_t n);
// Condense Nav (#592): in landscape, the main nav bar becomes a rail on the
// right edge and Home's launcher column shows icons only. Default off. Read
// when the UI is built, so a change takes effect after a restart.
bool touchPrefsGetCondenseNav();
bool touchPrefsSetCondenseNav(bool on);
/* Keyboard-nav tab hotkeys: the ASCII key that jumps to each main tab while
* keyboard navigation is on. `tab` is the tab index 0..4 = chat / contacts / home
* / map / settings. Defaults E/R/T/U/I. Programmable in Settings → Keyboard. */
@@ -375,6 +386,10 @@ bool touchPrefsReadUseSdAtBoot();
* device so the UI rebuilds at the new orientation. */
uint8_t touchPrefsGetUiRotation();
bool touchPrefsSetUiRotation(uint8_t rot);
// Pre-storage boot copy of the rotation (raw NVS), for the boot logo. Falls back
// to touchPrefsGetUiRotation() when absent. Sync after touchPrefsReload().
uint8_t touchPrefsGetBootUiRotation();
void touchPrefsSyncBootUiRotation();
/** Calibrated battery "full" voltage in mV — the reading captured when the pack
* was fully charged, treated as 100%. 0 = not calibrated (use the 4200 mV
@@ -548,6 +563,30 @@ bool touchPrefsGetEdgeScroll(); // push cursor past edge to scrol
void touchPrefsSetEdgeScroll(bool on);
bool touchPrefsGetLockOnScreenOff(); // idle screen-off auto-locks (default false; forced on for Pro/Max e-paper)
void touchPrefsSetLockOnScreenOff(bool on);
// Optional unlock PIN: four characters, digits and letters (letters match in either
// case). Only a salted SHA-256 of it is stored. touchPrefsPinSet(nullptr) removes it.
static constexpr int TOUCH_PIN_LEN = 4;
bool touchPrefsPinIsSet();
bool touchPrefsPinCheck(const char* pin);
bool touchPrefsPinSet(const char* pin);
// Wrong PIN tries in a row, kept across reboots so restarting does not reset the wait.
uint8_t touchPrefsPinFails();
void touchPrefsSetPinFails(uint8_t n);
// Constellation screensaver. Standalone keys like lock_off, not Config fields: no
// schema bump, no migration. Both default ON -- the screensaver only runs while
// charging unless the second is turned off, so a device on battery behaves
// exactly as before.
bool touchPrefsGetScreensaver();
void touchPrefsSetScreensaver(bool on);
// How long the screensaver shows on battery before the screen goes dark: 0..60 s
// (0 = straight to dark), 15 s until chosen. And whether it stays on for as long as
// external power is connected (default on).
uint8_t touchPrefsGetScreensaverBatterySecs();
void touchPrefsSetScreensaverBatterySecs(uint8_t secs);
bool touchPrefsGetScreensaverAlwaysOnPower();
void touchPrefsSetScreensaverAlwaysOnPower(bool on);
bool touchPrefsGetLockPreviews(); // message text on the lock screen's unread cards (default true)
void touchPrefsSetLockPreviews(bool on);
bool touchPrefsGetGlanceWhenLocked(); // "at a glance" also fires while manually/idle locked, not just unlocked+dimmed (default false)
void touchPrefsSetGlanceWhenLocked(bool on);
bool touchPrefsGetGlanceEnabled(); // master "at a glance" feature toggle (default true)
@@ -562,8 +601,8 @@ constexpr uint8_t TOUCH_CHMUTE_MSG = 0x1;
constexpr uint8_t TOUCH_CHMUTE_MEN = 0x2;
uint8_t touchPrefsGetChannelMute(const char* name);
void touchPrefsSetChannelMute(const char* name, uint8_t flags);
bool touchPrefsGetChannelEmoji(const char* name, char* out, size_t cap); // chat-list avatar override; false/empty = auto letters
void touchPrefsSetChannelEmoji(const char* name, const char* utf8); // empty/null clears back to auto
bool touchPrefsGetChannelEmoji(const char* name, char* out, size_t cap); // a conversation's avatar emoji (channel or chat); false/empty = its glyph
bool touchPrefsSetChannelEmoji(const char* name, const char* utf8); // empty/null clears back to the glyph; false when the table is full
/** Generic NVS blob (key/value). Used to persist the discovered-nodes list so
* it survives a reboot. getBlob returns the byte count copied (0 if absent). */
+12 -3
View File
@@ -2,12 +2,15 @@
// Browser file transfer requires the shared WebSocket transport and a removable
// card backend. Keep internal SPIFFS/LittleFS-only boards out: /transfer can hold
// large uploads and is intended to remain user-removable storage.
// large uploads and is intended to remain user-removable storage. The exception
// is the Attaky Mesh Deck, which has no card slot at all: it transfers to its
// internal LittleFS "tiles" partition (never SPIFFS, which holds the settings,
// contacts and chats), within that partition's free space.
#if defined(MULTI_TRANSPORT_COMPANION) && \
(defined(HAS_TDECK_GT911) || defined(HAS_TDECK_PRO) || defined(TLORA_PAGER) || \
defined(HAS_THINKNODE_M9) || defined(HELTEC_LORA_V4_R8) || \
defined(HAS_WIO_TRACKER_L2) || defined(HAS_CROWPANEL_35) || defined(HAS_TDISPLAY_P4) || \
defined(HAS_TANMATSU))
defined(HAS_TANMATSU) || defined(ATTAKY_MESH_SERIES))
#define WADA_WEB_FILE_TRANSFER 1
#else
#define WADA_WEB_FILE_TRANSFER 0
@@ -21,7 +24,13 @@
#define WADA_WEB_FILE_TRANSFER_SDMMC 0
#endif
#if WADA_WEB_FILE_TRANSFER && !WADA_WEB_FILE_TRANSFER_SDMMC
#if WADA_WEB_FILE_TRANSFER && defined(ATTAKY_MESH_SERIES)
#define WADA_WEB_FILE_TRANSFER_INTERNAL 1
#else
#define WADA_WEB_FILE_TRANSFER_INTERNAL 0
#endif
#if WADA_WEB_FILE_TRANSFER && !WADA_WEB_FILE_TRANSFER_SDMMC && !WADA_WEB_FILE_TRANSFER_INTERNAL
#define WADA_WEB_FILE_TRANSFER_SPI_SD 1
#else
#define WADA_WEB_FILE_TRANSFER_SPI_SD 0
+18 -11
View File
@@ -42,7 +42,12 @@ section{border:1px solid var(--line);background:var(--panel);border-radius:8px;p
<progress id="progress" max="100" value="0"></progress>
<div class="row"><button id="upload" class="primary" disabled>Upload</button><button id="cancel" class="danger" disabled>Cancel</button></div>
<div id="status">Enter the session code to connect.</div>
<small id="upload-help">Files are uploaded to the device's SD card in /transfer. The transfer session ends when you leave the File Transfer app on the device.</small>
<small id="upload-help">Files are uploaded to the device's storage in /transfer. The transfer session ends when you leave the File Transfer app on the device.</small>
</section>
<section>
<div class="row file-tools" style="margin-top:0"><b>Settings backup</b><button id="backup" disabled>Backup</button><button id="restore" class="danger" disabled>Restore</button></div>
<input id="restore-pick" type="file" accept=".json,application/json" style="display:none">
<small>Backup downloads the same settings backup the device's Export Backup writes (identity, channels, contacts and settings). Restore uploads one and applies it as Import backup does, replacing the device's identity, channels and contacts, then reboots. A backup carries your node's private key and every channel secret, so only use these on a network you trust.</small>
</section>
<section>
<div class="row file-tools" style="margin-top:0"><b>Device files</b><select id="filter" aria-label="File type" disabled><option value="all">ALL</option><option value="screenshots">Screenshots</option><option value="crashes">Crash reports</option><option value="backups">Backups</option><option value="transfers">Transfers</option></select><button id="refresh" disabled>Refresh</button></div>
@@ -51,24 +56,26 @@ section{border:1px solid var(--line);background:var(--panel);border-radius:8px;p
</section>
</main>
<script>
const E=id=>document.getElementById(id),dec=new TextDecoder(),CHUNK=2048,MAX=512*1024*1024,MAX_TILE=256*1024,MAX_DOWNLOAD=64*1024*1024;
const E=id=>document.getElementById(id),dec=new TextDecoder(),CHUNK=2048,MAX=512*1024*1024,MAX_TILE=256*1024,MAX_DOWNLOAD=64*1024*1024,MAX_RESTORE=4*1024*1024;
let ws=null,file=null,name='',offset=0,crc=0xffffffff,state='idle',pendingEnd=false,expectedAck=0,authed=false,cancelTimer=0,cancelText='',cancelKind='';
let selection=[],uploadIndex=0,uploadTotal=0,uploadProcessed=0,uploadSkipped=0,replaceTiles=false;
let entries=[],downloadParts=[],downloadSize=0,downloadOffset=0,downloadName='';
let entries=[],downloadParts=[],downloadSize=0,downloadOffset=0,downloadName='',restoreSaved=null;
const table=new Uint32Array(256);for(let n=0;n<256;n++){let c=n;for(let k=0;k<8;k++)c=(c&1)?(0xedb88320^(c>>>1)):(c>>>1);table[n]=c>>>0}
function crcAdd(c,a){for(let i=0;i<a.length;i++)c=table[(c^a[i])&255]^(c>>>8);return c>>>0}
function status(text,kind=''){E('status').textContent=text;E('status').className=kind}
function controls(){const ready=ws&&ws.readyState===1&&authed&&state==='ready',active=['starting','uploading','cancelling','downloading','listing'].includes(state);E('upload').disabled=!(ready&&selection.length);E('refresh').disabled=!ready;E('filter').disabled=!ready;E('destination').disabled=active;E('replace').disabled=active;E('cancel').disabled=state!=='starting'&&state!=='uploading'&&state!=='downloading'}
function resetUpload(){if(cancelTimer){clearTimeout(cancelTimer);cancelTimer=0}offset=0;crc=0xffffffff;pendingEnd=false;expectedAck=0;uploadIndex=uploadProcessed=uploadSkipped=0;cancelText=cancelKind='';downloadParts=[];downloadSize=downloadOffset=0;E('progress').value=0;state=authed?'ready':'idle';controls()}
function controls(){const ready=ws&&ws.readyState===1&&authed&&state==='ready',active=['starting','uploading','cancelling','downloading','listing'].includes(state);E('upload').disabled=!(ready&&selection.length);E('refresh').disabled=!ready;E('backup').disabled=!ready;E('restore').disabled=!ready;E('filter').disabled=!ready;E('destination').disabled=active;E('replace').disabled=active;E('cancel').disabled=state!=='starting'&&state!=='uploading'&&state!=='downloading'}
function resetUpload(){if(cancelTimer){clearTimeout(cancelTimer);cancelTimer=0}offset=0;crc=0xffffffff;pendingEnd=false;expectedAck=0;uploadIndex=uploadProcessed=uploadSkipped=0;cancelText=cancelKind='';downloadParts=[];downloadSize=downloadOffset=0;if(restoreSaved){selection=restoreSaved;restoreSaved=null;file=selection.length?selection[0].file:null;name=selection.length?selection[0].name:''}E('progress').value=0;state=authed?'ready':'idle';controls()}
function safeName(raw){return raw.replace(/[^A-Za-z0-9._-]/g,'_').replace(/^\.+/,'').slice(0,64)}
function mapMode(){return E('destination').value==='maps'}
function mapEntry(f){const parts=(f.webkitRelativePath||f.name).split('/').filter(Boolean);if(parts.length<3||f.size<=0||f.size>MAX_TILE)return null;const zt=parts[parts.length-3],xt=parts[parts.length-2],m=parts[parts.length-1].match(/^(\d+)\.png$/i);if(!/^\d+$/.test(zt)||!/^\d+$/.test(xt)||!m)return null;const z=Number(zt),x=Number(xt),y=Number(m[1]),span=2**z;if(!Number.isSafeInteger(z)||!Number.isSafeInteger(x)||!Number.isSafeInteger(y)||z<3||z>19||x<0||y<0||x>=span||y>=span)return null;return{file:f,name:z+'/'+x+'/'+y+'.png',map:true}}
function choose(input){const files=Array.from(input||[]);selection=[];file=null;name='';if(mapMode()){const seen=new Set();let rejected=0;for(const f of files){const item=mapEntry(f);if(!item||seen.has(item.name)){rejected++;continue}seen.add(item.name);selection.push(item)}if(!selection.length){E('name').textContent='No valid z/x/y.png tiles selected';E('drop').classList.remove('ready');status('Choose a folder containing OSM z/x/y.png tiles (256 KB maximum each).','err');controls();return}const bytes=selection.reduce((sum,item)=>sum+item.file.size,0);E('name').textContent=selection.length.toLocaleString()+' tiles ('+bytes.toLocaleString()+' bytes)'+(rejected?' · '+rejected+' skipped':'');}else{const f=files[0];if(!f)return;if(f.size<=0||f.size>MAX){status('File must be between 1 byte and 512 MB.','err');return}name=safeName(f.name)||'upload.bin';selection=[{file:f,name,map:false}];E('name').textContent=(name===f.name?name:(f.name+' → '+name))+' ('+f.size.toLocaleString()+' bytes)'}file=selection[0].file;E('drop').classList.add('ready');status(authed?'Ready to upload.':'Enter the session code to connect.');controls()}
function destinationChanged(){const maps=mapMode();selection=[];file=null;name='';E('pick').value='';E('pick').multiple=maps;if(maps)E('pick').setAttribute('webkitdirectory','');else E('pick').removeAttribute('webkitdirectory');E('replace-row').style.display=maps?'flex':'none';E('drop').textContent=maps?'Choose an OSM tile folder':'Choose or drop a file';E('name').textContent='No file selected';E('drop').classList.remove('ready');E('upload-help').textContent=maps?'Select a folder containing z/x/y.png tiles. They are stored on the SD card under /tiles; enable Tiles from SD card in Map options where available.':"Files are uploaded to the device's SD card in /transfer. The transfer session ends when you leave the File Transfer app on the device.";controls()}
function connect(){const code=E('code').value.trim();if(!/^\d{6}$/.test(code)){status('Enter the six-digit code.','err');return}if(ws)ws.close();authed=false;state='connecting';controls();const socket=new WebSocket((location.protocol==='https:'?'wss://':'ws://')+location.host+'/files');ws=socket;socket.binaryType='arraybuffer';socket.onopen=()=>{if(ws===socket){status('Authenticating…');socket.send('AUTH '+code)}};socket.onerror=()=>{if(ws===socket)status('Connection error.','err')};socket.onclose=()=>{if(ws!==socket)return;ws=null;authed=false;resetUpload();status('Disconnected. Reopen File Transfer on the device if the session ended.','err')};socket.onmessage=e=>{if(ws===socket)onMessage(e)}}
function onMessage(ev){const bytes=typeof ev.data==='string'?null:new Uint8Array(ev.data);if(bytes&&bytes[0]===1){if(state!=='cancelling')downloadChunk(bytes);return}const text=typeof ev.data==='string'?ev.data:dec.decode(bytes);if(state==='cancelling'&&text!=='CANCELLED')return;if(text==='AUTH OK'){authed=true;state='ready';status('Connected.','ok');controls();refreshList();return}if(text==='READY'){state='uploading';status('Uploading '+(uploadIndex+1)+' / '+selection.length+': '+name+'…');controls();sendNext();return}if(text==='SKIP'){if(state!=='starting'||!selection[uploadIndex]||!selection[uploadIndex].map){fail('Unexpected skip response.');return}uploadSkipped++;uploadProcessed+=file.size;uploadIndex++;startNextUpload();return}if(text.startsWith('ACK ')){const next=Number(text.slice(4));if(state!=='uploading'||!Number.isSafeInteger(next)||next!==expectedAck||next>file.size){fail('Invalid acknowledgement.');return}offset=next;expectedAck=0;E('progress').value=Math.floor((uploadProcessed+offset)*100/uploadTotal);sendNext();return}if(text.startsWith('DONE ')){const completed=Number(text.slice(5));if(state!=='uploading'||!pendingEnd||completed!==file.size||offset!==file.size){fail('Invalid completion response.');return}uploadProcessed+=completed;uploadIndex++;startNextUpload();return}if(text.startsWith('ENTRY ')){const m=text.match(/^ENTRY (\d+) (.+)$/);if(!m){fail('Invalid file list entry.');return}entries.push({size:Number(m[1]),path:m[2]});ws.send('LIST NEXT');return}if(text==='LIST DONE'){state='ready';renderFiles();controls();status(entries.length?'File list updated.':'No downloadable device files yet.','ok');return}if(text.startsWith('FILE ')){const m=text.match(/^FILE (\d+) (.+)$/);if(!m){fail('Invalid download metadata.');return}downloadSize=Number(m[1]);if(!Number.isSafeInteger(downloadSize)||downloadSize<=0||downloadSize>MAX_DOWNLOAD){fail('Downloads are limited to 64 MB in this browser tool.');return}downloadName=m[2];downloadOffset=0;downloadParts=[];state='downloading';E('progress').value=0;controls();status('Downloading '+downloadName+'…');ws.send('READ 0');return}if(text==='CANCELLED'){const message=cancelText||'Transfer cancelled.',kind=cancelKind;resetUpload();status(message,kind);return}if(text.startsWith('ERR ')){fail(text.slice(4));return}fail('Unexpected device response.')}
async function sendNext(){if(state!=='uploading'||!file)return;if(offset>=file.size){if(!pendingEnd){pendingEnd=true;ws.send('END '+((crc^0xffffffff)>>>0).toString(16).padStart(8,'0'))}return}const start=offset;const bytes=new Uint8Array(await file.slice(start,Math.min(start+CHUNK,file.size)).arrayBuffer());if(state!=='uploading'||start!==offset)return;crc=crcAdd(crc,bytes);const frame=new Uint8Array(bytes.length+4);new DataView(frame.buffer).setUint32(0,start,true);frame.set(bytes,4);expectedAck=start+bytes.length;ws.send(frame)}
function startNextUpload(){if(uploadIndex>=selection.length){E('progress').value=100;state='ready';controls();if(mapMode())status('Map upload complete: '+selection.length+' tiles'+(uploadSkipped?' ('+uploadSkipped+' existing skipped)':'')+'. Enable Tiles from SD card in Map options.','ok');else{status('Upload complete: '+name,'ok');refreshList()}return}const item=selection[uploadIndex];file=item.file;name=item.name;offset=0;crc=0xffffffff;pendingEnd=false;expectedAck=0;state='starting';controls();status('Preparing '+(uploadIndex+1)+' / '+selection.length+': '+name+'…');ws.send(item.map?('MAPBEGIN '+file.size+' '+(replaceTiles?'R':'S')+' '+name):('BEGIN '+file.size+' '+name))}
function destinationChanged(){const maps=mapMode();selection=[];file=null;name='';E('pick').value='';E('pick').multiple=maps;if(maps)E('pick').setAttribute('webkitdirectory','');else E('pick').removeAttribute('webkitdirectory');E('replace-row').style.display=maps?'flex':'none';E('drop').textContent=maps?'Choose an OSM tile folder':'Choose or drop a file';E('name').textContent='No file selected';E('drop').classList.remove('ready');E('upload-help').textContent=maps?'Select a folder containing z/x/y.png tiles. They are stored on the SD card under /tiles; enable Tiles from SD card in Map options where available.':"Files are uploaded to the device's storage in /transfer. The transfer session ends when you leave the File Transfer app on the device.";controls()}
function connect(){const code=E('code').value.trim();if(!/^\d{6}$/.test(code)){status('Enter the six-digit code.','err');return}if(ws)ws.close();authed=false;state='connecting';controls();const socket=new WebSocket((location.protocol==='https:'?'wss://':'ws://')+location.host+'/files');ws=socket;socket.binaryType='arraybuffer';socket.onopen=()=>{if(ws===socket){status('Authenticating…');socket.send('AUTH '+code)}};socket.onerror=()=>{if(ws===socket)status('Connection error.','err')};socket.onclose=()=>{if(ws!==socket)return;const restored=state==='restored';ws=null;authed=false;resetUpload();if(restored)status('Restore complete. The device is rebooting; reopen File Transfer on it to reconnect.','ok');else status('Disconnected. Reopen File Transfer on the device if the session ended.','err')};socket.onmessage=e=>{if(ws===socket)onMessage(e)}}
function onMessage(ev){const bytes=typeof ev.data==='string'?null:new Uint8Array(ev.data);if(bytes&&bytes[0]===1){if(state!=='cancelling')downloadChunk(bytes);return}const text=typeof ev.data==='string'?ev.data:dec.decode(bytes);if(state==='cancelling'&&text!=='CANCELLED')return;if(text==='AUTH OK'){authed=true;state='ready';status('Connected.','ok');controls();refreshList();return}if(text==='READY'){state='uploading';status('Uploading '+(uploadIndex+1)+' / '+selection.length+': '+name+'…');controls();sendNext();return}if(text==='SKIP'){if(state!=='starting'||!selection[uploadIndex]||!selection[uploadIndex].map){fail('Unexpected skip response.');return}uploadSkipped++;uploadProcessed+=file.size;uploadIndex++;startNextUpload();return}if(text.startsWith('ACK ')){const next=Number(text.slice(4));if(state!=='uploading'||!Number.isSafeInteger(next)||next!==expectedAck||next>file.size){fail('Invalid acknowledgement.');return}offset=next;expectedAck=0;E('progress').value=Math.floor((uploadProcessed+offset)*100/uploadTotal);sendNext();return}if(text.startsWith('DONE ')){const completed=Number(text.slice(5));if(state!=='uploading'||!pendingEnd||completed!==file.size||offset!==file.size){fail('Invalid completion response.');return}uploadProcessed+=completed;uploadIndex++;startNextUpload();return}if(text.startsWith('ENTRY ')){const m=text.match(/^ENTRY (\d+) (.+)$/);if(!m){fail('Invalid file list entry.');return}entries.push({size:Number(m[1]),path:m[2]});ws.send('LIST NEXT');return}if(text==='LIST DONE'){state='ready';renderFiles();controls();status(entries.length?'File list updated.':'No downloadable device files yet.','ok');return}if(text.startsWith('FILE ')){const m=text.match(/^FILE (\d+) (.+)$/);if(!m){fail('Invalid download metadata.');return}downloadSize=Number(m[1]);if(!Number.isSafeInteger(downloadSize)||downloadSize<=0||downloadSize>MAX_DOWNLOAD){fail('Downloads are limited to 64 MB in this browser tool.');return}downloadName=m[2];downloadOffset=0;downloadParts=[];state='downloading';E('progress').value=0;controls();status('Downloading '+downloadName+'…');ws.send('READ 0');return}if(text.startsWith('RESTORED ')){const m=text.match(/^RESTORED (\d+) (\d+)$/);if(state!=='uploading'||!pendingEnd||!m){fail('Invalid restore response.');return}restoreSaved=null;state='restored';E('progress').value=100;controls();status('Restored '+m[1]+' contacts and '+m[2]+' channels. The device is rebooting…','ok');return}if(text==='CANCELLED'){const message=cancelText||'Transfer cancelled.',kind=cancelKind;resetUpload();status(message,kind);return}if(text.startsWith('ERR ')){fail(text.slice(4));return}fail('Unexpected device response.')}
async function sendNext(){if(state!=='uploading'||!file)return;if(offset>=file.size){if(!pendingEnd){pendingEnd=true;if(selection[uploadIndex]&&selection[uploadIndex].restore)status('Applying the backup on the device…');ws.send('END '+((crc^0xffffffff)>>>0).toString(16).padStart(8,'0'))}return}const start=offset;const bytes=new Uint8Array(await file.slice(start,Math.min(start+CHUNK,file.size)).arrayBuffer());if(state!=='uploading'||start!==offset)return;crc=crcAdd(crc,bytes);const frame=new Uint8Array(bytes.length+4);new DataView(frame.buffer).setUint32(0,start,true);frame.set(bytes,4);expectedAck=start+bytes.length;ws.send(frame)}
function startNextUpload(){if(uploadIndex>=selection.length){E('progress').value=100;state='ready';controls();if(mapMode())status('Map upload complete: '+selection.length+' tiles'+(uploadSkipped?' ('+uploadSkipped+' existing skipped)':'')+'. Enable Tiles from SD card in Map options.','ok');else{status('Upload complete: '+name,'ok');refreshList()}return}const item=selection[uploadIndex];file=item.file;name=item.name;offset=0;crc=0xffffffff;pendingEnd=false;expectedAck=0;state='starting';controls();status('Preparing '+(uploadIndex+1)+' / '+selection.length+': '+name+'…');ws.send(item.restore?('RESTORE '+file.size):item.map?('MAPBEGIN '+file.size+' '+(replaceTiles?'R':'S')+' '+name):('BEGIN '+file.size+' '+name))}
function backup(){if(!authed||state!=='ready')return;state='starting';controls();status('Creating backup…');ws.send('BACKUP')}
function restoreChosen(input){const f=input&&input[0];E('restore-pick').value='';if(!f||!authed||state!=='ready')return;if(f.size<=0||f.size>MAX_RESTORE){status('A settings backup must be between 1 byte and 4 MB.','err');return}if(!/\.json$/i.test(f.name)){status('Choose a settings backup (.json) file.','err');return}if(!confirm('Restore '+f.name+'?\n\nThis replaces the identity, channels and contacts on the device, then reboots it.'))return;restoreSaved=selection;selection=[{file:f,name:f.name,map:false,restore:true}];uploadIndex=0;uploadProcessed=0;uploadSkipped=0;uploadTotal=f.size;E('progress').value=0;startNextUpload()}
function upload(){if(!selection.length||state!=='ready')return;uploadIndex=0;uploadProcessed=0;uploadSkipped=0;uploadTotal=selection.reduce((sum,item)=>sum+item.file.size,0);replaceTiles=E('replace').checked;E('progress').value=0;startNextUpload()}
function requestCancel(text='Transfer cancelled.',kind=''){if(state==='cancelling')return;if(ws&&ws.readyState===1&&authed&&state!=='ready'&&state!=='idle'&&state!=='connecting'){cancelText=text;cancelKind=kind;state='cancelling';controls();status('Cancelling…');ws.send('CANCEL');cancelTimer=setTimeout(()=>{if(state!=='cancelling')return;const socket=ws;resetUpload();status(text,kind);if(socket&&socket.readyState<2)socket.close()},5000);return}resetUpload();status(text,kind)}
function cancel(){requestCancel()}
@@ -79,7 +86,7 @@ function renderFiles(){const box=E('files'),filter=E('filter').value,shown=entri
function startDownload(path){if(!authed||state!=='ready')return;state='starting';controls();status('Opening '+path+'…');ws.send('GET '+path)}
function deleteDeviceFile(path){if(!authed||state!=='ready'||!confirm('Delete '+path+' from the device?'))return;entries=[];state='listing';controls();status('Deleting '+path+'…');ws.send('DELETE '+path)}
function downloadChunk(bytes){if(state!=='downloading'||bytes.length<6){fail('Unexpected download data.');return}const at=new DataView(bytes.buffer,bytes.byteOffset,bytes.byteLength).getUint32(1,true),part=bytes.slice(5);if(at!==downloadOffset||downloadOffset+part.length>downloadSize){fail('Invalid download offset.');return}downloadParts.push(part);downloadOffset+=part.length;E('progress').value=Math.floor(downloadOffset*100/downloadSize);if(downloadOffset<downloadSize){ws.send('READ '+downloadOffset);return}if(downloadOffset!==downloadSize){fail('Download size mismatch.');return}const href=URL.createObjectURL(new Blob(downloadParts)),a=document.createElement('a');a.href=href;a.download=downloadName;document.body.append(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(href),1000);state='ready';downloadParts=[];controls();status('Downloaded '+downloadName,'ok')}
E('connect').onclick=connect;E('upload').onclick=upload;E('cancel').onclick=cancel;E('refresh').onclick=refreshList;E('filter').onchange=renderFiles;E('destination').onchange=destinationChanged;E('pick').onchange=e=>choose(e.target.files);
E('connect').onclick=connect;E('upload').onclick=upload;E('cancel').onclick=cancel;E('refresh').onclick=refreshList;E('backup').onclick=backup;E('restore').onclick=()=>E('restore-pick').click();E('restore-pick').onchange=e=>restoreChosen(e.target.files);E('filter').onchange=renderFiles;E('destination').onchange=destinationChanged;E('pick').onchange=e=>choose(e.target.files);
const drop=E('drop');drop.ondragover=e=>{e.preventDefault();drop.classList.add('ready')};drop.ondragleave=()=>{if(!file)drop.classList.remove('ready')};drop.ondrop=e=>{e.preventDefault();choose(e.dataTransfer.files)};
destinationChanged();
</script></body></html>)FILEPAGE";
+8 -2
View File
@@ -857,9 +857,13 @@ void setup() {
#if defined(ESP32_PLATFORM) && defined(HAS_TOUCH_UI)
// Rotate the panel to the saved UI orientation BEFORE painting the boot
// wordmark, so it's upright in landscape too (UITask applies the same
// hardware rotation later for the LVGL UI). ROT_90->1, ROT_270->3.
// hardware rotation later for the LVGL UI). ROT_90->1, ROT_270->3. Settings
// live in SPIFFS/SD, which aren't mounted yet, so read the NVS boot copy.
{
uint8_t r = touchPrefsGetUiRotation();
uint8_t r = touchPrefsGetBootUiRotation();
#if defined(ATTAKY_MESH_SERIES)
if (r != 0) r = 1; // UI init coerces any landscape to ROT_90
#endif
if (r == 1) display.setDisplayRotation(1);
else if (r == 3) display.setDisplayRotation(3);
#if defined(HAS_CROWPANEL_35)
@@ -1435,6 +1439,8 @@ void setup() {
// file-saved values (theme accent, brightness, language, …) take effect this
// boot — otherwise a theme change "reverts" on every restart.
touchPrefsReload();
// Keep the boot logo's NVS copy of the rotation in step with the real one.
touchPrefsSyncBootUiRotation();
// Restore replay protection before mesh startup or optional cold-boot NTP.
// A trusted correction can then pull a poisoned future floor back through
// ClockFloorRTC's guarded set path without a later restore undoing it.
File diff suppressed because it is too large. Load diff
+72
View File
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
// The Constellation screensaver: your node at the centre, the nodes you have
// heard placed around it, and a ring wherever a packet actually came from.
//
// Every movement on it is something that happened on the radio. A ring means a
// packet arrived from that node; a dot travelling inward is that packet coming
// to you, through the repeater it took when we know it; a ring on your own node
// is you transmitting; light running inward along a route is a node heard in the
// last ten minutes. Nodes you have not heard from in a while fade, so a quiet
// night shows as a quiet screen. Nothing else animates except the breathing of
// your own node and the slow drift that keeps the image off the same pixels.
//
// Pure LVGL, and it knows nothing about MeshCore: UITask.cpp turns contacts into
// CstNode records and calls in, the same arrangement LuaAppHost uses, so the
// drawing and the mesh can change independently. Everything is drawn from lines,
// circles and labels: no images, which matters on boards near their flash limit.
#include <lvgl.h>
#include <stdint.h>
enum CstKind : uint8_t { CST_COMPANION = 0, CST_REPEATER = 1, CST_ROOM = 2 };
struct CstNode {
uint32_t key; // first 4 bytes of the public key: stable across rebuilds
int16_t bearing; // degrees, 0 = north, clockwise (true bearing when placed by GPS)
uint16_t dist; // 0..1000, centre to outer ring
uint8_t kind; // CstKind
uint8_t level; // recency 0..3: 3 heard under 10 min ago ... 0 over 6 h
int16_t via; // index of the repeater it is reached through;
// -1 = straight from us, -2 = path unknown (no line drawn)
char label[14]; // drawn when non-empty; the caller labels only a few
};
struct CstStatus {
char clock[12]; // "21:47": digits and a colon only, the face has nothing else
bool clock_ok; // false: no trustworthy time; the clock is hidden, date says why
char date[40]; // "TUE 6 OCT", or "CLOCK NOT SET · UP 2H14M" (the caller sets the case)
int unread; // unread messages; 0 hides the count
char unread_word[16]; // the word after the count: "new"
int heard; // nodes heard this hour, drawn strong; -1 for no count
char info[56]; // after the count: "nodes · heard 12 s ago"
int batt; // battery percent, -1 when there is no reading
bool charging;
bool north_up; // positions are true bearings: draw the N marker
};
struct CstTheme {
uint32_t bg;
uint32_t accent; // range rings and links
uint32_t glow; // nodes, you, and the light on a route
uint32_t text;
uint32_t sub;
uint32_t ter; // the N marker
const lv_font_t* small; // labels and the status lines
const lv_font_t* strong; // the counts in them
};
void cstShow(lv_obj_t* parent, const CstTheme& theme); // build; no-op when shown
void cstHide(); // tear down; async delete, safe from anywhere
bool cstShown();
// Where the status text is now (it changes corners): the lowest edge of what sits in
// the top half, the highest edge of what sits in the bottom half, and which of the
// two holds the clock. False when the screensaver is not up.
bool cstHudBands(lv_coord_t* top_end, lv_coord_t* bottom_start, bool* clock_top);
void cstSetNodes(const CstNode* nodes, int count); // relayout only when they changed
void cstSetStatus(const CstStatus& s); // touches only what changed
void cstPing(uint32_t key); // a packet arrived from this node
void cstPingSelf(); // we transmitted
void cstPingFar(); // heard something we cannot place
void cstTick(uint32_t now_ms); // burn-in drift; cheap, call every loop
+5 -2
View File
@@ -1,11 +1,14 @@
# Touch UI font assets
The generated `extras_font_*`, `extras_lat_*`, and `star_font_*` C files use
The generated `extras_font_*`, `extras_lat_*`, `star_font_*` and `clock_font_*` C files use
font software from the following projects:
- Montserrat Medium from LVGL v8.4.0
- Copyright 2011 The Montserrat Project Authors
- https://github.com/JulietaUla/Montserrat
- Montserrat Light v7.222 (`clock_font_*`: digits and colon only)
- Copyright 2011 The Montserrat Project Authors
- https://github.com/JulietaUla/Montserrat
- Noto Sans 2.015
- Copyright 2022 The Noto Project Authors
- https://github.com/notofonts/latin-greek-cyrillic
@@ -37,7 +40,7 @@ scripts/build/gen-touch-fonts.sh --check
The script downloads the pinned source releases into a temporary directory,
verifies both the archives and extracted font files, invokes the pinned
converter, and replaces all ten generated touch UI font files together.
converter, and replaces all twelve generated touch UI font files together.
All four fonts are licensed under the SIL Open Font License, Version 1.1:
+46 -1
View File
@@ -68,6 +68,7 @@ extern int luaHostDiscoverAt(int idx, char* pk_hex, size_t pk_cap, char* name,
int* type, int* rssi, float* snr, float* their_snr, int* hops,
uint32_t* first_ms_ago, uint32_t* last_ms_ago, int* heard);
extern void luaHostTextPrompt(const char* title, const char* initial, void (*cb)(const char*));
extern void luaHostEmojiPick(void (*cb)(const char*));
extern void luaHostTextPromptDismiss();
extern void luaHostRadioStats(float* rssi, float* noise, uint32_t* rx_air_s, uint32_t* tx_air_s,
uint32_t* rx_pkts, uint32_t* rx_err, int* budget_ms);
@@ -729,13 +730,28 @@ int uiTextLines(lua_State* L) {
// accessors compare generations and null it, so the existing `if (u->obj)` guard
// in every method turns a stale call into a no-op instead of a crash.
static int uiClear(lua_State* L) {
(void)L;
if (!s_h || !s_h->body) return 0;
#if CAP_LUA_SDK_EXT
destroyLiveMapView();
#endif
lv_obj_clean(s_h->body); // deletes the children, keeps the page itself
++s_ui_gen;
// Drop the button callbacks with the buttons they belong to. The table is
// keyed by the LVGL object POINTER, and lv_obj_clean just freed every one of
// them, so every entry here is now stale by definition. Leaving them cost an
// app a table entry and a live closure (with its upvalues) per button per
// rebuild, which is unbounded: an app with a few screens it redraws is the
// normal case, and Jade's hit "screen memory errors, worse the longer it has
// been open" exactly this way.
//
// It was also a correctness bug waiting to happen. LVGL reuses freed
// addresses, so a NEW button created without a callback could land on an
// address a dead one left behind and fire that stale closure on the next tap.
if (s_h->L && s_h->ref_btncb != LUA_NOREF) {
lua_newtable(s_h->L);
lua_rawseti(s_h->L, LUA_REGISTRYINDEX, s_h->ref_btncb);
}
(void)L;
return 0;
}
@@ -2342,6 +2358,34 @@ void promptDeliver(const char* text) {
serviceDeferredClose();
}
// wada.ui.emoji(cb) -- open the emoji picker and hand the chosen glyph back as a
// UTF-8 string, or nil if it was dismissed. The long-press Cut/Copy/Paste/Sym/
// Emoji menu covers the firmware's own field, which is what wada.ui.input()
// opens; this is for an app that draws its own text entry and keeps the buffer
// itself, where there is no textarea to long-press. Asked for by Jade, whose
// composer shows a pad-bytes counter the firmware's field has no way to show.
int emoji_cb_ref = LUA_NOREF;
void emojiDeliver(const char* utf8) {
if (!s_h || !s_h->L || emoji_cb_ref == LUA_NOREF) return;
lua_State* L = s_h->L;
lua_rawgeti(L, LUA_REGISTRYINDEX, emoji_cb_ref);
luaL_unref(L, LUA_REGISTRYINDEX, emoji_cb_ref);
emoji_cb_ref = LUA_NOREF;
if (utf8) lua_pushstring(L, utf8); else lua_pushnil(L);
guardedCall(s_h, 1);
serviceDeferredClose();
}
int uiEmoji(lua_State* L) {
luaL_checktype(L, 1, LUA_TFUNCTION);
if (!s_h) return luaL_error(L, "no app");
if (emoji_cb_ref != LUA_NOREF) return luaL_error(L, "the emoji picker is already open");
lua_pushvalue(L, 1);
emoji_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
luaHostEmojiPick(emojiDeliver);
return 0;
}
int uiInput(lua_State* L) {
size_t title_length = 0, initial_length = 0;
const char* title_raw = luaL_optlstring(L, 1, "", &title_length);
@@ -2513,6 +2557,7 @@ void openWada(lua_State* L) {
lua_pushcfunction(L, uiInput); lua_setfield(L, -2, "input"); // modal text entry
lua_pushcfunction(L, uiList); lua_setfield(L, -2, "list"); // scrollable selectable rows
lua_pushcfunction(L, uiClear); lua_setfield(L, -2, "clear"); // wipe the page (#318 / Discord)
lua_pushcfunction(L, uiEmoji); lua_setfield(L, -2, "emoji"); // glyph picker for an app's own text entry
lua_setfield(L, -2, "ui");
lua_newtable(L); // wada.sys
+8010 -2554
View File
File diff suppressed because it is too large. Load diff
+19 -3
View File
@@ -131,6 +131,11 @@ public:
uint8_t mesh_contact_key6[6];
/** Channel slot index for group sends, or -1 if unknown. */
int16_t mesh_channel_slot;
/** DM only: the user wiped this chat with "Delete history", so the row stays in the
* inbox with no messages behind it. Without this an empty DM is indistinguishable
* from a contact that was never chatted with, and the inbox hides (and the contact
* refresh reclaims) those. "Delete chat" (removeThread) is how the row goes away. */
bool keep_when_empty;
char name[MAX_THREAD_NAME + 1];
};
@@ -195,6 +200,8 @@ private:
mutable uint16_t _thread_msgs[MAX_UI_THREADS] = { 0 };
mutable bool _thread_hist_dirty = true;
void rebuildThreadHistoryFlags() const;
/** Time of a thread's newest message in the ring (0 when it has none). */
uint32_t newestThreadMessageTs(const char* name, bool channel) const;
/** Drop this thread's oldest stored messages until it is back within the per-chat cap
* (touchPrefsGetHistPerChat; 0 = uncapped). Called after an append. */
void enforceHistoryCap(int thread_idx);
@@ -349,7 +356,8 @@ public:
void markAllThreadsRead(); // clear every thread's unread count
bool threadHasMention(int idx) const; // unread @mention of me in this thread
int getThreadCount(bool channel_mode, int out_indexes[], int max_out) const;
/** Inbox list: channels (any used) + DMs that have at least one stored message, sorted by recency. */
/** Inbox list: channels (any used) + DMs that have at least one stored message or were
* emptied with "Delete history" (keep_when_empty), sorted by recency. */
int getCombinedInboxCount(int out_indexes[], int max_out) const;
bool threadHasMessageHistory(int thread_idx) const;
bool getThreadInfo(int idx, bool& channel, uint16_t& unread, uint32_t& ts, char* name, size_t name_len) const;
@@ -421,7 +429,9 @@ public:
* thread's name + kind and returns the latest by timestamp. False when the
* thread has no messages in the ring. */
bool getThreadLastMessage(int idx, char* sender, size_t sender_cap,
char* text, size_t text_cap, bool* outgoing) const {
char* text, size_t text_cap, bool* outgoing,
uint8_t* deliv = nullptr, uint8_t* meta = nullptr,
uint8_t* path_len = nullptr) const {
if (idx < 0 || idx >= MAX_UI_THREADS || !_ui_threads[idx].used || !_ui_msgs) return false;
const bool ch = _ui_threads[idx].channel;
const char* nm = _ui_threads[idx].name;
@@ -442,6 +452,9 @@ public:
if (sender && sender_cap) { strncpy(sender, m.sender, sender_cap - 1); sender[sender_cap - 1] = '\0'; }
if (text && text_cap) { strncpy(text, m.text, text_cap - 1); text[text_cap - 1] = '\0'; }
if (outgoing) *outgoing = m.outgoing;
if (deliv) *deliv = m.deliv_state;
if (meta) *meta = m.meta_flags;
if (path_len) *path_len = m.path_len;
return true;
}
return false;
@@ -556,8 +569,11 @@ public:
/** Reveal the lock screen (light the panel) without unlocking — for a key /
* trackball press while hard-locked. No-op unless manually locked. */
void lockscreenReveal();
/** Release a manual lock: hide the lock screen and turn the panel back on. */
/** Release a manual lock: hide the lock screen and turn the panel back on.
* With a PIN set this first asks for it, and unlocks once it matches. */
void unlockScreen();
/** A key on the PIN screen: keeps a lit lock screen from dimming mid-entry. */
void notePinActivity();
/** True while the panel backlight is off (idle-dimmed or manually locked). */
bool isScreenOff() const { return _screen_off; }
bool isManualLocked() const { return _manual_lock; } // hard screen lock engaged
+57 -64
View File
@@ -1,10 +1,15 @@
/*******************************************************************************
* Size: 16 px
* Bpp: 4
* Opts: --no-compress --bpp 4 --size 16 --font fa-solid-900.ttf -r 0xF023 -r 0xF0F3 -r 0xF1F6 --format lvgl --lv-font-name cc_icons_16 --no-kerning -o /Users/kaj/wadamesh/src/ui-touch/cc_icons_16.c
* Generated by scripts/build/gen-touch-fonts.sh with lv_font_conv 1.5.3
* Sources: Lucide 1.52.0 (cc icons)
******************************************************************************/
#if 1
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef CC_ICONS_16
#define CC_ICONS_16 1
@@ -19,65 +24,54 @@
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+F023 "" */
0x0, 0x0, 0x3b, 0xff, 0xb3, 0x0, 0x0, 0x0,
0x5, 0xff, 0xff, 0xff, 0x50, 0x0, 0x0, 0x1f,
0xf8, 0x11, 0x8f, 0xe1, 0x0, 0x0, 0x5f, 0xc0,
0x0, 0xc, 0xf5, 0x0, 0x0, 0x7f, 0x80, 0x0,
0x8, 0xf7, 0x0, 0x0, 0x8f, 0x80, 0x0, 0x8,
0xf8, 0x0, 0x4e, 0xff, 0xff, 0xff, 0xff, 0xff,
0xe4, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfe, 0x4e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4,
0x0, 0x0, 0x1, 0x43, 0x0, 0x0, 0x0, 0x0,
0x0, 0x8f, 0xff, 0xe5, 0x0, 0x0, 0x0, 0x8,
0xf6, 0x1, 0x9f, 0x40, 0x0, 0x0, 0xf, 0x60,
0x0, 0xb, 0xb0, 0x0, 0x0, 0x3f, 0x20, 0x0,
0x6, 0xe0, 0x0, 0x0, 0x3f, 0x10, 0x0, 0x6,
0xe0, 0x0, 0x3, 0x7f, 0x65, 0x55, 0x59, 0xf5,
0x10, 0x6f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1,
0xd8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf6, 0xd7,
0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0xd7, 0x0,
0x0, 0x0, 0x0, 0x0, 0xe6, 0xd7, 0x0, 0x0,
0x0, 0x0, 0x0, 0xe6, 0xd7, 0x0, 0x0, 0x0,
0x0, 0x0, 0xe6, 0xd8, 0x0, 0x0, 0x0, 0x0,
0x0, 0xf6, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xe1, 0x3, 0x55, 0x55, 0x55, 0x55, 0x54, 0x0,
/* U+F0F3 "" */
0x0, 0x0, 0x0, 0xc, 0xc0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3f, 0xf3, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1c, 0xff, 0xff, 0xc1, 0x0, 0x0,
0x0, 0x1, 0xef, 0xff, 0xff, 0xfe, 0x10, 0x0,
0x0, 0x8, 0xff, 0xff, 0xff, 0xff, 0x80, 0x0,
0x0, 0xe, 0xff, 0xff, 0xff, 0xff, 0xd0, 0x0,
0x0, 0xf, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0,
0x0, 0xf, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0,
0x0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x0,
0x0, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0,
0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20,
0xc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
0xb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xef, 0xfe, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x4e, 0xe4, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0x44, 0x0, 0x0, 0x0, 0x0,
0x0, 0x9f, 0xff, 0xf7, 0x0, 0x0, 0x0, 0xb,
0xe5, 0x1, 0x7f, 0x80, 0x0, 0x0, 0x5f, 0x30,
0x0, 0x5, 0xf2, 0x0, 0x0, 0xab, 0x0, 0x0,
0x0, 0xe7, 0x0, 0x0, 0xc9, 0x0, 0x0, 0x0,
0xc8, 0x0, 0x0, 0xd8, 0x0, 0x0, 0x0, 0xba,
0x0, 0x0, 0xf5, 0x0, 0x0, 0x0, 0x8d, 0x0,
0x6, 0xf1, 0x0, 0x0, 0x0, 0x3f, 0x30, 0x3f,
0x80, 0x0, 0x0, 0x0, 0xb, 0xd1, 0xae, 0x55,
0x55, 0x55, 0x55, 0x56, 0xf7, 0x5f, 0xff, 0xff,
0xff, 0xff, 0xff, 0xe3, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x35, 0x50,
0x0, 0x0, 0x0, 0x0, 0xa, 0xff, 0x70, 0x0,
0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x0,
/* U+F1F6 "" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xd, 0x90, 0x0, 0x0, 0x0,
0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xfc,
0x10, 0x0, 0x3, 0xff, 0x30, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4f, 0xe4, 0x2, 0xcf, 0xff, 0xfc,
0x10, 0x0, 0x0, 0x0, 0x0, 0x2, 0xdf, 0x8e,
0xff, 0xff, 0xff, 0xe1, 0x0, 0x0, 0x0, 0x0,
0x0, 0xa, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0xff, 0xff,
0xff, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x3, 0xef, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xa0, 0x1c, 0xff, 0xff, 0xff,
0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xfd, 0x20,
0x9f, 0xff, 0xff, 0x30, 0x0, 0x0, 0x0, 0x0,
0x8, 0xff, 0xf4, 0x5, 0xff, 0xff, 0x80, 0x0,
0x0, 0x0, 0x0, 0x2f, 0xff, 0xff, 0x80, 0x2d,
0xff, 0xf2, 0x0, 0x0, 0x0, 0x0, 0xcf, 0xff,
0xff, 0xfb, 0x11, 0xbf, 0xfc, 0x0, 0x0, 0x0,
0x0, 0xbf, 0xff, 0xff, 0xff, 0xd2, 0x7, 0xff,
0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4e, 0xf4, 0x0, 0x0, 0x0, 0x0,
0x0, 0xe, 0xff, 0xe0, 0x0, 0x2, 0xcf, 0x70,
0x0, 0x0, 0x0, 0x0, 0x4, 0xee, 0x40, 0x0,
0x0, 0x9, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0
0x3, 0x0, 0x0, 0x14, 0x40, 0x0, 0x0, 0x0,
0x4f, 0x50, 0x9, 0xff, 0xff, 0x80, 0x0, 0x0,
0x8, 0xf6, 0x8, 0x50, 0x17, 0xf8, 0x0, 0x0,
0x0, 0x8f, 0x60, 0x0, 0x0, 0x6f, 0x20, 0x0,
0x0, 0xb, 0xf5, 0x0, 0x0, 0xe, 0x70, 0x0,
0x0, 0xb, 0xef, 0x50, 0x0, 0xc, 0x80, 0x0,
0x0, 0xd, 0x88, 0xf5, 0x0, 0xb, 0xa0, 0x0,
0x0, 0xf, 0x50, 0x8f, 0x50, 0x8, 0xe0, 0x0,
0x0, 0x6f, 0x10, 0x8, 0xf5, 0x2, 0xf5, 0x0,
0x3, 0xf8, 0x0, 0x0, 0x8f, 0x50, 0xa9, 0x0,
0xa, 0xe5, 0x55, 0x55, 0x5d, 0xf5, 0x0, 0x0,
0x5, 0xff, 0xff, 0xff, 0xff, 0xff, 0x50, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf5, 0x0,
0x0, 0x0, 0x0, 0x73, 0x55, 0x0, 0x8f, 0x50,
0x0, 0x0, 0x0, 0xaf, 0xf7, 0x0, 0x8, 0xf1,
0x0, 0x0, 0x0, 0x3, 0x30, 0x0, 0x0, 0x30
};
@@ -87,9 +81,9 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
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 = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 112, .adv_w = 224, .box_w = 16, .box_h = 16, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 240, .adv_w = 320, .box_w = 22, .box_h = 18, .ofs_x = -1, .ofs_y = -3}
{.bitmap_index = 0, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 112, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 224, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}
};
/*---------------------
@@ -153,14 +147,14 @@ lv_font_t cc_icons_16 = {
#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*/
.line_height = 12, /*The maximum line height required by the font*/
.base_line = 0, /*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,
.underline_position = 0,
.underline_thickness = 0,
#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
@@ -172,4 +166,3 @@ lv_font_t cc_icons_16 = {
#endif /*#if CC_ICONS_16*/
+557
View File
@@ -0,0 +1,557 @@
/*******************************************************************************
* Size: 40 px
* Bpp: 4
* Generated by scripts/build/gen-touch-fonts.sh with lv_font_conv 1.5.3
* Sources: Montserrat Light v7.222 (digits and colon)
******************************************************************************/
#if 1
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef CLOCK_FONT_40
#define CLOCK_FONT_40 1
#endif
#if CLOCK_FONT_40
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+0030 "0" */
0x0, 0x0, 0x0, 0x5, 0xad, 0xff, 0xeb, 0x71,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0xff,
0xfe, 0xef, 0xff, 0xf7, 0x0, 0x0, 0x0, 0x0,
0x0, 0x8f, 0xfb, 0x50, 0x0, 0x3, 0x9f, 0xfb,
0x10, 0x0, 0x0, 0x0, 0x7f, 0xf5, 0x0, 0x0,
0x0, 0x0, 0x2d, 0xfc, 0x0, 0x0, 0x0, 0x4f,
0xf3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1d, 0xf9,
0x0, 0x0, 0xd, 0xf6, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2f, 0xf3, 0x0, 0x5, 0xfd, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f, 0xa0,
0x0, 0xcf, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0xff, 0x10, 0x1f, 0xf0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xf6, 0x5,
0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x6f, 0xa0, 0x8f, 0x80, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x3, 0xfd, 0xa, 0xf6,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1f, 0xf0, 0xbf, 0x40, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xff, 0xc, 0xf4, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf,
0xf1, 0xcf, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xff, 0x1b, 0xf4, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xf0,
0xaf, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0xff, 0x8, 0xf8, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0xd0, 0x5f,
0xb0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x6, 0xfa, 0x1, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xbf, 0x60, 0xc, 0xf5,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f,
0xf1, 0x0, 0x5f, 0xd0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x8, 0xfa, 0x0, 0x0, 0xdf, 0x60,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0x30,
0x0, 0x4, 0xff, 0x30, 0x0, 0x0, 0x0, 0x0,
0x1, 0xdf, 0x90, 0x0, 0x0, 0x8, 0xff, 0x50,
0x0, 0x0, 0x0, 0x2, 0xdf, 0xc0, 0x0, 0x0,
0x0, 0x8, 0xff, 0xb4, 0x0, 0x0, 0x39, 0xff,
0xc1, 0x0, 0x0, 0x0, 0x0, 0x4, 0xdf, 0xff,
0xee, 0xff, 0xff, 0x80, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x5a, 0xdf, 0xfe, 0xb7, 0x10, 0x0,
0x0, 0x0,
/* U+0031 "1" */
0xaf, 0xff, 0xff, 0xff, 0xf3, 0x8d, 0xdd, 0xdd,
0xdf, 0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3, 0x0,
0x0, 0x0, 0xc, 0xf3, 0x0, 0x0, 0x0, 0xc,
0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3, 0x0, 0x0,
0x0, 0xc, 0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3,
0x0, 0x0, 0x0, 0xc, 0xf3, 0x0, 0x0, 0x0,
0xc, 0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3, 0x0,
0x0, 0x0, 0xc, 0xf3, 0x0, 0x0, 0x0, 0xc,
0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3, 0x0, 0x0,
0x0, 0xc, 0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3,
0x0, 0x0, 0x0, 0xc, 0xf3, 0x0, 0x0, 0x0,
0xc, 0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3, 0x0,
0x0, 0x0, 0xc, 0xf3, 0x0, 0x0, 0x0, 0xc,
0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3, 0x0, 0x0,
0x0, 0xc, 0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3,
0x0, 0x0, 0x0, 0xc, 0xf3, 0x0, 0x0, 0x0,
0xc, 0xf3, 0x0, 0x0, 0x0, 0xc, 0xf3, 0x0,
0x0, 0x0, 0xc, 0xf3,
/* U+0032 "2" */
0x0, 0x0, 0x2, 0x7b, 0xde, 0xfe, 0xda, 0x60,
0x0, 0x0, 0x0, 0x0, 0x3c, 0xff, 0xff, 0xfe,
0xff, 0xff, 0xe6, 0x0, 0x0, 0x0, 0x9f, 0xfc,
0x72, 0x0, 0x0, 0x15, 0xbf, 0xf9, 0x0, 0x0,
0xbf, 0xe5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f,
0xf6, 0x0, 0x6, 0xd2, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x6f, 0xe0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xef, 0x40, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa,
0xf6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x9f, 0x70, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xa, 0xf6, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdf,
0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x3f, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xb, 0xf7, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xfd, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
0xff, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x3, 0xff, 0x50, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0xef, 0x60, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xef, 0x60,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
0xff, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x3, 0xff, 0x60, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0xff, 0x60, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0x60,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0xff, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x4, 0xff, 0x50, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x4, 0xff, 0x50, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0x50,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0xff, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x3, 0xff, 0xfd, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xb0, 0x5f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
/* U+0033 "3" */
0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf2, 0x0, 0x5d, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xde, 0xfe, 0x10, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2, 0xef, 0x40, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdf,
0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xaf, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x7f, 0xd0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xf2, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e,
0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xd, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xa, 0xfb, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xfd, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0xff, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xcf, 0xff, 0xeb, 0x82, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x8, 0xaa, 0xbd, 0xff,
0xfb, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0x6e, 0xfe, 0x20, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xfe, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xd, 0xf7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x5f, 0xd0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x10,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xe, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xef, 0x20, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xf0, 0x1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6,
0xfc, 0x1, 0xe8, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0xef, 0x50, 0x3f, 0xfc, 0x30, 0x0,
0x0, 0x0, 0x0, 0x2, 0xdf, 0xc0, 0x0, 0x3d,
0xff, 0xc7, 0x20, 0x0, 0x0, 0x49, 0xff, 0xc1,
0x0, 0x0, 0x7, 0xef, 0xff, 0xff, 0xee, 0xff,
0xff, 0x80, 0x0, 0x0, 0x0, 0x0, 0x48, 0xbe,
0xff, 0xfd, 0xa6, 0x10, 0x0, 0x0,
/* U+0034 "4" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7,
0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x5, 0xfe, 0x10, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0xff, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0xdf, 0x50, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xcf, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x9f, 0xb0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x6f, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x4f, 0xe2, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2e, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xd, 0xf6, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xa, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x8, 0xfc, 0x0, 0x0,
0x0, 0x1, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0,
0x5, 0xfe, 0x10, 0x0, 0x0, 0x0, 0x4f, 0xb0,
0x0, 0x0, 0x0, 0x0, 0x3, 0xff, 0x30, 0x0,
0x0, 0x0, 0x4, 0xfb, 0x0, 0x0, 0x0, 0x0,
0x1, 0xef, 0x50, 0x0, 0x0, 0x0, 0x0, 0x4f,
0xb0, 0x0, 0x0, 0x0, 0x0, 0xcf, 0x80, 0x0,
0x0, 0x0, 0x0, 0x4, 0xfb, 0x0, 0x0, 0x0,
0x0, 0x9f, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x4f, 0xb0, 0x0, 0x0, 0x0, 0x7f, 0xd0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x4, 0xfb, 0x0, 0x0,
0x0, 0x2f, 0xfd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
0xcc, 0xdf, 0xec, 0xcc, 0xcc, 0x74, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x5f, 0xb0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xfb,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x5f, 0xb0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5,
0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xb0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x5, 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xb0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x5, 0xfb, 0x0, 0x0, 0x0,
/* U+0035 "5" */
0x0, 0xa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x30, 0x0, 0xc, 0xfd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0x30, 0x0, 0xe, 0xf0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf,
0xe0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1f, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3f, 0xb0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x90, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f,
0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x9f, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xaf, 0x30, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xcf, 0x10, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xef,
0xdd, 0xdd, 0xdc, 0xb9, 0x63, 0x0, 0x0, 0x0,
0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x25,
0x8c, 0xff, 0xd2, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3c, 0xfe, 0x10, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcf, 0xb0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2f, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xb, 0xf7, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x7, 0xf9, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xfa,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x7, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xa, 0xf7, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1e, 0xf3, 0x8e, 0x20,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f, 0xc0,
0x9f, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9,
0xff, 0x20, 0x7, 0xff, 0xea, 0x51, 0x0, 0x0,
0x27, 0xdf, 0xf4, 0x0, 0x0, 0x2a, 0xff, 0xff,
0xfe, 0xef, 0xff, 0xfc, 0x20, 0x0, 0x0, 0x0,
0x16, 0xad, 0xef, 0xfe, 0xc8, 0x30, 0x0, 0x0,
/* U+0036 "6" */
0x0, 0x0, 0x0, 0x1, 0x6a, 0xde, 0xff, 0xec,
0x83, 0x0, 0x0, 0x0, 0x0, 0x19, 0xff, 0xff,
0xed, 0xde, 0xff, 0xfb, 0x0, 0x0, 0x0, 0x4e,
0xfe, 0x83, 0x0, 0x0, 0x1, 0x5b, 0x60, 0x0,
0x0, 0x4f, 0xf9, 0x10, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2f, 0xf6, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xf8, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xbf, 0x60, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xf, 0xf0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfb,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x7f, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xa, 0xf5, 0x0, 0x0, 0x16,
0x9b, 0xbb, 0x95, 0x10, 0x0, 0x0, 0xbf, 0x40,
0x1, 0x9f, 0xff, 0xff, 0xff, 0xff, 0x90, 0x0,
0xc, 0xf4, 0x4, 0xef, 0xb6, 0x21, 0x1, 0x48,
0xff, 0xe3, 0x0, 0xcf, 0x43, 0xfe, 0x40, 0x0,
0x0, 0x0, 0x1, 0xbf, 0xe1, 0xb, 0xf5, 0xde,
0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0xb0,
0xaf, 0xbf, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0xff, 0x39, 0xff, 0xc0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x9, 0xf8, 0x5f, 0xf8, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0xb2,
0xff, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x4, 0xfb, 0xd, 0xf7, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x5f, 0xb0, 0x7f, 0xb0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf8, 0x1,
0xff, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xef, 0x40, 0x7, 0xfc, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x8f, 0xd0, 0x0, 0xb, 0xfb, 0x10,
0x0, 0x0, 0x0, 0x0, 0x7f, 0xf3, 0x0, 0x0,
0xb, 0xfe, 0x82, 0x0, 0x0, 0x4, 0xcf, 0xf4,
0x0, 0x0, 0x0, 0x6, 0xef, 0xff, 0xdc, 0xdf,
0xff, 0xc2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a,
0xde, 0xff, 0xd9, 0x40, 0x0, 0x0,
/* U+0037 "7" */
0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x7a, 0xfe, 0xdd, 0xdd, 0xdd, 0xdd,
0xdd, 0xdd, 0xdd, 0xdf, 0xf5, 0xaf, 0x50, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xfe, 0xa,
0xf5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xaf, 0x80, 0xaf, 0x50, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2f, 0xf1, 0xa, 0xf5, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x9, 0xf9, 0x0, 0xaf,
0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xff,
0x20, 0x2, 0x31, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x8f, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xe, 0xf3, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xfc, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xdf, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x5f, 0xd0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xc, 0xf6, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xfe,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xbf, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2f, 0xf1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xf9, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0xff, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x8f, 0xb0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xe, 0xf4, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xfc,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xdf, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x5f, 0xe0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xf7, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xbf, 0x80, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2f, 0xf1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xfa,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
/* U+0038 "8" */
0x0, 0x0, 0x1, 0x7a, 0xde, 0xff, 0xeb, 0x82,
0x0, 0x0, 0x0, 0x0, 0x1, 0x9f, 0xff, 0xed,
0xcd, 0xef, 0xff, 0xb2, 0x0, 0x0, 0x0, 0x2e,
0xfe, 0x72, 0x0, 0x0, 0x1, 0x6d, 0xff, 0x40,
0x0, 0x0, 0xdf, 0xb0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x8f, 0xf2, 0x0, 0x7, 0xfd, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x9, 0xfb, 0x0, 0xc,
0xf5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0xff, 0x0, 0xe, 0xf2, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xdf, 0x20, 0xe, 0xf1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdf, 0x30,
0xc, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xff, 0x0, 0x7, 0xfc, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x7, 0xfb, 0x0, 0x0, 0xdf,
0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xf2,
0x0, 0x0, 0x2d, 0xfd, 0x50, 0x0, 0x0, 0x0,
0x4b, 0xfe, 0x40, 0x0, 0x0, 0x0, 0x7e, 0xff,
0xdb, 0xab, 0xcf, 0xff, 0x91, 0x0, 0x0, 0x0,
0x0, 0x5b, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x60,
0x0, 0x0, 0x0, 0x3d, 0xfe, 0x95, 0x21, 0x0,
0x14, 0x8d, 0xfe, 0x50, 0x0, 0x4, 0xff, 0x90,
0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0xf6, 0x0,
0x1e, 0xf7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x3, 0xff, 0x30, 0x7f, 0xb0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x7f, 0xb0, 0xcf, 0x50,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f,
0xf0, 0xef, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xd, 0xf2, 0xef, 0x10, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xf3, 0xcf,
0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xf, 0xf1, 0x8f, 0x90, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x5f, 0xd0, 0x2f, 0xf3, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xef, 0x60,
0x8, 0xff, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2d, 0xfc, 0x0, 0x0, 0x9f, 0xfb, 0x51, 0x0,
0x0, 0x0, 0x4a, 0xff, 0xc1, 0x0, 0x0, 0x4,
0xdf, 0xff, 0xed, 0xcd, 0xef, 0xff, 0xe6, 0x0,
0x0, 0x0, 0x0, 0x3, 0x8b, 0xef, 0xff, 0xec,
0x94, 0x0, 0x0, 0x0,
/* U+0039 "9" */
0x0, 0x0, 0x3, 0x9c, 0xef, 0xed, 0xa5, 0x10,
0x0, 0x0, 0x0, 0x0, 0x1b, 0xff, 0xfd, 0xcd,
0xff, 0xff, 0x70, 0x0, 0x0, 0x0, 0x3f, 0xfc,
0x50, 0x0, 0x0, 0x28, 0xef, 0xc0, 0x0, 0x0,
0x2e, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x1, 0xbf,
0xc0, 0x0, 0xb, 0xf9, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xbf, 0x80, 0x3, 0xfe, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0xff, 0x20, 0x7f,
0xa0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9,
0xf8, 0xa, 0xf7, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x6f, 0xe0, 0xaf, 0x60, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x5, 0xff, 0x39, 0xf7,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f,
0xf7, 0x7f, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xb, 0xff, 0xa2, 0xff, 0x10, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0xfb, 0xfb, 0xb, 0xfb,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xde, 0x4f,
0xc0, 0x1e, 0xfb, 0x10, 0x0, 0x0, 0x0, 0x3,
0xdf, 0x42, 0xfd, 0x0, 0x2d, 0xff, 0x94, 0x10,
0x2, 0x5b, 0xff, 0x50, 0x2f, 0xd0, 0x0, 0x8,
0xff, 0xff, 0xff, 0xff, 0xfa, 0x20, 0x3, 0xfc,
0x0, 0x0, 0x0, 0x58, 0xbb, 0xb9, 0x61, 0x0,
0x0, 0x4f, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x7, 0xf9, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaf, 0x50,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xe, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x5, 0xfc, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xcf, 0x50, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f,
0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x5f, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x8f, 0xf5, 0x0, 0x0, 0x5,
0xb5, 0x10, 0x0, 0x0, 0x37, 0xef, 0xe4, 0x0,
0x0, 0x0, 0xaf, 0xff, 0xed, 0xde, 0xff, 0xff,
0xa1, 0x0, 0x0, 0x0, 0x0, 0x38, 0xbe, 0xff,
0xfd, 0xa6, 0x10, 0x0, 0x0, 0x0,
/* U+003A ":" */
0x3d, 0xd3, 0xaf, 0xfa, 0x6f, 0xf6, 0x4, 0x40,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4, 0x40, 0x6f, 0xf6, 0xaf, 0xfa,
0x3d, 0xd3
};
/*---------------------
* 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 = 421, .box_w = 23, .box_h = 28, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 322, .adv_w = 226, .box_w = 10, .box_h = 28, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 462, .adv_w = 360, .box_w = 21, .box_h = 28, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 756, .adv_w = 357, .box_w = 21, .box_h = 28, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 1050, .adv_w = 419, .box_w = 25, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1400, .adv_w = 357, .box_w = 20, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1680, .adv_w = 385, .box_w = 21, .box_h = 28, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1974, .adv_w = 372, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2268, .adv_w = 404, .box_w = 22, .box_h = 28, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 2576, .adv_w = 385, .box_w = 21, .box_h = 28, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2870, .adv_w = 128, .box_w = 4, .box_h = 21, .ofs_x = 2, .ofs_y = 0}
};
/*---------------------
* CHARACTER MAPPING
*--------------------*/
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
{
.range_start = 48, .range_length = 11, .glyph_id_start = 1,
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
}
};
/*-----------------
* KERNING
*----------------*/
/*Map glyph_ids to kern left classes*/
static const uint8_t kern_left_class_mapping[] =
{
0, 1, 0, 2, 3, 4, 5, 6,
7, 0, 1, 8
};
/*Map glyph_ids to kern right classes*/
static const uint8_t kern_right_class_mapping[] =
{
0, 1, 2, 3, 4, 5, 6, 1,
7, 8, 9, 10
};
/*Kern values between classes*/
static const int8_t kern_class_values[] =
{
0, 0, 0, -6, 0, 0, -6, 0,
0, 0, -2, 0, 0, 0, -17, 0,
-2, 0, 0, 0, 0, 0, -3, -3,
0, -6, -9, 0, 0, 0, 0, -16,
-2, -11, 0, -2, -33, 6, -6, 4,
0, 0, -3, -3, 0, -3, -9, 0,
0, 0, 0, 0, 0, 0, 0, 0,
-6, 0, 0, 0, -13, 6, 0, -6,
-42, -12, 0, -12, 0, -13, 0, 0,
0, 0, 0, 0, 6, 0, 0, 0
};
/*Collect the kern class' data in one place*/
static const lv_font_fmt_txt_kern_classes_t kern_classes =
{
.class_pair_values = kern_class_values,
.left_class_mapping = kern_left_class_mapping,
.right_class_mapping = kern_right_class_mapping,
.left_class_cnt = 8,
.right_class_cnt = 10,
};
/*--------------------
* 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 = &kern_classes,
.kern_scale = 16,
.cmap_num = 1,
.bpp = 4,
.kern_classes = 1,
.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 clock_font_40 = {
#else
lv_font_t clock_font_40 = {
#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 = 28, /*The maximum line height required by the font*/
.base_line = 0, /*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 = -3,
.underline_thickness = 2,
#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 CLOCK_FONT_40*/
+706
View File
@@ -0,0 +1,706 @@
/*******************************************************************************
* Size: 48 px
* Bpp: 4
* Generated by scripts/build/gen-touch-fonts.sh with lv_font_conv 1.5.3
* Sources: Montserrat Light v7.222 (digits and colon)
******************************************************************************/
#if 1
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef CLOCK_FONT_48
#define CLOCK_FONT_48 1
#endif
#if CLOCK_FONT_48
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+0030 "0" */
0x0, 0x0, 0x0, 0x0, 0x3, 0x9c, 0xef, 0xfd,
0xb7, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x4d, 0xff, 0xff, 0xff, 0xff, 0xff, 0x91,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaf, 0xff,
0xc8, 0x54, 0x46, 0x9e, 0xff, 0xf4, 0x0, 0x0,
0x0, 0x0, 0x0, 0xcf, 0xfb, 0x30, 0x0, 0x0,
0x0, 0x6, 0xef, 0xf6, 0x0, 0x0, 0x0, 0x0,
0xbf, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0xcf, 0xf4, 0x0, 0x0, 0x0, 0x7f, 0xf7, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf, 0xe1,
0x0, 0x0, 0x1f, 0xfb, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0xff, 0x90, 0x0, 0x9,
0xff, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x8, 0xff, 0x20, 0x0, 0xff, 0x90, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f,
0xf8, 0x0, 0x5f, 0xf3, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x9f, 0xe0, 0xa,
0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4, 0xff, 0x30, 0xdf, 0x90, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xf, 0xf7, 0xf, 0xf6, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdf, 0x92,
0xff, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xb, 0xfc, 0x4f, 0xf2, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x9f, 0xd5, 0xff, 0x20, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xfe,
0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x8f, 0xe5, 0xff, 0x20,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x8, 0xfe, 0x4f, 0xf2, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f,
0xd2, 0xff, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xb, 0xfc, 0xf, 0xf6,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xdf, 0x90, 0xdf, 0xa0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf,
0xf7, 0xa, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0x30, 0x5f,
0xf3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x9f, 0xe0, 0x0, 0xff, 0x90, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f,
0xf8, 0x0, 0x9, 0xff, 0x10, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x8, 0xff, 0x20, 0x0,
0x1f, 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0xff, 0xa0, 0x0, 0x0, 0x7f, 0xf7,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf,
0xe1, 0x0, 0x0, 0x0, 0xbf, 0xf7, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0xcf, 0xf4, 0x0, 0x0,
0x0, 0x0, 0xcf, 0xfb, 0x30, 0x0, 0x0, 0x0,
0x6, 0xef, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x0,
0xaf, 0xff, 0xc8, 0x54, 0x46, 0x9e, 0xff, 0xf4,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0xff,
0xff, 0xff, 0xff, 0xff, 0x91, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x3, 0x9c, 0xef, 0xfe,
0xb7, 0x10, 0x0, 0x0, 0x0, 0x0,
/* U+0031 "1" */
0x9f, 0xff, 0xff, 0xff, 0xff, 0xf1, 0x9f, 0xff,
0xff, 0xff, 0xff, 0xf1, 0x13, 0x33, 0x33, 0x33,
0x7f, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0,
0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0,
0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0,
0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0,
0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0,
0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0,
0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0,
0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0,
0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0,
0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0,
0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0,
0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0,
0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0,
0x0, 0x0, 0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0,
0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1,
0x0, 0x0, 0x0, 0x0, 0x5f, 0xf1,
/* U+0032 "2" */
0x0, 0x0, 0x0, 0x59, 0xce, 0xff, 0xfe, 0xb8,
0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xd4, 0x0, 0x0,
0x0, 0x0, 0x9f, 0xff, 0xfb, 0x76, 0x44, 0x46,
0x9d, 0xff, 0xf9, 0x0, 0x0, 0x1, 0xcf, 0xfe,
0x60, 0x0, 0x0, 0x0, 0x0, 0x4, 0xdf, 0xfa,
0x0, 0x0, 0xaf, 0xf8, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xbf, 0xf5, 0x0, 0x1, 0xb6,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0xef, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x8, 0xff, 0x20, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x3f, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xff, 0x60,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2f, 0xf5, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff,
0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x8f, 0xf0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe,
0xfa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x6, 0xff, 0x30, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0xff, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0xdf, 0xe1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xcf, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xbf, 0xf4, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xcf, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0xcf, 0xf4, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0xcf, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0xdf, 0xe3, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
0xdf, 0xe3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0xdf, 0xe2, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0xef, 0xe2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0xef, 0xe2, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
0xef, 0xd2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0xef, 0xd1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
0xef, 0xd1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0xff, 0xd1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0xff, 0xe4, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
0x33, 0x33, 0x33, 0x0, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1,
0xf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x10,
/* U+0033 "3" */
0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x0, 0x2f, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
0x0, 0x0, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
0x33, 0x33, 0x3a, 0xff, 0x40, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff,
0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x2, 0xff, 0xa0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf,
0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xcf, 0xe1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f,
0xf3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x6f, 0xf6, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f,
0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2e, 0xfb, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd,
0xfd, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xb, 0xff, 0x20, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8,
0xff, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0xff, 0xfe, 0xdc, 0x95,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2f, 0xff, 0xff, 0xff, 0xff, 0x91, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x25,
0x9e, 0xff, 0xe3, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xff, 0xf3,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0xff, 0xd0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5,
0xff, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xd, 0xfc, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x7f, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xff, 0x20,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4f, 0xf3, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xff,
0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x8f, 0xf0, 0x1, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe,
0xfb, 0x0, 0xab, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x7, 0xff, 0x50, 0x2f, 0xfd,
0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6,
0xff, 0xc0, 0x0, 0x6f, 0xff, 0xc5, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2a, 0xff, 0xe1, 0x0, 0x0,
0x3c, 0xff, 0xff, 0xb7, 0x54, 0x34, 0x58, 0xcf,
0xff, 0xc1, 0x0, 0x0, 0x0, 0x5, 0xbf, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfe, 0x60, 0x0, 0x0,
0x0, 0x0, 0x0, 0x16, 0x9c, 0xef, 0xff, 0xec,
0x84, 0x0, 0x0, 0x0, 0x0,
/* U+0034 "4" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2, 0xff, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf,
0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0xe1, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x9f, 0xf3, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x6f, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f,
0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0xfb, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xc, 0xfd, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xa, 0xfe, 0x20, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7,
0xff, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xff, 0x70,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0xff, 0xa0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1, 0xdf, 0xc0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xcf, 0xe1, 0x0, 0x0, 0x0, 0x0, 0x57, 0x40,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f, 0xf3,
0x0, 0x0, 0x0, 0x0, 0xb, 0xf9, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x6f, 0xf5, 0x0, 0x0,
0x0, 0x0, 0x0, 0xbf, 0x90, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4f, 0xf8, 0x0, 0x0, 0x0, 0x0,
0x0, 0xb, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2e, 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xbf, 0x90, 0x0, 0x0, 0x0, 0x0, 0xd, 0xfd,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xf9,
0x0, 0x0, 0x0, 0x0, 0xa, 0xfe, 0x20, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0x90, 0x0,
0x0, 0x0, 0x8, 0xff, 0x40, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xb, 0xf9, 0x0, 0x0, 0x0,
0x5, 0xff, 0x92, 0x22, 0x22, 0x22, 0x22, 0x22,
0x22, 0x22, 0xcf, 0xa2, 0x22, 0x22, 0x21, 0xef,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfa, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xf9, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xcf, 0x90, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xc, 0xf9, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xcf, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc,
0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcf, 0x90,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xc, 0xf9, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xcf, 0x90, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xc, 0xf9, 0x0, 0x0, 0x0, 0x0,
/* U+0035 "5" */
0x0, 0x0, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x10, 0x0, 0x0, 0xef, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10,
0x0, 0x0, 0xff, 0x63, 0x33, 0x33, 0x33, 0x33,
0x33, 0x33, 0x33, 0x0, 0x0, 0x1, 0xff, 0x30,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x3, 0xff, 0x10, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x7, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xfb, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xa, 0xfa, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xf8, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xe, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xf4, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x1f, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0xf4, 0x33,
0x32, 0x21, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xda,
0x72, 0x0, 0x0, 0x0, 0x0, 0x7f, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xc4, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x24, 0x6a,
0xef, 0xff, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x5, 0xdf, 0xfb, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xa, 0xff, 0x70, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcf, 0xf1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3f, 0xf5, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xfa,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xb, 0xfb, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xfc,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xb, 0xfb, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xf9,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4f, 0xf4, 0x1d, 0x40, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdf, 0xe0,
0x8f, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xb, 0xff, 0x50, 0xb, 0xff, 0xe8, 0x20,
0x0, 0x0, 0x0, 0x0, 0x5, 0xef, 0xf9, 0x0,
0x0, 0x6e, 0xff, 0xfd, 0x96, 0x44, 0x45, 0x6a,
0xef, 0xff, 0x80, 0x0, 0x0, 0x1, 0x7d, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xb3, 0x0, 0x0,
0x0, 0x0, 0x0, 0x37, 0xad, 0xef, 0xff, 0xdb,
0x72, 0x0, 0x0, 0x0,
/* U+0036 "6" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x48, 0xbe, 0xff,
0xfe, 0xc9, 0x51, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x90, 0x0, 0x0, 0x0, 0x0, 0x4e, 0xff, 0xfb,
0x74, 0x32, 0x23, 0x58, 0xdf, 0xc0, 0x0, 0x0,
0x0, 0x7, 0xff, 0xf7, 0x0, 0x0, 0x0, 0x0,
0x0, 0x3, 0x20, 0x0, 0x0, 0x0, 0x7f, 0xfc,
0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x4, 0xff, 0xa0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd,
0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x7f, 0xf2, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xef, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0x20,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x9, 0xfd, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd,
0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xf, 0xf6, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2f, 0xf4, 0x0, 0x0, 0x3, 0x8c, 0xef,
0xfe, 0xc9, 0x50, 0x0, 0x0, 0x0, 0x4f, 0xf3,
0x0, 0x4, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xfe,
0x70, 0x0, 0x0, 0x4f, 0xf2, 0x0, 0x9f, 0xfe,
0x84, 0x21, 0x23, 0x6a, 0xff, 0xfd, 0x20, 0x0,
0x5f, 0xf1, 0xa, 0xff, 0x60, 0x0, 0x0, 0x0,
0x0, 0x19, 0xff, 0xe1, 0x0, 0x5f, 0xf2, 0x7f,
0xd1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f,
0xfc, 0x0, 0x4f, 0xf4, 0xfe, 0x20, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x8, 0xff, 0x50, 0x3f,
0xfc, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xef, 0xb0, 0x1f, 0xff, 0xe0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f,
0xf0, 0xe, 0xff, 0x90, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x4f, 0xf3, 0xb, 0xff,
0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2f, 0xf4, 0x7, 0xff, 0x70, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0xf4,
0x2, 0xff, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x4f, 0xf3, 0x0, 0xbf, 0xe0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x8f, 0xf0, 0x0, 0x4f, 0xf5, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xef, 0xb0, 0x0,
0xa, 0xfe, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x8, 0xff, 0x40, 0x0, 0x1, 0xef, 0xd1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0xfa,
0x0, 0x0, 0x0, 0x3f, 0xfe, 0x60, 0x0, 0x0,
0x0, 0x0, 0x19, 0xff, 0xd1, 0x0, 0x0, 0x0,
0x2, 0xdf, 0xfe, 0x95, 0x21, 0x23, 0x5a, 0xff,
0xfc, 0x10, 0x0, 0x0, 0x0, 0x0, 0x7, 0xef,
0xff, 0xff, 0xff, 0xff, 0xfe, 0x60, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x5, 0x9c, 0xef, 0xfe,
0xc9, 0x50, 0x0, 0x0, 0x0,
/* U+0037 "7" */
0x6f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xb6, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfb, 0x6f, 0xf3, 0x33, 0x33, 0x33, 0x33, 0x33,
0x33, 0x33, 0x33, 0x36, 0xff, 0x56, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xbf, 0xe0, 0x6f, 0xf0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2f, 0xf7, 0x6, 0xff,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xa, 0xfe, 0x0, 0x6f, 0xf0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0x80, 0x6,
0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x9f, 0xf1, 0x0, 0x38, 0x80, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xf9, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x8, 0xff, 0x20, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xef, 0xa0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x6f, 0xf3, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xfc,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x5, 0xff, 0x40, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdf,
0xd0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x4f, 0xf5, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc,
0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0xff, 0x70, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xaf, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2f, 0xf8, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x9, 0xff, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0xff, 0x90, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x8f, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xfa, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x7, 0xff, 0x30, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xef, 0xb0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x6f, 0xf4, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xfd,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x5, 0xff, 0x50, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcf,
0xe0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3f, 0xf6, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb,
0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2, 0xff, 0x80, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0,
/* U+0038 "8" */
0x0, 0x0, 0x0, 0x2, 0x7a, 0xde, 0xff, 0xed,
0xb8, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x60,
0x0, 0x0, 0x0, 0x0, 0x9f, 0xff, 0xc7, 0x42,
0x21, 0x24, 0x7b, 0xff, 0xfc, 0x10, 0x0, 0x0,
0xb, 0xff, 0xb2, 0x0, 0x0, 0x0, 0x0, 0x0,
0x19, 0xff, 0xe1, 0x0, 0x0, 0x6f, 0xf9, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xfb,
0x0, 0x0, 0xef, 0xc0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x8, 0xff, 0x30, 0x2, 0xff,
0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0xff, 0x80, 0x4, 0xff, 0x20, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdf, 0xa0,
0x5, 0xff, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xdf, 0xa0, 0x3, 0xff, 0x50,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xff, 0x80, 0x0, 0xef, 0xb0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x6, 0xff, 0x30, 0x0,
0x6f, 0xf7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2f, 0xfa, 0x0, 0x0, 0x9, 0xff, 0x91,
0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xff, 0xd1,
0x0, 0x0, 0x0, 0x7f, 0xff, 0xa5, 0x31, 0x0,
0x12, 0x59, 0xef, 0xfa, 0x0, 0x0, 0x0, 0x0,
0x1, 0x8e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa,
0x30, 0x0, 0x0, 0x0, 0x0, 0x4, 0xaf, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfc, 0x60, 0x0, 0x0,
0x0, 0x2, 0xcf, 0xff, 0x96, 0x31, 0x0, 0x12,
0x58, 0xdf, 0xfd, 0x40, 0x0, 0x0, 0x3e, 0xff,
0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xdf,
0xf6, 0x0, 0x1, 0xef, 0xe2, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xb, 0xff, 0x40, 0xa,
0xff, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xcf, 0xe0, 0x1f, 0xf8, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f,
0xf6, 0x5f, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xd, 0xfa, 0x7f, 0xf0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xa, 0xfd, 0x8f, 0xe0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xfd,
0x7f, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xa, 0xfc, 0x5f, 0xf3, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xe, 0xfa, 0xf, 0xf9, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xf5, 0x9,
0xff, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xdf, 0xe0, 0x1, 0xef, 0xe3, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0xff,
0x50, 0x0, 0x3f, 0xff, 0x81, 0x0, 0x0, 0x0,
0x0, 0x0, 0x6, 0xef, 0xf7, 0x0, 0x0, 0x3,
0xdf, 0xff, 0xb7, 0x42, 0x21, 0x23, 0x6a, 0xff,
0xff, 0x60, 0x0, 0x0, 0x0, 0x7, 0xdf, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x91, 0x0, 0x0,
0x0, 0x0, 0x0, 0x3, 0x7a, 0xde, 0xff, 0xed,
0xb8, 0x50, 0x0, 0x0, 0x0,
/* U+0039 "9" */
0x0, 0x0, 0x0, 0x5, 0xad, 0xef, 0xfe, 0xc9,
0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8,
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x60, 0x0,
0x0, 0x0, 0x0, 0x3, 0xdf, 0xfe, 0x95, 0x31,
0x23, 0x59, 0xef, 0xfc, 0x10, 0x0, 0x0, 0x0,
0x3f, 0xff, 0x70, 0x0, 0x0, 0x0, 0x0, 0x6,
0xef, 0xe2, 0x0, 0x0, 0x0, 0xdf, 0xe3, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0xfd, 0x0,
0x0, 0x7, 0xff, 0x40, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0xef, 0x80, 0x0, 0xe, 0xfa,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x5f, 0xf2, 0x0, 0x2f, 0xf5, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xf9, 0x0,
0x5f, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xa, 0xff, 0x0, 0x6f, 0xf0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8,
0xff, 0x50, 0x5f, 0xf1, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x9, 0xff, 0x90, 0x3f,
0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xd, 0xff, 0xc0, 0xf, 0xfa, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xef,
0xf0, 0x9, 0xff, 0x20, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x1, 0xdf, 0x7f, 0xf1, 0x1, 0xff,
0xe1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc,
0xf8, 0x4f, 0xf2, 0x0, 0x5f, 0xfe, 0x50, 0x0,
0x0, 0x0, 0x0, 0x4, 0xef, 0xc0, 0x3f, 0xf3,
0x0, 0x5, 0xff, 0xfd, 0x74, 0x10, 0x2, 0x38,
0xdf, 0xfb, 0x0, 0x3f, 0xf3, 0x0, 0x0, 0x2b,
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x60, 0x0,
0x4f, 0xf3, 0x0, 0x0, 0x0, 0x28, 0xce, 0xff,
0xff, 0xda, 0x50, 0x0, 0x0, 0x5f, 0xf2, 0x0,
0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x6f, 0xf0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f,
0xe0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xcf, 0xb0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xff, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xff, 0x20,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xc, 0xfc, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f,
0xf5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1, 0xef, 0xc0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1c, 0xff, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x2, 0xdf, 0xf5, 0x0,
0x0, 0x0, 0x4, 0x40, 0x0, 0x0, 0x0, 0x0,
0x1, 0x9f, 0xff, 0x50, 0x0, 0x0, 0x0, 0xe,
0xfd, 0x85, 0x32, 0x23, 0x57, 0xcf, 0xff, 0xd3,
0x0, 0x0, 0x0, 0x0, 0x1a, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xe6, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x16, 0xac, 0xef, 0xff, 0xdb, 0x83,
0x0, 0x0, 0x0, 0x0, 0x0,
/* U+003A ":" */
0x7, 0xed, 0x21, 0xff, 0xfb, 0x1f, 0xff, 0xb0,
0x7f, 0xd2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x7f, 0xd2, 0x2f,
0xff, 0xb1, 0xff, 0xfb, 0x7, 0xed, 0x20
};
/*---------------------
* 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 = 505, .box_w = 27, .box_h = 33, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 446, .adv_w = 271, .box_w = 12, .box_h = 33, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 644, .adv_w = 432, .box_w = 25, .box_h = 33, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1057, .adv_w = 429, .box_w = 25, .box_h = 33, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 1470, .adv_w = 502, .box_w = 29, .box_h = 33, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1949, .adv_w = 429, .box_w = 24, .box_h = 33, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2345, .adv_w = 462, .box_w = 26, .box_h = 33, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 2774, .adv_w = 446, .box_w = 25, .box_h = 33, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3187, .adv_w = 485, .box_w = 26, .box_h = 33, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 3616, .adv_w = 462, .box_w = 26, .box_h = 33, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 4045, .adv_w = 154, .box_w = 5, .box_h = 25, .ofs_x = 2, .ofs_y = 0}
};
/*---------------------
* CHARACTER MAPPING
*--------------------*/
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
{
.range_start = 48, .range_length = 11, .glyph_id_start = 1,
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
}
};
/*-----------------
* KERNING
*----------------*/
/*Map glyph_ids to kern left classes*/
static const uint8_t kern_left_class_mapping[] =
{
0, 1, 0, 2, 3, 4, 5, 6,
7, 0, 1, 8
};
/*Map glyph_ids to kern right classes*/
static const uint8_t kern_right_class_mapping[] =
{
0, 1, 2, 3, 4, 5, 6, 1,
7, 8, 9, 10
};
/*Kern values between classes*/
static const int8_t kern_class_values[] =
{
0, 0, 0, -8, 0, 0, -7, 0,
0, 0, -2, 0, 0, 0, -20, 0,
-2, 0, 0, 0, 0, 0, -4, -4,
0, -8, -11, 0, 0, 0, 0, -19,
-2, -13, 0, -2, -40, 8, -7, 5,
0, 0, -4, -4, 0, -4, -11, 0,
0, 0, 0, 0, 0, 0, 0, 0,
-7, 0, 0, 0, -15, 8, 0, -7,
-50, -15, 0, -15, 0, -15, 0, 0,
0, 0, 0, 0, 7, 0, 0, 0
};
/*Collect the kern class' data in one place*/
static const lv_font_fmt_txt_kern_classes_t kern_classes =
{
.class_pair_values = kern_class_values,
.left_class_mapping = kern_left_class_mapping,
.right_class_mapping = kern_right_class_mapping,
.left_class_cnt = 8,
.right_class_cnt = 10,
};
/*--------------------
* 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 = &kern_classes,
.kern_scale = 16,
.cmap_num = 1,
.bpp = 4,
.kern_classes = 1,
.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 clock_font_48 = {
#else
lv_font_t clock_font_48 = {
#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 = 33, /*The maximum line height required by the font*/
.base_line = 0, /*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 = -4,
.underline_thickness = 2,
#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 CLOCK_FONT_48*/
File diff suppressed because it is too large. Load diff
+14 -4
View File
@@ -162,7 +162,7 @@
#elif defined(ATTAKY_MESH_SERIES)
#define CAP_TOUCH 1
#define CAP_ROTATABLE 0
#define CAP_ROTATABLE 1 // landscape (ROT_90) by default; portrait for the build without a keyboard
#define CAP_LARGE_SCREEN 0
#define CAP_SD 0
#define CAP_FILESYSTEM 0
@@ -196,9 +196,10 @@
#endif
// Persisted, restart-to-apply UI-size selector. Large-screen boards already
// expose it; the Pager, V4-R8 and ThinkNode M9 add font-only presets because
// their compact viewports cannot safely take global geometry scaling.
#if CAP_LARGE_SCREEN || defined(TLORA_PAGER) || defined(HELTEC_LORA_V4_R8) || defined(HAS_THINKNODE_M9)
// expose it; the Pager, V4-R8, ThinkNode M9 and T-Deck add font-only presets
// because their compact viewports cannot safely take global geometry scaling.
// (The V4 stays without: its flash has no room for the bigger faces.)
#if CAP_LARGE_SCREEN || defined(TLORA_PAGER) || defined(HELTEC_LORA_V4_R8) || defined(HAS_THINKNODE_M9) || defined(HAS_TDECK_GT911)
#define CAP_UI_SIZE 1
#else
#define CAP_UI_SIZE 0
@@ -300,6 +301,15 @@
#define CAP_ROUND_CORNERS 0
#endif
// The Constellation screensaver (Constellation.cpp) animates continuously. E-paper
// cannot show that, and refreshing it to try would wear the panel, so the T-Deck
// Pro and Max (both HAS_TDECK_PRO) go without. Every other display gets it.
#if defined(HAS_TDECK_PRO)
#define CAP_SCREENSAVER 0
#else
#define CAP_SCREENSAVER 1
#endif
// ---- Capabilities aliased to existing device-neutral macros -----------------
// (behaviour-identical; lets new code use CAP_* while old sites migrate lazily)
#if defined(HAS_TDECK_TRACKBALL)
+70
View File
@@ -174,6 +174,8 @@ static const I18nPair kBuiltin_hu[] = {
{ "Compact", "Kompakt" },
{ "Compact messages", "Kompakt üzenetek" },
{ "Compact messages (IRC style)", "Kompakt üzenetek (IRC stílus)" },
{ "Condense Nav (restart to apply)", "Tömör navigáció (újraindítás az alkalmazáshoz)" },
{ "Condense Nav saved — restart to apply", "Tömör navigáció mentve – indítsa újra az alkalmazáshoz" },
{ "Connect", "Csatlakozás" },
{ "Connect Wi-Fi to reload tiles", "Csatlakozz Wi-Fi-hez a csempék újratöltéséhez" },
{ "Connect to Wi-Fi", "Csatlakozás Wi-Fi-hez" },
@@ -420,11 +422,13 @@ static const I18nPair kBuiltin_hu[] = {
{ "Keyboard light on", "Billentyűzetfény be" },
{ "Keyboard nav: WASDZ on", "Billentyűzet navigáció: WASDZ bekapcsolva" },
{ "Keyboard nav: off", "Billentyűzetnavigáció: kikapcsolva" },
{ "Keyboard nav: on", "Billentyűzetnavigáció: bekapcsolva" },
{ "Keyboard navigation", "Billentyűzetnavigáció" },
{ "Keys", "Kulcsok" },
{ "Known regions", "Ismert régiók" },
{ "LOS → %s", "Rálátás → %s" },
{ "Landscape (desktop)", "Fekvő (asztali)" },
{ "Landscape only", "Csak fekvő tájolásban" },
{ "Landscape set - applies when remote mode starts.", "Fekvő beállítva - a távoli mód indulásakor lép életbe." },
{ "Language", "Nyelv" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "A nyelvi katalógus nem érhető el - ellenőrizd a Wi-Fit, majd nyisd meg újra a lapot." },
@@ -436,6 +440,7 @@ static const I18nPair kBuiltin_hu[] = {
{ "Leaving remote mode - rebooting...", "Kilépés a távoli módból - újraindítás..." },
{ "Left", "Balra" },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Állítsuk be az eszközt.\n\nVálassz egy nevet, és válaszd ki a LoRa régiódat. Körülbelül egy percet vesz igénybe." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "A betűk mozgatják a kijelölést, ha nincs aktív szövegmező. Az Enter kiválaszt; a Backspace nyomva tartása visszalép." },
{ "Letters only", "Csak betűk" },
{ "Loading catalog…", "Katalógus betöltése…" },
{ "Loading map tiles…\n\nKeep Wi-Fi connected.\nTiles appear as they arrive.", "Térképcsempék betöltése…\n\nWi-Fi kapcsolaton keresztül.\nA csempék érkezésükkor jelennek meg." },
@@ -1195,6 +1200,8 @@ static const I18nPair kBuiltin_nl[] = {
{ "Community preset (meshcomod web)", "Community-preset (meshcomod web)" },
{ "Compact messages", "Compacte berichten" },
{ "Compact messages (IRC style)", "Compacte berichten (IRC-stijl)" },
{ "Condense Nav (restart to apply)", "Compacte navigatie (herstart om toe te passen)" },
{ "Condense Nav saved — restart to apply", "Compacte navigatie opgeslagen — herstart om toe te passen" },
{ "Connect", "Verbinden" },
{ "Connect Wi-Fi to reload tiles", "Verbind wifi om tegels te herladen" },
{ "Connect to Wi-Fi", "Verbinden met wifi" },
@@ -1429,10 +1436,12 @@ static const I18nPair kBuiltin_nl[] = {
{ "Keyboard light on", "Toetsenbordverlichting aan" },
{ "Keyboard nav: WASDZ on", "Toetsenbordnav.: WASDZ aan" },
{ "Keyboard nav: off", "Toetsenbordnav.: uit" },
{ "Keyboard nav: on", "Toetsenbordnav.: aan" },
{ "Keyboard navigation", "Toetsenbordnavigatie" },
{ "Keys", "Toetsen" },
{ "LOS → %s", "Zichtlijn → %s" },
{ "Landscape (desktop)", "Liggend (desktop)" },
{ "Landscape only", "Alleen liggend" },
{ "Landscape set - applies when remote mode starts.", "Liggend ingesteld - geldt zodra de externe modus start." },
{ "Language", "Taal" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Taalcatalogus niet beschikbaar - controleer Wi-Fi en open dit tabblad opnieuw." },
@@ -1444,6 +1453,7 @@ static const I18nPair kBuiltin_nl[] = {
{ "Leaving remote mode - rebooting...", "Externe modus verlaten - herstarten..." },
{ "Left", "Links" },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Laten we je apparaat instellen.\n\nJe kiest een naam en je LoRa-regio. Duurt ongeveer een minuut." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Letters verplaatsen de markering als er geen tekstveld actief is. Enter selecteert; houd Backspace ingedrukt om terug te gaan." },
{ "Letters only", "Alleen letters" },
{ "LoRa antenna (P4)", "LoRa-antenne (P4)" },
{ "Loading catalog…", "Catalogus laden…" },
@@ -2126,6 +2136,8 @@ static const I18nPair kBuiltin_de[] = {
{ "Compact", "Kompakt" },
{ "Compact messages", "Kompakte Nachrichten" },
{ "Compact messages (IRC style)", "Kompakte Nachrichten (IRC-Stil)" },
{ "Condense Nav (restart to apply)", "Kompakte Navigation (Neustart nötig)" },
{ "Condense Nav saved — restart to apply", "Kompakte Navigation gespeichert — Neustart zum Anwenden" },
{ "Connect", "Verbinden" },
{ "Connect Wi-Fi to reload tiles", "WLAN verbinden, um Kacheln neu zu laden" },
{ "Connect to Wi-Fi", "Mit WLAN verbinden" },
@@ -2361,10 +2373,12 @@ static const I18nPair kBuiltin_de[] = {
{ "Keyboard light on", "Tastaturlicht ein" },
{ "Keyboard nav: WASDZ on", "Tastaturnav.: WASDZ an" },
{ "Keyboard nav: off", "Tastaturnav.: aus" },
{ "Keyboard nav: on", "Tastaturnav.: an" },
{ "Keyboard navigation", "Tastaturnavigation" },
{ "Keys", "Tasten" },
{ "LOS → %s", "Sichtlinie → %s" },
{ "Landscape (desktop)", "Querformat (Desktop)" },
{ "Landscape only", "Nur im Querformat" },
{ "Landscape set - applies when remote mode starts.", "Querformat gesetzt - gilt beim Start des Fernmodus." },
{ "Language", "Sprache" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Sprachkatalog nicht verfügbar - Wi-Fi prüfen, dann den Tab neu öffnen." },
@@ -2376,6 +2390,7 @@ static const I18nPair kBuiltin_de[] = {
{ "Leaving remote mode - rebooting...", "Fernmodus wird beendet - Neustart..." },
{ "Left", "Links" },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Richten wir dein Gerät ein.\n\nDu wählst einen Namen und deine LoRa-Region. Dauert etwa eine Minute." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Buchstaben bewegen die Markierung, wenn kein Textfeld aktiv ist. Enter wählt aus; Rücktaste halten geht zurück." },
{ "Letters only", "Nur Buchstaben" },
{ "LoRa antenna (P4)", "LoRa-Antenne (P4)" },
{ "Loading catalog…", "Katalog wird geladen…" },
@@ -3060,6 +3075,8 @@ static const I18nPair kBuiltin_fr[] = {
{ "Community preset (meshcomod web)", "Préréglage communauté (meshcomod web)" },
{ "Compact messages", "Messages compacts" },
{ "Compact messages (IRC style)", "Messages compacts (style IRC)" },
{ "Condense Nav (restart to apply)", "Navigation compacte (redémarrer pour appliquer)" },
{ "Condense Nav saved — restart to apply", "Navigation compacte enregistrée — redémarrez pour appliquer" },
{ "Connect", "Connecter" },
{ "Connect Wi-Fi to reload tiles", "Connectez le Wi-Fi pour recharger les tuiles" },
{ "Connect to Wi-Fi", "Se connecter au Wi-Fi" },
@@ -3279,9 +3296,11 @@ static const I18nPair kBuiltin_fr[] = {
{ "Keyboard light on", "Éclairage clavier activé" },
{ "Keyboard nav: WASDZ on", "Nav. clavier : WASDZ activé" },
{ "Keyboard nav: off", "Nav. clavier : désactivé" },
{ "Keyboard nav: on", "Nav. clavier : activé" },
{ "Keyboard navigation", "Navigation clavier" },
{ "LOS → %s", "Vue directe → %s" },
{ "Landscape (desktop)", "Paysage (bureau)" },
{ "Landscape only", "Paysage uniquement" },
{ "Landscape set - applies when remote mode starts.", "Paysage défini - s'applique au démarrage du mode distant." },
{ "Language", "Langue" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Catalogue de langues indisponible - vérifiez le Wi-Fi puis rouvrez cet onglet." },
@@ -3290,6 +3309,7 @@ static const I18nPair kBuiltin_fr[] = {
{ "Leave empty if not required", "Laisser vide si non requis" },
{ "Leaving remote mode - rebooting...", "Sortie du mode distant - redémarrage..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Configurons votre appareil.\n\nVous choisirez un nom et votre région LoRa. Cela prend environ une minute." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Les lettres déplacent la sélection quand aucun champ de texte n'est actif. Entrée sélectionne ; maintenez Retour arrière pour revenir." },
{ "Letters only", "Lettres uniquement" },
{ "LoRa antenna (P4)", "Antenne LoRa (P4)" },
{ "Loading catalog…", "Chargement du catalogue…" },
@@ -3950,6 +3970,8 @@ static const I18nPair kBuiltin_es[] = {
{ "Community preset (meshcomod web)", "Preajuste comunidad (meshcomod web)" },
{ "Compact messages", "Mensajes compactos" },
{ "Compact messages (IRC style)", "Mensajes compactos (estilo IRC)" },
{ "Condense Nav (restart to apply)", "Navegación compacta (reiniciar para aplicar)" },
{ "Condense Nav saved — restart to apply", "Navegación compacta guardada — reinicia para aplicar" },
{ "Connect", "Conectar" },
{ "Connect Wi-Fi to reload tiles", "Conecta Wi-Fi para recargar teselas" },
{ "Connect to Wi-Fi", "Conectar a Wi-Fi" },
@@ -4170,9 +4192,11 @@ static const I18nPair kBuiltin_es[] = {
{ "Keyboard light on", "Luz del teclado encendida" },
{ "Keyboard nav: WASDZ on", "Nav. teclado: WASDZ act." },
{ "Keyboard nav: off", "Nav. teclado: desact." },
{ "Keyboard nav: on", "Nav. teclado: activ." },
{ "Keyboard navigation", "Navegación por teclado" },
{ "LOS → %s", "Línea de vista → %s" },
{ "Landscape (desktop)", "Horizontal (escritorio)" },
{ "Landscape only", "Solo en horizontal" },
{ "Landscape set - applies when remote mode starts.", "Horizontal fijado - se aplica al iniciar el modo remoto." },
{ "Language", "Idioma" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Catálogo de idiomas no disponible - revisa el Wi-Fi y reabre esta pestaña." },
@@ -4182,6 +4206,7 @@ static const I18nPair kBuiltin_es[] = {
{ "Leave empty if not required", "Dejar vacío si no es necesario" },
{ "Leaving remote mode - rebooting...", "Saliendo del modo remoto - reiniciando..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Configuremos tu dispositivo.\n\nElegirás un nombre y tu región LoRa. Tarda alrededor de un minuto." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Las letras mueven el resaltado cuando no hay un campo de texto activo. Intro selecciona; mantén Retroceso para volver." },
{ "Letters only", "Solo letras" },
{ "LoRa antenna (P4)", "Antena LoRa (P4)" },
{ "Loading catalog…", "Cargando catálogo…" },
@@ -4847,6 +4872,8 @@ static const I18nPair kBuiltin_it[] = {
{ "Community preset (meshcomod web)", "Preset community (meshcomod web)" },
{ "Compact messages", "Messaggi compatti" },
{ "Compact messages (IRC style)", "Messaggi compatti (stile IRC)" },
{ "Condense Nav (restart to apply)", "Navigazione compatta (riavvia per applicare)" },
{ "Condense Nav saved — restart to apply", "Navigazione compatta salvata — riavvia per applicare" },
{ "Connect", "Connetti" },
{ "Connect Wi-Fi to reload tiles", "Connetti Wi-Fi per ricaricare i riquadri" },
{ "Connect to Wi-Fi", "Connetti al Wi-Fi" },
@@ -5067,9 +5094,11 @@ static const I18nPair kBuiltin_it[] = {
{ "Keyboard light on", "Luce tastiera accesa" },
{ "Keyboard nav: WASDZ on", "Nav. tastiera: WASDZ on" },
{ "Keyboard nav: off", "Nav. tastiera: off" },
{ "Keyboard nav: on", "Nav. tastiera: on" },
{ "Keyboard navigation", "Navigazione da tastiera" },
{ "LOS → %s", "Vista diretta → %s" },
{ "Landscape (desktop)", "Orizzontale (desktop)" },
{ "Landscape only", "Solo in orizzontale" },
{ "Landscape set - applies when remote mode starts.", "Orizzontale impostato - vale all'avvio della modalità remota." },
{ "Language", "Lingua" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Catalogo lingue non disponibile - controlla il Wi-Fi e riapri questa scheda." },
@@ -5079,6 +5108,7 @@ static const I18nPair kBuiltin_it[] = {
{ "Leave empty if not required", "Lascia vuoto se non richiesto" },
{ "Leaving remote mode - rebooting...", "Uscita dalla modalità remota - riavvio..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Configuriamo il tuo dispositivo.\n\nSceglierai un nome e la tua regione LoRa. Richiede circa un minuto." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Le lettere spostano l'evidenziazione quando nessun campo di testo è attivo. Invio seleziona; tieni premuto Backspace per tornare indietro." },
{ "Letters only", "Solo lettere" },
{ "LoRa antenna (P4)", "Antenna LoRa (P4)" },
{ "Loading catalog…", "Caricamento catalogo…" },
@@ -5744,6 +5774,8 @@ static const I18nPair kBuiltin_ru[] = {
{ "Community preset (meshcomod web)", "Пресет сообщества (meshcomod web)" },
{ "Compact messages", "Компактные сообщения" },
{ "Compact messages (IRC style)", "Компактные сообщения (стиль IRC)" },
{ "Condense Nav (restart to apply)", "Компактная навигация (перезапуск для применения)" },
{ "Condense Nav saved — restart to apply", "Компактная навигация сохранена — перезапуск для применения" },
{ "Connect", "Подключить" },
{ "Connect Wi-Fi to reload tiles", "Подключите Wi-Fi для перезагрузки тайлов" },
{ "Connect to Wi-Fi", "Подключиться к Wi-Fi" },
@@ -5968,9 +6000,11 @@ static const I18nPair kBuiltin_ru[] = {
{ "Keyboard light on", "Подсветка клавиатуры вкл." },
{ "Keyboard nav: WASDZ on", "Навигация клавиатурой: WASDZ вкл." },
{ "Keyboard nav: off", "Навигация клавиатурой: выкл." },
{ "Keyboard nav: on", "Навигация клавиатурой: вкл." },
{ "Keyboard navigation", "Навигация клавиатурой" },
{ "LOS → %s", "Прямая видимость → %s" },
{ "Landscape (desktop)", "Альбомная (десктоп)" },
{ "Landscape only", "Только в альбомной ориентации" },
{ "Landscape set - applies when remote mode starts.", "Альбомная выбрана - применится при запуске удалённого режима." },
{ "Language", "Язык" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Каталог языков недоступен - проверьте Wi-Fi и откройте вкладку снова." },
@@ -5980,6 +6014,7 @@ static const I18nPair kBuiltin_ru[] = {
{ "Leave empty if not required", "Оставьте пустым, если не нужно" },
{ "Leaving remote mode - rebooting...", "Выход из удалённого режима - перезагрузка..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Настроим ваше устройство.\n\nВы выберете имя и регион LoRa. Займёт около минуты." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Буквы перемещают выделение, когда не активно текстовое поле. Enter выбирает; удерживайте Backspace, чтобы вернуться." },
{ "Letters only", "Только буквы" },
{ "LoRa antenna (P4)", "Антенна LoRa (P4)" },
{ "Loading catalog…", "Загрузка каталога…" },
@@ -6652,6 +6687,8 @@ static const I18nPair kBuiltin_uk[] = {
{ "Community preset (meshcomod web)", "Пресет спільноти (meshcomod web)" },
{ "Compact messages", "Компактні повідомлення" },
{ "Compact messages (IRC style)", "Компактні повідомлення (стиль IRC)" },
{ "Condense Nav (restart to apply)", "Компактна навігація (перезапуск для застосування)" },
{ "Condense Nav saved — restart to apply", "Компактну навігацію збережено — перезапуск для застосування" },
{ "Connect", "Підключити" },
{ "Connect Wi-Fi to reload tiles", "Підключіть Wi-Fi, щоб перезавантажити плитки" },
{ "Connect to Wi-Fi", "Підключитися до Wi-Fi" },
@@ -6876,9 +6913,11 @@ static const I18nPair kBuiltin_uk[] = {
{ "Keyboard light on", "Підсвітка клавіатури увімк." },
{ "Keyboard nav: WASDZ on", "Навігація клавіатурою: WASDZ увімк." },
{ "Keyboard nav: off", "Навігація клавіатурою: вимк." },
{ "Keyboard nav: on", "Навігація клавіатурою: увімк." },
{ "Keyboard navigation", "Навігація клавіатурою" },
{ "LOS → %s", "Пряма видимість → %s" },
{ "Landscape (desktop)", "Альбомна (десктоп)" },
{ "Landscape only", "Лише в альбомній орієнтації" },
{ "Landscape set - applies when remote mode starts.", "Альбомну вибрано - застосується під час запуску віддаленого режиму." },
{ "Language", "Мова" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Каталог мов недоступний - перевірте Wi-Fi і відкрийте вкладку знову." },
@@ -6888,6 +6927,7 @@ static const I18nPair kBuiltin_uk[] = {
{ "Leave empty if not required", "Залиште порожнім, якщо не потрібно" },
{ "Leaving remote mode - rebooting...", "Вихід із віддаленого режиму - перезавантаження..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Налаштуймо ваш пристрій.\n\nВи виберете ім'я та регіон LoRa. Займе близько хвилини." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Літери переміщують виділення, коли не активне текстове поле. Enter вибирає; утримуйте Backspace, щоб повернутися." },
{ "Letters only", "Лише літери" },
{ "LoRa antenna (P4)", "Антена LoRa (P4)" },
{ "Loading catalog…", "Завантаження каталогу…" },
@@ -7558,6 +7598,8 @@ static const I18nPair kBuiltin_bg[] = {
{ "Community preset (meshcomod web)", "Пресет на общността (meshcomod web)" },
{ "Compact messages", "Компактни съобщения" },
{ "Compact messages (IRC style)", "Компактни съобщения (IRC стил)" },
{ "Condense Nav (restart to apply)", "Компактна навигация (рестарт за прилагане)" },
{ "Condense Nav saved — restart to apply", "Компактната навигация е запазена — рестарт за прилагане" },
{ "Connect", "Свързване" },
{ "Connect Wi-Fi to reload tiles", "Свържете Wi-Fi за презареждане на плочките" },
{ "Connect to Wi-Fi", "Свържи се с Wi-Fi" },
@@ -7782,9 +7824,11 @@ static const I18nPair kBuiltin_bg[] = {
{ "Keyboard light on", "Подсветката на клавиатурата е вкл." },
{ "Keyboard nav: WASDZ on", "Клавиатурна навиг.: WASDZ вкл." },
{ "Keyboard nav: off", "Клавиатурна навиг.: изкл." },
{ "Keyboard nav: on", "Клавиатурна навиг.: вкл." },
{ "Keyboard navigation", "Навигация с клавиатура" },
{ "LOS → %s", "Пряка видимост → %s" },
{ "Landscape (desktop)", "Пейзаж (десктоп)" },
{ "Landscape only", "Само в хоризонтална ориентация" },
{ "Landscape set - applies when remote mode starts.", "Пейзажът е зададен - прилага се при стартиране на отдалечения режим." },
{ "Language", "Език" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Каталогът с езици е недостъпен - провери Wi-Fi и отвори раздела отново." },
@@ -7794,6 +7838,7 @@ static const I18nPair kBuiltin_bg[] = {
{ "Leave empty if not required", "Оставете празно, ако не е нужно" },
{ "Leaving remote mode - rebooting...", "Излизане от отдалечен режим - рестартиране..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Нека настроим устройството ви.\n\nЩе изберете име и вашия LoRa регион. Отнема около минута." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Буквите местят маркера, когато няма активно текстово поле. Enter избира; задръжте Backspace за назад." },
{ "Letters only", "Само букви" },
{ "LoRa antenna (P4)", "LoRa антена (P4)" },
{ "Loading catalog…", "Зареждане на каталога…" },
@@ -8464,6 +8509,8 @@ static const I18nPair kBuiltin_sr[] = {
{ "Community preset (meshcomod web)", "Унапред (meshcomod web)" },
{ "Compact messages", "Компактне поруке" },
{ "Compact messages (IRC style)", "Компактне поруке (IRC стил)" },
{ "Condense Nav (restart to apply)", "Компактна навигација (рестарт за примену)" },
{ "Condense Nav saved — restart to apply", "Компактна навигација сачувана — рестарт за примену" },
{ "Connect", "Повежи" },
{ "Connect Wi-Fi to reload tiles", "Повежите Wi-Fi да поново учитате плочице" },
{ "Connect to Wi-Fi", "Повежи се на Wi-Fi" },
@@ -8688,9 +8735,11 @@ static const I18nPair kBuiltin_sr[] = {
{ "Keyboard light on", "Осветљење тастатуре укљ." },
{ "Keyboard nav: WASDZ on", "Навиг. тастатуром: WASDZ укљ." },
{ "Keyboard nav: off", "Навиг. тастатуром: искљ." },
{ "Keyboard nav: on", "Навиг. тастатуром: укљ." },
{ "Keyboard navigation", "Навигација тастатуром" },
{ "LOS → %s", "Оптичка видљивост → %s" },
{ "Landscape (desktop)", "Положено (десктоп)" },
{ "Landscape only", "Само у хоризонталном положају" },
{ "Landscape set - applies when remote mode starts.", "Положено подешено - важи кад крене даљински режим." },
{ "Language", "Језик" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Каталог језика није доступан - провери Wi-Fi па поново отвори картицу." },
@@ -8700,6 +8749,7 @@ static const I18nPair kBuiltin_sr[] = {
{ "Leave empty if not required", "Оставите празно ако није потребно" },
{ "Leaving remote mode - rebooting...", "Излазак из даљинског режима - рестартовање..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Хајде да подесимо ваш уређај.\n\nИзабраћете име и ваш LoRa регион. Траје око минут." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Слова померају истицање када ниједно текстуално поље није активно. Enter бира; држите Backspace за назад." },
{ "Letters only", "Само слова" },
{ "LoRa antenna (P4)", "LoRa антена (P4)" },
{ "Loading catalog…", "Учитавање каталога…" },
@@ -9370,6 +9420,8 @@ static const I18nPair kBuiltin_el[] = {
{ "Community preset (meshcomod web)", "Προεπιλογή κοινότητας (meshcomod web)" },
{ "Compact messages", "Συμπαγή μηνύματα" },
{ "Compact messages (IRC style)", "Συμπαγή μηνύματα (στυλ IRC)" },
{ "Condense Nav (restart to apply)", "Συμπαγής πλοήγηση (επανεκκίνηση για εφαρμογή)" },
{ "Condense Nav saved — restart to apply", "Η συμπαγής πλοήγηση αποθηκεύτηκε — επανεκκίνηση για εφαρμογή" },
{ "Connect", "Σύνδεση" },
{ "Connect Wi-Fi to reload tiles", "Συνδέστε Wi-Fi για επαναφόρτωση πλακιδίων" },
{ "Connect to Wi-Fi", "Σύνδεση σε Wi-Fi" },
@@ -9592,9 +9644,11 @@ static const I18nPair kBuiltin_el[] = {
{ "Keyboard light on", "Φωτισμός πληκτρολογίου ενεργός" },
{ "Keyboard nav: WASDZ on", "Πλοήγηση πληκτρ.: WASDZ ενεργό" },
{ "Keyboard nav: off", "Πλοήγηση πληκτρ.: κλειστό" },
{ "Keyboard nav: on", "Πλοήγηση πληκτρ.: ανοιχτό" },
{ "Keyboard navigation", "Πλοήγηση με πληκτρολόγιο" },
{ "LOS → %s", "Οπτική επαφή → %s" },
{ "Landscape (desktop)", "Οριζόντια (desktop)" },
{ "Landscape only", "Μόνο σε οριζόντιο προσανατολισμό" },
{ "Landscape set - applies when remote mode starts.", "Ορίστηκε οριζόντια - ισχύει όταν ξεκινήσει η απομακρυσμένη λειτουργία." },
{ "Language", "Γλώσσα" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Ο κατάλογος γλωσσών δεν είναι διαθέσιμος - ελέγξτε το Wi-Fi και ξανανοίξτε την καρτέλα." },
@@ -9604,6 +9658,7 @@ static const I18nPair kBuiltin_el[] = {
{ "Leave empty if not required", "Αφήστε κενό αν δεν απαιτείται" },
{ "Leaving remote mode - rebooting...", "Έξοδος από απομακρυσμένη λειτουργία - επανεκκίνηση..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Ας ρυθμίσουμε τη συσκευή σας.\n\nΘα επιλέξετε όνομα και την περιοχή LoRa. Διαρκεί περίπου ένα λεπτό." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Τα γράμματα μετακινούν την επισήμανση όταν δεν είναι ενεργό πεδίο κειμένου. Το Enter επιλέγει· κρατήστε το Backspace για επιστροφή." },
{ "Letters only", "Μόνο γράμματα" },
{ "LoRa antenna (P4)", "Κεραία LoRa (P4)" },
{ "Loading catalog…", "Φόρτωση καταλόγου…" },
@@ -10267,6 +10322,8 @@ static const I18nPair kBuiltin_pt_br[] = {
{ "Community preset (meshcomod web)", "Predefinição da comunidade (meshcomod web)" },
{ "Compact messages", "Mensagens compactas" },
{ "Compact messages (IRC style)", "Mensagens compactas (estilo IRC)" },
{ "Condense Nav (restart to apply)", "Navegação compacta (reinicie para aplicar)" },
{ "Condense Nav saved — restart to apply", "Navegação compacta salva — reinicie para aplicar" },
{ "Connect", "Conectar" },
{ "Connect Wi-Fi to reload tiles", "Conecte o Wi-Fi para recarregar os tiles" },
{ "Connect to Wi-Fi", "Conectar ao Wi-Fi" },
@@ -10488,9 +10545,11 @@ static const I18nPair kBuiltin_pt_br[] = {
{ "Keyboard light on", "Luz do teclado ligada" },
{ "Keyboard nav: WASDZ on", "Nav. teclado: WASDZ ligado" },
{ "Keyboard nav: off", "Nav. teclado: desligado" },
{ "Keyboard nav: on", "Nav. teclado: ligado" },
{ "Keyboard navigation", "Navegação por teclado" },
{ "LOS → %s", "Linha de visada → %s" },
{ "Landscape (desktop)", "Paisagem (desktop)" },
{ "Landscape only", "Somente em paisagem" },
{ "Landscape set - applies when remote mode starts.", "Paisagem definida - vale quando o modo remoto iniciar." },
{ "Language", "Idioma" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Catálogo de idiomas indisponível - verifique o Wi-Fi e reabra esta aba." },
@@ -10499,6 +10558,7 @@ static const I18nPair kBuiltin_pt_br[] = {
{ "Leave empty if not required", "Deixe vazio se não for necessário" },
{ "Leaving remote mode - rebooting...", "Saindo do modo remoto - reiniciando..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Vamos configurar seu dispositivo.\n\nVocê escolherá um nome e sua região LoRa. Leva cerca de um minuto." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "As letras movem o destaque quando nenhum campo de texto está ativo. Enter seleciona; segure Backspace para voltar." },
{ "Letters only", "Somente letras" },
{ "LoRa antenna (P4)", "Antena LoRa (P4)" },
{ "Loading catalog…", "Carregando catálogo…" },
@@ -11158,6 +11218,8 @@ static const I18nPair kBuiltin_ro[] = {
{ "Community preset (meshcomod web)", "Presetare comunitate (meshcomod web)" },
{ "Compact messages", "Mesaje compacte" },
{ "Compact messages (IRC style)", "Mesaje compacte (stil IRC)" },
{ "Condense Nav (restart to apply)", "Navigare compactă (repornire pentru aplicare)" },
{ "Condense Nav saved — restart to apply", "Navigare compactă salvată — reporneşte pentru aplicare" },
{ "Connect", "Conectează" },
{ "Connect Wi-Fi to reload tiles", "Conectează Wi-Fi pentru a reîncărca dalele" },
{ "Connect to Wi-Fi", "Conectare la Wi-Fi" },
@@ -11375,9 +11437,11 @@ static const I18nPair kBuiltin_ro[] = {
{ "Keyboard light on", "Lumina tastaturii pornită" },
{ "Keyboard nav: WASDZ on", "Navigare tastatură: WASDZ pornit" },
{ "Keyboard nav: off", "Navigare tastatură: oprită" },
{ "Keyboard nav: on", "Navigare tastatură: pornită" },
{ "Keyboard navigation", "Navigare cu tastatura" },
{ "LOS → %s", "Linie vizuală → %s" },
{ "Landscape (desktop)", "Peisaj (desktop)" },
{ "Landscape only", "Doar în modul peisaj" },
{ "Landscape set - applies when remote mode starts.", "Peisaj setat - se aplică la pornirea modului la distanţă." },
{ "Language", "Limbă" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Catalog de limbi indisponibil - verifică Wi-Fi, apoi redeschide fila." },
@@ -11387,6 +11451,7 @@ static const I18nPair kBuiltin_ro[] = {
{ "Leave empty if not required", "Lasă gol dacă nu e necesar" },
{ "Leaving remote mode - rebooting...", "Se iese din modul la distanţă - repornire..." },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Să-ţi configurăm dispozitivul.\n\nÎţi alegi un nume şi regiunea LoRa. Durează cam un minut." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Literele mută selecția când niciun câmp de text nu este activ. Enter selectează; ține apăsat Backspace pentru înapoi." },
{ "Letters only", "Doar litere" },
{ "LoRa antenna (P4)", "Antenă LoRa (P4)" },
{ "Loading catalog…", "Se încarcă catalogul…" },
@@ -12098,6 +12163,8 @@ static const I18nPair kBuiltin_cs[] = {
{ "Compact", "Kompaktní" },
{ "Compact messages", "Kompaktní zprávy" },
{ "Compact messages (IRC style)", "Kompaktní zprávy (styl IRC)" },
{ "Condense Nav (restart to apply)", "Kompaktní navigace (pro použití restartujte)" },
{ "Condense Nav saved — restart to apply", "Kompaktní navigace uložena — pro použití restartujte" },
{ "Connect", "Připojit" },
{ "Connect Wi-Fi to reload tiles", "Připojte Wi-Fi pro znovunačtení dlaždic" },
{ "Connect to Wi-Fi", "Připojit k Wi-Fi" },
@@ -12386,11 +12453,13 @@ static const I18nPair kBuiltin_cs[] = {
{ "Keyboard light on", "Světlo klávesnice zapnuto" },
{ "Keyboard nav: WASDZ on", "Navigace klávesnicí: WASDZ zapnuta" },
{ "Keyboard nav: off", "Navigace klávesnicí: vypnuta" },
{ "Keyboard nav: on", "Navigace klávesnicí: zapnuta" },
{ "Keyboard navigation", "Navigace klávesnicí" },
{ "Keys", "Klávesy" },
{ "Known regions", "Známé regiony" },
{ "LOS → %s", "Přímá viditelnost → %s" },
{ "Landscape (desktop)", "Na šířku (desktop)" },
{ "Landscape only", "Pouze na šířku" },
{ "Landscape set - applies when remote mode starts.", "Orientace na šířku nastavena - použije se při spuštění vzdáleného režimu." },
{ "Language", "Jazyk" },
{ "Language catalog unavailable - check Wi-Fi, then reopen this tab.", "Katalog jazyků není dostupný - zkontrolujte Wi-Fi a potom kartu znovu otevřete." },
@@ -12404,6 +12473,7 @@ static const I18nPair kBuiltin_cs[] = {
{ "Leaving remote mode - restarting…", "Ukončování vzdáleného režimu - restartování…" },
{ "Left", "Vlevo" },
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Nastavme vaše zařízení.\n\nVyberete jméno a region LoRa. Zabere to asi minutu." },
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Písmena posouvají zvýraznění, když není aktivní textové pole. Enter vybírá; podržením Backspace se vrátíte zpět." },
{ "Letters only", "Pouze písmena" },
{ "Link lost", "Spojení ztraceno" },
{ "LoRa antenna (P4)", "LoRa anténa (P4)" },
+52 -61
View File
@@ -1,10 +1,15 @@
/*******************************************************************************
* Size: 16 px
* Bpp: 4
* Opts: --no-compress --bpp 4 --size 16 --font /tmp/fa/fa-solid-900.ttf -r 0xF007 -r 0xF519 -r 0xF0C0 --format lvgl -o src/ui-touch/person_font.c
* Generated by scripts/build/gen-touch-fonts.sh with lv_font_conv 1.5.3
* Sources: Lucide 1.52.0 (person icons)
******************************************************************************/
#if 1
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef PERSON_FONT
#define PERSON_FONT 1
@@ -19,64 +24,51 @@
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+F007 "" */
0x0, 0x0, 0x2a, 0xee, 0xa2, 0x0, 0x0, 0x0,
0x2, 0xef, 0xff, 0xfe, 0x20, 0x0, 0x0, 0xa,
0xff, 0xff, 0xff, 0xa0, 0x0, 0x0, 0xe, 0xff,
0xff, 0xff, 0xe0, 0x0, 0x0, 0xe, 0xff, 0xff,
0xff, 0xe0, 0x0, 0x0, 0xa, 0xff, 0xff, 0xff,
0xa0, 0x0, 0x0, 0x2, 0xef, 0xff, 0xfe, 0x20,
0x0, 0x0, 0x0, 0x2a, 0xee, 0xa2, 0x0, 0x0,
0x0, 0x0, 0x26, 0x61, 0x0, 0x0, 0x0, 0x5,
0xfe, 0xef, 0x40, 0x0, 0x0, 0x1f, 0x90, 0x9,
0xf1, 0x0, 0x0, 0x4f, 0x0, 0x0, 0xf4, 0x0,
0x0, 0x3f, 0x30, 0x3, 0xf3, 0x0, 0x0, 0xb,
0xe6, 0x6e, 0xb0, 0x0, 0x0, 0x0, 0x9e, 0xf9,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0x57, 0x77, 0x75, 0x10, 0x0, 0x0, 0x8f,
0xff, 0xff, 0xff, 0xf8, 0x0, 0xa, 0xff, 0xff,
0xff, 0xff, 0xff, 0xa0, 0x5f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xf5, 0xbf, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfe, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
0x0, 0x15, 0x66, 0x66, 0x61, 0x0, 0x4, 0xff,
0xee, 0xee, 0xff, 0x40, 0xe, 0xa0, 0x0, 0x0,
0xa, 0xe0, 0x4f, 0x10, 0x0, 0x0, 0x1, 0xf4,
0x5f, 0x0, 0x0, 0x0, 0x0, 0xf5, 0x3b, 0x0,
0x0, 0x0, 0x0, 0xb3,
/* U+F0C0 "" */
0x0, 0x1c, 0xfc, 0x10, 0x0, 0x0, 0x0, 0x7e,
0xe7, 0x0, 0x0, 0xbf, 0xff, 0xb0, 0x0, 0x0,
0x3, 0xff, 0xff, 0x30, 0x0, 0xff, 0xff, 0xf0,
0x0, 0x0, 0x7, 0xff, 0xff, 0x70, 0x0, 0xbf,
0xff, 0xb0, 0x0, 0x0, 0x3, 0xff, 0xff, 0x30,
0x0, 0x1c, 0xfc, 0x10, 0x8e, 0xe8, 0x0, 0x7e,
0xe7, 0x0, 0x0, 0x0, 0x0, 0x8, 0xff, 0xff,
0x80, 0x0, 0x0, 0x0, 0x6, 0xdf, 0xfd, 0xe,
0xff, 0xff, 0xe0, 0xdf, 0xfd, 0x60, 0x6f, 0xff,
0xff, 0xe, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xf6,
0xdf, 0xff, 0xff, 0x58, 0xff, 0xff, 0x85, 0xff,
0xff, 0xfd, 0xdf, 0xff, 0xff, 0xd1, 0x8e, 0xe8,
0x1d, 0xff, 0xff, 0xfd, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x26, 0x61, 0x0, 0x53, 0x0, 0x0,
0x0, 0x5, 0xfe, 0xef, 0x40, 0xcf, 0x60, 0x0,
0x0, 0x1f, 0x90, 0x9, 0xf1, 0x8, 0xf1, 0x0,
0x0, 0x4f, 0x0, 0x0, 0xf4, 0x0, 0xf4, 0x0,
0x0, 0x3f, 0x30, 0x3, 0xf3, 0x3, 0xf2, 0x0,
0x0, 0xb, 0xe6, 0x6e, 0xb0, 0x5e, 0xb0, 0x0,
0x0, 0x0, 0x9e, 0xf9, 0x0, 0xca, 0x10, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x1, 0xae, 0xff, 0xff, 0xea, 0x10, 0x0, 0x0,
0x0, 0x0, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xe1,
0x0, 0x0, 0x0, 0x0, 0xaf, 0xff, 0xff, 0xff,
0xff, 0xfa, 0x0, 0x0, 0x0, 0x0, 0xef, 0xff,
0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0,
0xcf, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0, 0x0,
0x0, 0x15, 0x66, 0x66, 0x61, 0x0, 0x31, 0x0,
0x4, 0xff, 0xee, 0xee, 0xff, 0x40, 0xdf, 0x40,
0xe, 0xa0, 0x0, 0x0, 0xa, 0xe0, 0x9, 0xe0,
0x4f, 0x10, 0x0, 0x0, 0x1, 0xf4, 0x1, 0xf4,
0x5f, 0x0, 0x0, 0x0, 0x0, 0xf5, 0x0, 0xf5,
0x3b, 0x0, 0x0, 0x0, 0x0, 0xb3, 0x0, 0xb3,
/* U+F519 "" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x3e, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0,
0x5, 0xf3, 0x9f, 0x75, 0xe3, 0x0, 0x0, 0x0,
0x3e, 0x57, 0xf9, 0xdf, 0x2c, 0xf4, 0x4, 0xee,
0x40, 0x4f, 0xc2, 0xfd, 0xff, 0xf, 0xf0, 0xe,
0xff, 0xe0, 0xf, 0xf0, 0xff, 0xff, 0xf, 0xf0,
0xd, 0xff, 0xd0, 0xf, 0xf0, 0xff, 0xdf, 0x2c,
0xf4, 0x4, 0xff, 0x40, 0x4f, 0xc2, 0xfd, 0x9f,
0x75, 0xe3, 0x0, 0xff, 0x0, 0x3e, 0x57, 0xf9,
0x3f, 0x50, 0x0, 0x0, 0xff, 0x0, 0x0, 0x5,
0xe3, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0,
0x0
0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x72, 0x0,
0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0xbd, 0x10,
0x7, 0xf3, 0x48, 0x0, 0x0, 0x73, 0x1e, 0x80,
0xe, 0x81, 0xe9, 0x1, 0x10, 0x9e, 0x7, 0xf0,
0x2f, 0x36, 0xf0, 0x7f, 0xf6, 0xf, 0x62, 0xf3,
0x5f, 0x9, 0xc0, 0xf8, 0x8e, 0xc, 0x90, 0xf4,
0x5f, 0x8, 0xd0, 0xdc, 0xcd, 0xd, 0x80, 0xf4,
0x2f, 0x33, 0xf2, 0x2f, 0xf2, 0x2f, 0x44, 0xf1,
0xe, 0x80, 0x71, 0x3f, 0xf3, 0x17, 0xa, 0xc0,
0x7, 0xf2, 0x0, 0x9d, 0xd9, 0x0, 0x5f, 0x30,
0x0, 0xcc, 0x0, 0xe7, 0x7e, 0x0, 0xd8, 0x0,
0x0, 0x14, 0x4, 0xfe, 0xef, 0x40, 0x0, 0x0,
0x0, 0x0, 0xa, 0xd7, 0x7d, 0xa0, 0x0, 0x0,
0x0, 0x0, 0xf, 0x60, 0x6, 0xf0, 0x0, 0x0,
0x0, 0x0, 0x4f, 0x0, 0x0, 0xf3, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};
@@ -86,9 +78,9 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
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 = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 112, .adv_w = 320, .box_w = 20, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 272, .adv_w = 288, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -2}
{.bitmap_index = 0, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = -1},
{.bitmap_index = 84, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 196, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}
};
/*---------------------
@@ -152,14 +144,14 @@ lv_font_t person_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 = 17, /*The maximum line height required by the font*/
.base_line = 2, /*Baseline measured from the bottom of the line*/
.line_height = 12, /*The maximum line height required by the font*/
.base_line = 0, /*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,
.underline_position = 0,
.underline_thickness = 0,
#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
@@ -171,4 +163,3 @@ lv_font_t person_font = {
#endif /*#if PERSON_FONT*/
+45 -54
View File
@@ -1,10 +1,15 @@
/*******************************************************************************
* Size: 14 px
* Bpp: 4
* Opts: --no-compress --bpp 4 --size 14 --font /tmp/fa/fa-solid-900.ttf -r 0xF007 -r 0xF519 -r 0xF0C0 --format lvgl -o src/ui-touch/person_font14.c
* Generated by scripts/build/gen-touch-fonts.sh with lv_font_conv 1.5.3
* Sources: Lucide 1.52.0 (person icons)
******************************************************************************/
#if 1
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef PERSON_FONT14
#define PERSON_FONT14 1
@@ -19,55 +24,42 @@
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+F007 "" */
0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0,
0x1b, 0xff, 0xd3, 0x0, 0x0, 0x0, 0xc, 0xff,
0xff, 0xe1, 0x0, 0x0, 0x3, 0xff, 0xff, 0xff,
0x70, 0x0, 0x0, 0x5f, 0xff, 0xff, 0xf9, 0x0,
0x0, 0x2, 0xff, 0xff, 0xff, 0x60, 0x0, 0x0,
0x9, 0xff, 0xff, 0xc0, 0x0, 0x0, 0x0, 0x6,
0xcc, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x5, 0xcf, 0xff, 0xfd, 0x70,
0x0, 0x9, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x5,
0xff, 0xff, 0xff, 0xff, 0xff, 0x90, 0xcf, 0xff,
0xff, 0xff, 0xff, 0xff, 0xf, 0xff, 0xff, 0xff,
0xff, 0xff, 0xf3, 0x8b, 0xbb, 0xbb, 0xbb, 0xbb,
0xba, 0x0,
0x0, 0x7, 0xdc, 0x40, 0x0, 0x0, 0x8e, 0x67,
0xf4, 0x0, 0x0, 0xe4, 0x0, 0x8a, 0x0, 0x0,
0xf4, 0x0, 0x8b, 0x0, 0x0, 0x9d, 0x35, 0xe5,
0x0, 0x0, 0x9, 0xfe, 0x60, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x6, 0xdf, 0xff, 0xfc, 0x30,
0x5f, 0x63, 0x33, 0x38, 0xf2, 0xb7, 0x0, 0x0,
0x0, 0xb7, 0xc5, 0x0, 0x0, 0x0, 0xa8, 0x72,
0x0, 0x0, 0x0, 0x54,
/* U+F0C0 "" */
0x0, 0x2, 0x20, 0x0, 0x0, 0x0, 0x2, 0x20,
0x0, 0x0, 0xaf, 0xf8, 0x0, 0x0, 0x0, 0x9f,
0xf9, 0x0, 0x2, 0xff, 0xff, 0x0, 0x0, 0x1,
0xff, 0xff, 0x10, 0x2, 0xff, 0xff, 0x0, 0x0,
0x1, 0xff, 0xff, 0x10, 0x0, 0x8f, 0xf6, 0x6,
0xb9, 0x20, 0x7f, 0xf7, 0x0, 0x0, 0x0, 0x0,
0x6f, 0xff, 0xe0, 0x0, 0x0, 0x0, 0x9, 0xef,
0xf3, 0xdf, 0xff, 0xf5, 0xaf, 0xfc, 0x40, 0x9f,
0xff, 0xf5, 0xcf, 0xff, 0xf4, 0xdf, 0xff, 0xf1,
0xef, 0xff, 0xfa, 0x3f, 0xff, 0xb3, 0xff, 0xff,
0xf6, 0x68, 0x88, 0x87, 0x12, 0x75, 0x5, 0x88,
0x88, 0x72, 0x0, 0x0, 0x0, 0x46, 0x66, 0x62,
0x0, 0x0, 0x0, 0x0, 0x0, 0x4e, 0xff, 0xff,
0xff, 0xa0, 0x0, 0x0, 0x0, 0x1, 0xef, 0xff,
0xff, 0xff, 0xf8, 0x0, 0x0, 0x0, 0x5, 0xff,
0xff, 0xff, 0xff, 0xfd, 0x0, 0x0, 0x0, 0x5,
0xff, 0xff, 0xff, 0xff, 0xfc, 0x0, 0x0,
0x0, 0x4, 0xbd, 0x80, 0x1b, 0x30, 0x0, 0x0,
0x2f, 0x85, 0xda, 0x8, 0xf2, 0x0, 0x0, 0x8a,
0x0, 0x2f, 0x10, 0xa9, 0x0, 0x0, 0x9a, 0x0,
0x1f, 0x10, 0x99, 0x0, 0x0, 0x3f, 0x63, 0xbb,
0x6, 0xf3, 0x0, 0x0, 0x5, 0xdf, 0xa1, 0x1d,
0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0xbf, 0xff, 0xfe, 0x70, 0x5b, 0x20,
0xe, 0x93, 0x33, 0x35, 0xe7, 0x19, 0xe0, 0x5d,
0x0, 0x0, 0x0, 0x5d, 0x0, 0xd5, 0x6c, 0x0,
0x0, 0x0, 0x3e, 0x0, 0xc6, 0x36, 0x0, 0x0,
0x0, 0x18, 0x0, 0x63,
/* U+F519 "" */
0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20,
0x7f, 0x23, 0x10, 0x0, 0x0, 0x2, 0x16, 0xf3,
0xcf, 0x1f, 0xa0, 0x4, 0x30, 0xe, 0xc4, 0xf8,
0xed, 0x4f, 0x70, 0x9f, 0xf5, 0xb, 0xf1, 0xfa,
0xfc, 0x5f, 0x60, 0xcf, 0xf8, 0xa, 0xf1, 0xfb,
0xee, 0x3f, 0x90, 0x5f, 0xe2, 0xd, 0xf2, 0xf9,
0xaf, 0x2a, 0x70, 0xf, 0xc0, 0xa, 0x76, 0xf6,
0x2a, 0x0, 0x0, 0xf, 0xc0, 0x0, 0x3, 0xa0,
0x0, 0x0, 0x0, 0xf, 0xc0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xf, 0xc0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xf, 0xc0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xf, 0xc0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xf, 0xc0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xf, 0xb0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xa, 0x60, 0x0, 0x0, 0x0
0x0, 0x42, 0x0, 0x0, 0x0, 0x24, 0x0, 0x3,
0xf3, 0x0, 0x0, 0x0, 0x4f, 0x30, 0xc, 0x91,
0xb0, 0x0, 0xa, 0x9, 0xc0, 0x2f, 0x1a, 0xb0,
0x55, 0xb, 0x91, 0xf2, 0x5d, 0xf, 0x38, 0xee,
0x84, 0xe0, 0xd5, 0x6c, 0xf, 0x2b, 0x99, 0xb2,
0xf0, 0xc6, 0x4e, 0xc, 0x63, 0xff, 0x36, 0xc0,
0xe3, 0xf, 0x44, 0x51, 0xff, 0x6, 0x44, 0xe0,
0x8, 0xd0, 0x6, 0xdd, 0x60, 0x1d, 0x60, 0x0,
0xc5, 0xc, 0x88, 0xc0, 0x5a, 0x0, 0x0, 0x0,
0x2f, 0xff, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x7c,
0x11, 0xc7, 0x0, 0x0, 0x0, 0x0, 0xd6, 0x0,
0x6d, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x4,
0x0, 0x0
};
@@ -77,9 +69,9 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
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 = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 98, .adv_w = 280, .box_w = 18, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 233, .adv_w = 252, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}
{.bitmap_index = 0, .adv_w = 224, .box_w = 10, .box_h = 12, .ofs_x = 2, .ofs_y = -1},
{.bitmap_index = 60, .adv_w = 224, .box_w = 14, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 144, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}
};
/*---------------------
@@ -143,14 +135,14 @@ lv_font_t person_font14 = {
#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 = 15, /*The maximum line height required by the font*/
.base_line = 2, /*Baseline measured from the bottom of the line*/
.line_height = 10, /*The maximum line height required by the font*/
.base_line = 0, /*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,
.underline_position = 0,
.underline_thickness = 0,
#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
@@ -162,4 +154,3 @@ lv_font_t person_font14 = {
#endif /*#if PERSON_FONT14*/
+40 -40
View File
@@ -1,10 +1,15 @@
/*******************************************************************************
* 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
* Generated by scripts/build/gen-touch-fonts.sh with lv_font_conv 1.5.3
* Sources: Lucide 1.52.0 (sleep icons)
******************************************************************************/
#if 1
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef SLEEPICONS_FONT
#define SLEEPICONS_FONT 1
@@ -19,41 +24,37 @@
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+F185 "" */
0x0, 0x0, 0x0, 0x3, 0x40, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x8, 0xb0, 0x0, 0x0, 0x0,
0x0, 0x17, 0x0, 0x8, 0xb0, 0x0, 0x40, 0x0,
0x0, 0x2f, 0xa0, 0x0, 0x0, 0x6, 0xf3, 0x0,
0x0, 0x3, 0x80, 0x16, 0x72, 0xa, 0x60, 0x0,
0x0, 0x0, 0x4, 0xff, 0xff, 0x60, 0x0, 0x0,
0x0, 0x0, 0xe, 0xb1, 0x2b, 0xf2, 0x0, 0x0,
0x1c, 0xd3, 0x3f, 0x10, 0x1, 0xf6, 0x1c, 0xd3,
0x7, 0x71, 0x2f, 0x10, 0x1, 0xf5, 0x7, 0x71,
0x0, 0x0, 0xb, 0xb1, 0x1b, 0xe0, 0x0, 0x0,
0x0, 0x0, 0x1, 0xbf, 0xfc, 0x20, 0x0, 0x0,
0x0, 0x6, 0xa0, 0x1, 0x10, 0xa, 0x60, 0x0,
0x0, 0x3f, 0x60, 0x3, 0x40, 0x7, 0xf3, 0x0,
0x0, 0x3, 0x0, 0x8, 0xb0, 0x0, 0x30, 0x0,
0x0, 0x0, 0x0, 0x8, 0xb0, 0x0, 0x0, 0x0,
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
0x0, 0x0, 0x17, 0x94, 0x0, 0x0, 0x0, 0x0,
0x8, 0xfe, 0xfb, 0x0, 0x0, 0x0, 0x0, 0xbe,
0x61, 0xf5, 0x0, 0x0, 0x0, 0x8, 0xf2, 0x3,
0xf2, 0x0, 0x0, 0x0, 0x1f, 0x60, 0x1, 0xf3,
0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0xdb, 0x0,
0x0, 0x0, 0x8c, 0x0, 0x0, 0x3f, 0xa2, 0x3,
0x94, 0x8c, 0x0, 0x0, 0x3, 0xdf, 0xff, 0xfb,
0x6f, 0x0, 0x0, 0x0, 0x3, 0x52, 0xc8, 0x1f,
0x60, 0x0, 0x0, 0x0, 0x3, 0xf3, 0x8, 0xe2,
0x0, 0x0, 0x0, 0x1d, 0xb0, 0x0, 0xce, 0x50,
0x0, 0x4, 0xdd, 0x10, 0x0, 0x9, 0xfe, 0xbb,
0xef, 0xb1, 0x0, 0x0, 0x0, 0x17, 0xaa, 0x73,
0x0, 0x0
};
@@ -63,8 +64,8 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
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}
{.bitmap_index = 0, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 128, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}
};
/*---------------------
@@ -126,14 +127,14 @@ 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*/
.line_height = 12, /*The maximum line height required by the font*/
.base_line = 0, /*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,
.underline_position = 0,
.underline_thickness = 0,
#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
@@ -145,4 +146,3 @@ lv_font_t sleepicons_font = {
#endif /*#if SLEEPICONS_FONT*/
+246
View File
@@ -0,0 +1,246 @@
/*******************************************************************************
* Size: 11 px
* Bpp: 4
* Generated by scripts/build/gen-touch-fonts.sh with lv_font_conv 1.5.3
* Sources: Montserrat Bold v7.222 (digits, + and !)
******************************************************************************/
#if 1
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef UI_BADGE_11
#define UI_BADGE_11 1
#endif
#if UI_BADGE_11
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+0020 " " */
/* U+0021 "!" */
0x6f, 0x95, 0xf8, 0x4f, 0x73, 0xf6, 0x2f, 0x50,
0x0, 0x4e, 0x64, 0xf6,
/* U+002B "+" */
0x0, 0x1, 0x0, 0x0, 0x6f, 0x0, 0x1, 0x7f,
0x11, 0x5f, 0xff, 0xfe, 0x14, 0x9f, 0x43, 0x0,
0x6f, 0x0,
/* U+0030 "0" */
0x2, 0xbf, 0xe6, 0x0, 0xe, 0xf9, 0xcf, 0x60,
0x5f, 0x70, 0x1f, 0xd0, 0x8f, 0x40, 0xd, 0xf0,
0x8f, 0x40, 0xc, 0xf0, 0x5f, 0x70, 0xf, 0xd0,
0xe, 0xf9, 0xcf, 0x60, 0x2, 0xbf, 0xe7, 0x0,
/* U+0031 "1" */
0xef, 0xf6, 0x6a, 0xf6, 0x6, 0xf6, 0x6, 0xf6,
0x6, 0xf6, 0x6, 0xf6, 0x6, 0xf6, 0x6, 0xf6,
/* U+0032 "2" */
0x2b, 0xff, 0xb1, 0xa, 0xf9, 0xaf, 0xb0, 0x1,
0x0, 0xee, 0x0, 0x0, 0x4f, 0xa0, 0x0, 0x4f,
0xd1, 0x0, 0x4f, 0xd1, 0x0, 0x4f, 0xf8, 0x77,
0x1a, 0xff, 0xff, 0xf3,
/* U+0033 "3" */
0xa, 0xff, 0xff, 0xc0, 0x4, 0x77, 0xcf, 0x70,
0x0, 0x4, 0xf9, 0x0, 0x0, 0xe, 0xfa, 0x20,
0x0, 0x7, 0x9f, 0xd0, 0x0, 0x0, 0xb, 0xf1,
0xa, 0xc8, 0x9f, 0xd0, 0x6, 0xcf, 0xfb, 0x20,
/* U+0034 "4" */
0x0, 0x6, 0xf7, 0x0, 0x0, 0x2f, 0xc0, 0x0,
0x0, 0xcf, 0x20, 0x0, 0x8, 0xf6, 0x4a, 0x20,
0x3f, 0xc2, 0x8f, 0x50, 0xaf, 0xff, 0xff, 0xf7,
0x35, 0x55, 0xaf, 0x72, 0x0, 0x0, 0x8f, 0x30,
/* U+0035 "5" */
0xf, 0xff, 0xfc, 0x0, 0xfb, 0x77, 0x50, 0x2f,
0x81, 0x0, 0x3, 0xff, 0xfe, 0x50, 0x15, 0x56,
0xef, 0x10, 0x0, 0x9, 0xf3, 0x8d, 0x99, 0xfe,
0x5, 0xce, 0xfb, 0x30,
/* U+0036 "6" */
0x0, 0x9e, 0xfd, 0x30, 0xcf, 0xa7, 0xa1, 0x4f,
0x80, 0x0, 0x8, 0xfb, 0xff, 0xb1, 0x8f, 0xd4,
0x8f, 0x96, 0xf7, 0x0, 0xfb, 0xe, 0xd6, 0x9f,
0x70, 0x2b, 0xee, 0x90,
/* U+0037 "7" */
0xbf, 0xff, 0xff, 0x7b, 0xe7, 0x7d, 0xf4, 0x8a,
0x1, 0xfe, 0x0, 0x0, 0x7f, 0x70, 0x0, 0xd,
0xf1, 0x0, 0x4, 0xfa, 0x0, 0x0, 0xbf, 0x30,
0x0, 0x2f, 0xc0, 0x0,
/* U+0038 "8" */
0x6, 0xdf, 0xd9, 0x3, 0xfc, 0x6a, 0xf7, 0x3f,
0xa1, 0x6f, 0x70, 0xcf, 0xff, 0xe1, 0x5f, 0xa3,
0x7f, 0x99, 0xf3, 0x0, 0xfd, 0x5f, 0xb5, 0x9f,
0x90, 0x7d, 0xfe, 0x90,
/* U+0039 "9" */
0x8, 0xee, 0xb2, 0x7, 0xf9, 0x6e, 0xe0, 0xbf,
0x0, 0x7f, 0x6a, 0xf5, 0x1c, 0xf8, 0x1d, 0xff,
0xdf, 0x80, 0x3, 0x28, 0xf4, 0x9, 0x79, 0xfc,
0x3, 0xdf, 0xe9, 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 = 50, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 0, .adv_w = 51, .box_w = 3, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 12, .adv_w = 105, .box_w = 6, .box_h = 6, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 30, .adv_w = 120, .box_w = 8, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 62, .adv_w = 69, .box_w = 4, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 78, .adv_w = 104, .box_w = 7, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 106, .adv_w = 104, .box_w = 8, .box_h = 8, .ofs_x = -1, .ofs_y = 0},
{.bitmap_index = 138, .adv_w = 121, .box_w = 8, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 170, .adv_w = 105, .box_w = 7, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 198, .adv_w = 112, .box_w = 7, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 226, .adv_w = 109, .box_w = 7, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 254, .adv_w = 116, .box_w = 7, .box_h = 8, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 282, .adv_w = 112, .box_w = 7, .box_h = 8, .ofs_x = 0, .ofs_y = 0}
};
/*---------------------
* CHARACTER MAPPING
*--------------------*/
static const uint16_t unicode_list_0[] = {
0x0, 0x1, 0xb
};
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
{
.range_start = 32, .range_length = 12, .glyph_id_start = 1,
.unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 3, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
},
{
.range_start = 48, .range_length = 10, .glyph_id_start = 4,
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
}
};
/*-----------------
* KERNING
*----------------*/
/*Map glyph_ids to kern left classes*/
static const uint8_t kern_left_class_mapping[] =
{
0, 0, 1, 2, 3, 0, 4, 5,
6, 7, 8, 9, 0, 3
};
/*Map glyph_ids to kern right classes*/
static const uint8_t kern_right_class_mapping[] =
{
0, 0, 0, 1, 2, 3, 4, 5,
6, 7, 2, 8, 9, 10
};
/*Kern values between classes*/
static const int8_t kern_class_values[] =
{
0, 0, 2, 0, 0, 0, 0, 1,
0, 0, 1, 1, -4, -3, -4, 1,
0, -2, 0, 0, 1, 0, -1, -1,
-2, 0, 0, -1, 0, 0, -1, 0,
0, 0, 0, -2, 0, 0, 0, 0,
0, 0, 0, -1, -1, 0, -2, -2,
0, 0, 2, 0, -4, 0, -1, 0,
0, -7, 2, -1, 0, 0, 0, -1,
-1, 0, -1, -2, 0, 0, 1, 0,
0, 0, 0, 0, 0, -1, 0, 0,
-9, -4, 2, 0, -1, -11, -3, 0,
-3, 0
};
/*Collect the kern class' data in one place*/
static const lv_font_fmt_txt_kern_classes_t kern_classes =
{
.class_pair_values = kern_class_values,
.left_class_mapping = kern_left_class_mapping,
.right_class_mapping = kern_right_class_mapping,
.left_class_cnt = 9,
.right_class_cnt = 10,
};
/*--------------------
* 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 = &kern_classes,
.kern_scale = 16,
.cmap_num = 2,
.bpp = 4,
.kern_classes = 1,
.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 ui_badge_11 = {
#else
lv_font_t ui_badge_11 = {
#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 = 8, /*The maximum line height required by the font*/
.base_line = 0, /*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 UI_BADGE_11*/
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+95
View File
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Generated by scripts/build/gen-touch-fonts.sh (lucide_font.py header).
// The app icons in ui_icons_16 / ui_icons_20, as UTF-8 string literals:
// Lucide 1.52.0, on Lucide's own codepoints.
#pragma once
#define UI_ICON_MESSAGE_SQUARE "\xEE\x84\x97" /* U+E117 message-square */
#define UI_ICON_USER "\xEE\x86\x9F" /* U+E19F user */
#define UI_ICON_USERS "\xEE\x86\xA4" /* U+E1A4 users */
#define UI_ICON_AT_SIGN "\xEE\x81\x8E" /* U+E04E at-sign */
#define UI_ICON_MAP_PIN "\xEE\x84\x91" /* U+E111 map-pin */
#define UI_ICON_MAP "\xEE\x84\x90" /* U+E110 map */
#define UI_ICON_RADIO "\xEE\x85\x82" /* U+E142 radio */
#define UI_ICON_SIGNAL "\xEE\x89\x9F" /* U+E25F signal */
#define UI_ICON_ACTIVITY "\xEE\x80\xB8" /* U+E038 activity */
#define UI_ICON_AUDIO_LINES "\xEE\x95\x9A" /* U+E55A audio-lines */
#define UI_ICON_SQUARE_TERMINAL "\xEE\x88\x8A" /* U+E20A square-terminal */
#define UI_ICON_MONITOR "\xEE\x84\x9D" /* U+E11D monitor */
#define UI_ICON_CAST "\xEE\x81\xA6" /* U+E066 cast */
#define UI_ICON_SLIDERS_HORIZONTAL "\xEE\x8A\x9A" /* U+E29A sliders-horizontal */
#define UI_ICON_SETTINGS "\xEE\x85\x94" /* U+E154 settings */
#define UI_ICON_HOUSE "\xEE\x83\xB5" /* U+E0F5 house */
#define UI_ICON_FOLDER "\xEE\x83\x97" /* U+E0D7 folder */
#define UI_ICON_LAYOUT_GRID "\xEE\x83\xBF" /* U+E0FF layout-grid */
#define UI_ICON_GAMEPAD_2 "\xEE\x83\x9F" /* U+E0DF gamepad-2 */
#define UI_ICON_GLOBE "\xEE\x83\xA8" /* U+E0E8 globe */
#define UI_ICON_RADAR "\xEE\x92\x97" /* U+E497 radar */
#define UI_ICON_SHIELD_CHECK "\xEE\x87\xBF" /* U+E1FF shield-check */
#define UI_ICON_USB "\xEE\x8D\x96" /* U+E356 usb */
#define UI_ICON_QR_CODE "\xEE\x87\x9F" /* U+E1DF qr-code */
#define UI_ICON_POWER "\xEE\x85\x80" /* U+E140 power */
#define UI_ICON_COMPASS "\xEE\x82\x9B" /* U+E09B compass */
#define UI_ICON_SATELLITE "\xEE\x91\x87" /* U+E447 satellite */
#define UI_ICON_WIFI "\xEE\x86\xAE" /* U+E1AE wifi */
#define UI_ICON_BLUETOOTH "\xEE\x81\x9C" /* U+E05C bluetooth */
#define UI_ICON_LOCK "\xEE\x84\x8B" /* U+E10B lock */
#define UI_ICON_BELL "\xEE\x81\x99" /* U+E059 bell */
#define UI_ICON_BELL_OFF "\xEE\x81\x9A" /* U+E05A bell-off */
#define UI_ICON_SUN "\xEE\x85\xB8" /* U+E178 sun */
#define UI_ICON_MOON "\xEE\x84\x9E" /* U+E11E moon */
#define UI_ICON_SEARCH "\xEE\x85\x91" /* U+E151 search */
#define UI_ICON_ARROW_UP "\xEE\x81\x8A" /* U+E04A arrow-up */
#define UI_ICON_CHECK "\xEE\x81\xAC" /* U+E06C check */
#define UI_ICON_CHECK_CHECK "\xEE\x8E\x8E" /* U+E38E check-check */
#define UI_ICON_CHEVRON_LEFT "\xEE\x81\xAE" /* U+E06E chevron-left */
#define UI_ICON_CHEVRON_RIGHT "\xEE\x81\xAF" /* U+E06F chevron-right */
#define UI_ICON_X "\xEE\x86\xB2" /* U+E1B2 x */
#define UI_ICON_PLUS "\xEE\x84\xBD" /* U+E13D plus */
#define UI_ICON_ELLIPSIS "\xEE\x82\xB6" /* U+E0B6 ellipsis */
#define UI_ICON_HASH "\xEE\x83\xAF" /* U+E0EF hash */
#define UI_ICON_MAIL "\xEE\x84\x8F" /* U+E10F mail */
#define UI_ICON_REFRESH_CW "\xEE\x85\x85" /* U+E145 refresh-cw */
#define UI_ICON_TRASH_2 "\xEE\x86\x8E" /* U+E18E trash-2 */
#define UI_ICON_PENCIL "\xEE\x87\xB9" /* U+E1F9 pencil */
#define UI_ICON_ZAP "\xEE\x86\xB4" /* U+E1B4 zap */
#define UI_ICON_BATTERY_CHARGING "\xEE\x81\x94" /* U+E054 battery-charging */
#define UI_ICON_THERMOMETER "\xEE\x86\x86" /* U+E186 thermometer */
#define UI_ICON_GAUGE "\xEE\x86\xBF" /* U+E1BF gauge */
#define UI_ICON_INFO "\xEE\x83\xB9" /* U+E0F9 info */
#define UI_ICON_LINK "\xEE\x84\x82" /* U+E102 link */
#define UI_ICON_STAR "\xEE\x85\xB6" /* U+E176 star */
#define UI_ICON_KEYBOARD "\xEE\x8A\x84" /* U+E284 keyboard */
#define UI_ICON_FILE_TEXT "\xEE\x83\x8C" /* U+E0CC file-text */
#define UI_ICON_STORE "\xEE\x8F\xA4" /* U+E3E4 store */
#define UI_ICON_WAVES "\xEE\x8A\x83" /* U+E283 waves */
#define UI_ICON_ANTENNA "\xEE\x93\xA2" /* U+E4E2 antenna */
#define UI_ICON_ROUTE "\xEE\x94\xBE" /* U+E53E route */
#define UI_ICON_RADIO_TOWER "\xEE\x90\x84" /* U+E404 radio-tower */
#define UI_ICON_CROSSHAIR "\xEE\x82\xAC" /* U+E0AC crosshair */
#define UI_ICON_LANGUAGES "\xEE\x83\xBE" /* U+E0FE languages */
#define UI_ICON_PALETTE "\xEE\x87\x9D" /* U+E1DD palette */
#define UI_ICON_TIMER "\xEE\x87\xA0" /* U+E1E0 timer */
#define UI_ICON_HISTORY "\xEE\x87\xB5" /* U+E1F5 history */
#define UI_ICON_COPY "\xEE\x82\x9E" /* U+E09E copy */
#define UI_ICON_EYE "\xEE\x82\xBA" /* U+E0BA eye */
#define UI_ICON_SMARTPHONE "\xEE\x85\xA3" /* U+E163 smartphone */
#define UI_ICON_CHART_NO_AXES_COLUMN "\xEE\x81\xA8" /* U+E068 chart-no-axes-column */
#define UI_ICON_MESSAGE_SQUARE_TEXT "\xEE\x95\xB5" /* U+E575 message-square-text */
#define UI_ICON_REPLY "\xEE\x88\xAA" /* U+E22A reply */
#define UI_ICON_SHARE_2 "\xEE\x85\x96" /* U+E156 share-2 */
#define UI_ICON_SMILE "\xEE\x85\xA4" /* U+E164 smile */
#define UI_ICON_SEND_HORIZONTAL "\xEE\x93\xB2" /* U+E4F2 send-horizontal */
#define UI_ICON_ARROW_LEFT "\xEE\x81\x88" /* U+E048 arrow-left */
#define UI_ICON_BATTERY_FULL "\xEE\x81\x95" /* U+E055 battery-full */
#define UI_ICON_BATTERY_MEDIUM "\xEE\x81\x97" /* U+E057 battery-medium */
#define UI_ICON_BATTERY_LOW "\xEE\x81\x96" /* U+E056 battery-low */
#define UI_ICON_BATTERY "\xEE\x81\x93" /* U+E053 battery */
#define UI_ICON_IMAGE "\xEE\x83\xB6" /* U+E0F6 image */
#define UI_ICON_LOCK_OPEN "\xEE\x84\x8C" /* U+E10C lock-open */
#define UI_ICON_BELL_RING "\xEE\x88\xA4" /* U+E224 bell-ring */
#define UI_ICON_CIRCLE_ALERT "\xEE\x81\xB7" /* U+E077 circle-alert */
#define UI_ICON_ARROW_DOWN_WIDE_NARROW "\xEE\x81\x87" /* U+E047 arrow-down-wide-narrow */
#define UI_ICON_USER_PLUS "\xEE\x86\xA2" /* U+E1A2 user-plus */
#define UI_ICON_LIST_CHECKS "\xEE\x87\x90" /* U+E1D0 list-checks */
#define UI_ICON_BAN "\xEE\x81\x91" /* U+E051 ban */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+24 -23
View File
@@ -1,10 +1,15 @@
/*******************************************************************************
* Size: 16 px
* Bpp: 4
* Opts: --no-compress --bpp 4 --size 16 --font /Users/dan.vybiral/.claude/jobs/dadc5d4b/tmp/fa-solid-900.ttf -r 0xF002 --format lvgl --lv-font-name zoom_font -o src/ui-touch/zoom_font.c
* Generated by scripts/build/gen-touch-fonts.sh with lv_font_conv 1.5.3
* Sources: Lucide 1.52.0 (zoom icons)
******************************************************************************/
#if 1
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#ifndef ZOOM_FONT
#define ZOOM_FONT 1
@@ -19,22 +24,19 @@
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+F002 "" */
0x0, 0x3, 0xae, 0xfe, 0xa3, 0x0, 0x0, 0x0,
0x0, 0x9f, 0xff, 0xff, 0xff, 0x90, 0x0, 0x0,
0x9, 0xff, 0x82, 0x2, 0x8f, 0xf9, 0x0, 0x0,
0x3f, 0xf3, 0x0, 0x0, 0x3, 0xff, 0x30, 0x0,
0xaf, 0x80, 0x0, 0x0, 0x0, 0x8f, 0xa0, 0x0,
0xef, 0x20, 0x0, 0x0, 0x0, 0x2f, 0xe0, 0x0,
0xff, 0x0, 0x0, 0x0, 0x0, 0xf, 0xf0, 0x0,
0xef, 0x20, 0x0, 0x0, 0x0, 0x2f, 0xe0, 0x0,
0xaf, 0x80, 0x0, 0x0, 0x0, 0x8f, 0xa0, 0x0,
0x3f, 0xf3, 0x0, 0x0, 0x3, 0xff, 0x40, 0x0,
0x9, 0xff, 0x82, 0x2, 0x8f, 0xfe, 0x10, 0x0,
0x0, 0x9f, 0xff, 0xff, 0xff, 0xef, 0xd1, 0x0,
0x0, 0x3, 0xae, 0xfe, 0xa4, 0x1d, 0xfd, 0x10,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf, 0xd1,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1d, 0xfc,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xcc
0x0, 0x0, 0x26, 0x76, 0x10, 0x0, 0x0, 0x0,
0x1a, 0xff, 0xdf, 0xf9, 0x0, 0x0, 0x1, 0xed,
0x40, 0x0, 0x5e, 0xc0, 0x0, 0xb, 0xd1, 0x0,
0x0, 0x2, 0xe9, 0x0, 0x3f, 0x30, 0x0, 0x0,
0x0, 0x6f, 0x0, 0x7e, 0x0, 0x0, 0x0, 0x0,
0xf, 0x40, 0x8c, 0x0, 0x0, 0x0, 0x0, 0xf,
0x60, 0x7e, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x40,
0x2f, 0x30, 0x0, 0x0, 0x0, 0x6f, 0x10, 0xb,
0xd0, 0x0, 0x0, 0x2, 0xe9, 0x0, 0x1, 0xed,
0x40, 0x0, 0x5e, 0xf6, 0x0, 0x0, 0x1b, 0xfe,
0xdf, 0xfa, 0x8f, 0x50, 0x0, 0x0, 0x26, 0x76,
0x10, 0x8, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x78
};
@@ -44,7 +46,7 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
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 = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}
{.bitmap_index = 0, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}
};
/*---------------------
@@ -106,14 +108,14 @@ lv_font_t zoom_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 = 16, /*The maximum line height required by the font*/
.base_line = 2, /*Baseline measured from the bottom of the line*/
.line_height = 12, /*The maximum line height required by the font*/
.base_line = 0, /*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,
.underline_position = 0,
.underline_thickness = 0,
#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
@@ -125,4 +127,3 @@ lv_font_t zoom_font = {
#endif /*#if ZOOM_FONT*/
+62 -1
View File
@@ -33,7 +33,21 @@ P4_BUILD_DIR="$PROJECT_DIR/build/$P4_BUILD_TARGET"
# removes only the generated CMake/Ninja tree; dependencies stay in place and the
# idempotent patches below remain valid.
if [ "$#" -eq 1 ] && [ "$1" = "fullclean" ]; then
cmake -E remove_directory "$P4_BUILD_DIR"
# Plain `rm -rf`, not `cmake -E remove_directory`: this branch runs BEFORE
# esp-idf/export.sh further down, so cmake is not on PATH yet and the clean
# failed with "cmake: command not found" -- while still exiting 0 and printing
# that it had removed the tree. Nothing reported it, so a caller that cleaned
# and rebuilt got an incremental build on the old configuration. That matters
# here beyond tidiness: the esp-hosted and ESP-AT builds SHARE a build dir
# (the C6 axis does not change P4_BUILD_TARGET) and CMake does not reconfigure
# on an env change, so a failed clean silently yields the other variant.
if [ -d "$P4_BUILD_DIR" ]; then
rm -rf "${P4_BUILD_DIR:?}" || { echo "[build.sh] FAILED to remove $P4_BUILD_DIR" >&2; exit 1; }
fi
if [ -d "$P4_BUILD_DIR" ]; then
echo "[build.sh] FAILED to remove $P4_BUILD_DIR" >&2
exit 1
fi
echo "[build.sh] removed build/$P4_BUILD_TARGET (patched managed components preserved)"
exit 0
fi
@@ -107,6 +121,53 @@ if [ -f "$SDMMC_C" ] && ! grep -q 'wadamesh P4: internal pullups' "$SDMMC_C"; th
sed -i '' 's|sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();|sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();\n slot_config.flags \|= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; // wadamesh P4: internal pullups (no external ones on the SD lines)|' "$SDMMC_C"
echo "[build.sh] patched arduino SD_MMC.cpp (internal pull-ups on the SD slot)"
fi
# --- Build-time patch: a dead C6 must not spin the SDIO rx task -----------------------------------
# With CONFIG_ESP_HOSTED_TRANSPORT_RESTART_ON_FAILURE=n (see sdkconfigs/tdisplay_p4 for why it is
# off), sdio_read_task posts ESP_HOSTED_EVENT_TRANSPORT_FAILURE and then `continue`s — straight back
# into a register read that is going to fail again. On a unit whose C6 runs factory ESP-AT the
# transport NEVER comes up, so that is an unbounded retry storm: measured at ~16 failed reads per
# second, forever, with the whole transport re-initialising on top of it. It floods the USB-CDC the
# companion app talks over and keeps a task busy for the life of the session.
#
# Turning the restart back on is not the answer — that is the boot loop this board had before.
# Both are the same mistake: a co-processor that cannot be reached is a permanent condition, and
# the driver treats it as transient. Park the task instead. The failure event has already been
# posted, Wi-Fi and BLE are simply unavailable, and everything else on the device keeps running.
# Idempotent.
HOSTED_SDIO="managed_components/nicolaielectronics__esp-hosted-tanmatsu/host/drivers/transport/sdio/sdio_drv.c"
if [ -f "$HOSTED_SDIO" ] && ! grep -q 'wadamesh: park instead of spinning' "$HOSTED_SDIO"; then
python3 - "$HOSTED_SDIO" <<'PYEOF'
import re, sys
p = sys.argv[1]
s = open(p).read()
# Both failure arms look identical apart from the log line: event post, optional restart, continue.
old = """#if H_TRANSPORT_RESTART_ON_FAILURE
ESP_LOGI(TAG, "Host is resetting itself, to avoid any sdio race condition");
g_h.funcs->_h_restart_host();
#endif
continue;"""
new = """#if H_TRANSPORT_RESTART_ON_FAILURE
ESP_LOGI(TAG, "Host is resetting itself, to avoid any sdio race condition");
g_h.funcs->_h_restart_host();
#else
/* wadamesh: park instead of spinning. The transport is down and we are
* not allowed to reboot the host, so retrying can only fail again at
* full speed. The failure event is already posted; stop here and let the
* rest of the device run without Wi-Fi/BLE. */
ESP_LOGE(TAG, "transport down, host restart disabled: parking rx task (Wi-Fi/BLE unavailable)");
while (1) g_h.funcs->_h_msleep(60000);
#endif
continue;"""
n = s.count(old)
if n == 0:
sys.exit("esp-hosted sdio_drv.c: failure arm not found; patch needs updating")
s = s.replace(old, new)
open(p, "w").write(s)
print(" patched %d failure arm(s)" % n)
PYEOF
echo "[build.sh] patched esp-hosted sdio_drv.c (park the rx task on a dead transport)"
fi
# ⚠️ The P4/slot-0 branch REBUILDS slot_config from scratch with `.flags = 0`, silently discarding
# the flag added above (that's the branch that actually runs with BOARD_SDMMC_SLOT=0) — so patch that
# struct literal too. Without the flag the IDF host explicitly FLOATS the pads -> deterministic 0x109
+67 -38
View File
@@ -53,13 +53,23 @@
#endif
// --- C6 / esp-hosted bring-up gate -----------------------------------------------------------------
// The ESP32-C6 (Wi-Fi/BLE co-processor over SDIO) doesn't come up yet: the SDIO bus inits, but the
// C6's esp-hosted slave never signals ready, so esp_hosted_connect_to_slave() times out (~12 s) and
// esp-hosted resets the P4 — a boot loop that never reaches ui_task.begin(), leaving the AMOLED dark.
// Until the C6 reset/power/slave-firmware path is proven (needs a reset callback via the XL9535 — see
// TDISPLAY_P4_PORT.md), gate ALL C6-dependent bring-up OFF so the display + touch + LoRa come up.
// LoRa is a raw SX1262/LR2021 on P4 GPIOs, independent of the C6, so the mesh still works over USB/LoRa.
// Flip to 1 once the C6 link is solid to restore Wi-Fi + BLE.
// TWO HARDWARE GENERATIONS, and the image has to match the one in front of it. Current T-Display P4
// V1 units ship the C6 with esp-hosted firmware (2.12.3) and are what TDP4_C6_HOSTED=1 (the default)
// is for. OLDER units still carry the factory ESP-AT firmware and need WADA_P4_LEGACY_AT=1. Flash a
// hosted image onto an ESP-AT unit and the SDIO stream never frames — the log fills with
// "H_SDIO_DRV: len[...]>max[1524] ... Drop" and Wi-Fi/BLE simply never arrive.
//
// That mismatch must cost Wi-Fi and BLE and NOTHING ELSE. All C6-dependent bring-up therefore runs
// at the END of setup, after ui_task.begin() — see the block there. LoRa is a raw SX1262/LR2021 on
// P4 GPIOs, independent of the C6, so the mesh still works over USB/LoRa on a mismatched unit.
//
// esp-hosted RESETS THE HOST on a transport failure, which is what turns a mismatch into a boot
// loop rather than a missing feature. There are two reset paths and they need separate switches:
// CONFIG_ESP_HOSTED_HOST_RESTART_NO_COMMUNICATION_WITH_SLAVE=n covers one, and
// CONFIG_ESP_HOSTED_TRANSPORT_RESTART_ON_FAILURE=n covers the one that actually fires here, about
// 10 s after the connect attempt begins (sdio_drv.c, H_TRANSPORT_RESTART_ON_FAILURE). Both are off
// in sdkconfigs/tdisplay_p4. Measured on an ESP-AT unit before the second one was set: five resets
// in a 45 s capture, each rst:0x9 HP_SYS_LP_WDT_RESET. Do not re-enable either.
#define TDP4_C6_READY TDP4_C6_HOSTED
// BLE over the C6 uses arduino-esp32's hostedInitBLE(), which only exists in arduino-esp32 >=3.3.10.
// We pin 3.3.0 (so esp_hosted can be the C6-compatible 2.0.17), which predates hostedInitBLE — so BLE
@@ -147,10 +157,10 @@ static void wadameshSetup() {
esp_netif_init();
esp_event_loop_create_default();
// 4. C6: runs the FACTORY ESP-AT firmware (never esp-hosted, never reflashed — Kaj's product
// decision 2026-07-15). Wi-Fi comes up via the c6_at AT-over-SDIO worker spawned at the end of
// setup; BLE companion is unavailable on this AT build (advertising commands stubbed) so the
// phone pairs over Wi-Fi (TCP:5000) or USB.
// 4. C6: which firmware it runs depends on the unit's age — see the generation note at the top.
// Either way nothing happens HERE: both backends are started at the end of setup, after the
// UI. On the ESP-AT build the c6_at AT-over-SDIO worker carries Wi-Fi and the BLE companion is
// unavailable (advertising commands stubbed), so the phone pairs over Wi-Fi (TCP:5000) or USB.
#if TDP4_C6_HOSTED
printf("[BOOT] C6 = esp-hosted (lazy Wi-Fi/BLE initialization)\n");
#else
@@ -298,39 +308,13 @@ static void wadameshSetup() {
the_mesh.begin(disp != NULL);
#if TDP4_C6_HOSTED
// Cold-boot clock sync over SAVED Wi-Fi (#383, BootTimeSync.h) -- the same opt-in
// Settings > Clock switch the T-Deck/M9 have. Same place in the sequence as the S3
// main.cpp: before the TCP/WS/BLE transports exist, so its temporary Wi-Fi session has
// nothing live to disturb. Returns Skipped at no cost unless this was a true power-on,
// the user turned it on, and the PCF8563 did not already give a trustworthy time.
{
wifiConfigBegin();
uint32_t synced_epoch = 0;
const BootTimeSyncResult r = bootTimeSyncRun(rtc_clock.timeIsCurrent(), synced_epoch);
if (r == BootTimeSyncResult::Ok) {
rtc_clock.setCurrentTime(synced_epoch); // floor + system clock + RTC chip, like an NTP sync
printf("[BOOT] cold-boot time sync ok: %lu\n", (unsigned long)synced_epoch);
} else if (r != BootTimeSyncResult::Skipped) {
printf("[BOOT] cold-boot time sync: %s\n", bootTimeSyncResultName(r));
}
}
wifiConfigBegin(); // NVS only, no radio — safe this early, and the getters assume it
#endif
serial_interface.begin(Serial, TCP_PORT, WS_PORT);
serial_interface.setBroadcastResponses(true);
the_mesh.startInterface(serial_interface);
#if defined(BLE_PIN_CODE) && TDP4_C6_READY && TDP4_BLE_READY
if (hostedInitBLE()) {
char* nm = the_mesh.getNodePrefs()->node_name;
serial_interface.prepareBle("wadamesh-", nm, the_mesh.getBLEPin());
if (wifiConfigGetBleEnabled())
serial_interface.beginBle("wadamesh-", nm, the_mesh.getBLEPin());
} else {
printf("[BOOT] hostedInitBLE FAILED\n");
}
#endif
// GPS UART resilience (same fix as the S3 boards): the core opens Serial1 with Arduino's
// 256-byte RX ring; one long LVGL frame overflows it and corrupts NMEA, stretching TTFF from
// ~1 min to many. Must precede sensors.begin() (a no-op once the UART runs). Unconditional
@@ -414,9 +398,54 @@ static void wadameshSetup() {
printf("[BOOT] setup done (helper)\n");
return;
#endif
// ---- C6 bring-up: AFTER the UI, for BOTH backends --------------------------------------------
// Nothing below here may run before ui_task.begin(). The C6 is a separate chip that can be
// unreachable — an older unit still carries the factory ESP-AT firmware while this build speaks
// esp-hosted, and then the SDIO stream never frames (H_SDIO_DRV drop storm). The legacy backend
// always started its worker here, after the UI, so a dead C6 cost you Wi-Fi and nothing else.
// The hosted backend was wired in ahead of the UI instead, so the same dead C6 held the AMOLED
// dark through a handshake that could not succeed, and the board read as bricked. Both now start
// here. A co-processor must never be able to take the display, touch and LoRa down with it.
#if !TDP4_C6_HOSTED
// Legacy factory ESP-AT units use the asynchronous AT worker.
c6at_worker_start();
#else
#if defined(BLE_PIN_CODE) && TDP4_C6_READY && TDP4_BLE_READY
// Only bring the C6 up for BLE when BLE is actually wanted. hostedInitBLE()
// is a full esp-hosted handshake, and it ran unconditionally here even though
// the boot banner says "lazy Wi-Fi/BLE initialization" -- so every boot paid
// for it, and on a unit whose C6 speaks ESP-AT it started a transport that
// can never come up for a radio the user had switched off. Honour the stored
// intent the way beginBle() below already did.
if (wifiConfigGetBleEnabled()) {
if (hostedInitBLE()) {
char* nm = the_mesh.getNodePrefs()->node_name;
serial_interface.prepareBle("wadamesh-", nm, the_mesh.getBLEPin());
serial_interface.beginBle("wadamesh-", nm, the_mesh.getBLEPin());
} else {
printf("[BOOT] hostedInitBLE FAILED\n");
}
} else {
printf("[BOOT] BLE off in settings: C6 not started for it\n");
}
#endif
// Cold-boot clock sync over SAVED Wi-Fi (#383, BootTimeSync.h) -- the same opt-in
// Settings > Clock switch the T-Deck/M9 have. Returns Skipped at no cost unless this was a
// true power-on, the user turned it on, and the PCF8563 did not already give a trustworthy
// time. It used to sit before the transports so its temporary Wi-Fi session had nothing live
// to disturb; from here the transports exist but none of them is associated yet, and the UI
// being up first is worth more than that ordering nicety. The clock simply corrects itself a
// moment after the screen appears instead of the screen waiting on the network.
{
uint32_t synced_epoch = 0;
const BootTimeSyncResult r = bootTimeSyncRun(rtc_clock.timeIsCurrent(), synced_epoch);
if (r == BootTimeSyncResult::Ok) {
rtc_clock.setCurrentTime(synced_epoch); // floor + system clock + RTC chip, like an NTP sync
printf("[BOOT] cold-boot time sync ok: %lu\n", (unsigned long)synced_epoch);
} else if (r != BootTimeSyncResult::Skipped) {
printf("[BOOT] cold-boot time sync: %s\n", bootTimeSyncResultName(r));
}
}
#endif
}
+12
View File
@@ -44,6 +44,18 @@ CONFIG_BT_CONTROLLER_DISABLED=y
# C6 SDIO/power path isn't proven yet; without this the host reboots ~10 s after a failed C6 init,
# preventing the UI (and LoRa) from ever coming up. Keep the device alive; Wi-Fi/BLE just stay down.
CONFIG_ESP_HOSTED_HOST_RESTART_NO_COMMUNICATION_WITH_SLAVE=n
# ...and the OTHER reset path, which is the one that actually bites. The line
# above only covers "no communication with slave"; a transport failure after the
# connect attempt times out goes through H_TRANSPORT_RESTART_ON_FAILURE in
# sdio_drv.c and calls _h_restart_host() regardless. On a unit whose C6 still
# runs factory ESP-AT the SDIO stream never frames, so that fires every ~10 s
# and the board boot-loops: measured on Kaj's own P4, 2026-10-05, five resets in
# a 45 s capture, each one rst:0x9 HP_SYS_LP_WDT_RESET right after
# "Host is resetting itself, to avoid any sdio race condition".
# A co-processor that cannot be reached must cost Wi-Fi and BLE, not the device.
# The failure still arrives as ESP_HOSTED_EVENT_TRANSPORT_FAILURE either way,
# which is what the app should surface instead.
CONFIG_ESP_HOSTED_TRANSPORT_RESTART_ON_FAILURE=n
# Panic coredump -> the dedicated 64K 'coredump' partition (see tdisplay_p4_16M.csv). Unlike the
# Tanmatsu (whose table has no coredump partition, so UITask stubs the API), the P4 has one, so
+22
View File
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Host configuration for test/constellation_render: the firmware's colour depth
// and fonts, the system allocator, no tick source (the renderer drives time).
#ifndef LV_CONF_H
#define LV_CONF_H
#include <stdint.h>
#define LV_COLOR_DEPTH 16
#define LV_COLOR_16_SWAP 0
#define LV_MEM_CUSTOM 1
#define LV_MEM_CUSTOM_INCLUDE <stdlib.h>
#define LV_MEM_CUSTOM_ALLOC malloc
#define LV_MEM_CUSTOM_FREE free
#define LV_MEM_CUSTOM_REALLOC realloc
#define LV_DISP_DEF_REFR_PERIOD 16
#define LV_USE_LOG 0
#define LV_USE_USER_DATA 1
#define LV_FONT_MONTSERRAT_12 1
#define LV_USE_FONT_COMPRESSED 1 /* the SemiBold faces are RLE-compressed, as on the device */
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1
#define LV_USE_FONT_PLACEHOLDER 1
#endif
+128
View File
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Renders the Constellation screensaver (src/ui-touch/Constellation.cpp) on the
// host at one logical screen size and writes it as a 24-bit BMP, so its layout
// can be checked for every board without the board. run.sh calls this once per
// size. The scene is a fixed, plausible mesh: repeaters with nodes behind them,
// direct neighbours, a room, stale ghosts, names with emoji in them.
//
// render <width> <height> <out.bmp> [ping]
#include "lvgl.h"
#include "../../src/ui-touch/Constellation.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C" const lv_font_t ui_semibold_12; // the counts in the status lines
static int s_w = 0, s_h = 0;
static uint8_t* s_rgb = nullptr;
static void flush(lv_disp_drv_t* drv, const lv_area_t* a, lv_color_t* px) {
for (int y = a->y1; y <= a->y2; ++y) {
for (int x = a->x1; x <= a->x2; ++x) {
const uint32_t c = lv_color_to32(*px++);
uint8_t* o = s_rgb + ((size_t)y * s_w + x) * 3;
o[0] = (c >> 16) & 0xFF; o[1] = (c >> 8) & 0xFF; o[2] = c & 0xFF;
}
}
lv_disp_flush_ready(drv);
}
static void run(uint32_t ms) {
for (uint32_t t = 0; t < ms; t += 10) { lv_tick_inc(10); lv_timer_handler(); }
}
static bool writeBmp(const char* path) {
FILE* f = fopen(path, "wb");
if (!f) return false;
const uint32_t row = (uint32_t)((s_w * 3 + 3) & ~3), img = row * (uint32_t)s_h;
uint8_t h[54] = {'B', 'M'};
auto p32 = [&](int o, uint32_t v) { h[o] = v; h[o + 1] = v >> 8; h[o + 2] = v >> 16; h[o + 3] = v >> 24; };
p32(2, 54 + img); p32(10, 54); p32(14, 40); p32(18, (uint32_t)s_w); p32(22, (uint32_t)s_h);
h[26] = 1; h[28] = 24; p32(34, img);
fwrite(h, 1, sizeof h, f);
uint8_t pad[3] = {0, 0, 0};
for (int y = s_h - 1; y >= 0; --y) {
for (int x = 0; x < s_w; ++x) {
const uint8_t* c = s_rgb + ((size_t)y * s_w + x) * 3;
const uint8_t bgr[3] = {c[2], c[1], c[0]};
fwrite(bgr, 1, 3, f);
}
fwrite(pad, 1, row - (uint32_t)s_w * 3, f);
}
fclose(f);
return true;
}
static CstNode node(uint32_t key, int bearing, int dist, CstKind kind, int level, int via, const char* label) {
CstNode n = {};
n.key = key; n.bearing = (int16_t)bearing; n.dist = (uint16_t)dist;
n.kind = (uint8_t)kind; n.level = (uint8_t)level; n.via = (int16_t)via;
if (label) snprintf(n.label, sizeof n.label, "%s", label);
return n;
}
int main(int argc, char** argv) {
if (argc < 4) { fprintf(stderr, "usage: render <w> <h> <out.bmp> [ping]\n"); return 2; }
s_w = atoi(argv[1]); s_h = atoi(argv[2]);
const bool ping = argc > 4 && strcmp(argv[4], "ping") == 0;
s_rgb = (uint8_t*)calloc((size_t)s_w * s_h, 3);
lv_init();
static lv_disp_draw_buf_t db;
lv_color_t* buf = (lv_color_t*)malloc(sizeof(lv_color_t) * (size_t)s_w * s_h);
lv_disp_draw_buf_init(&db, buf, nullptr, (uint32_t)(s_w * s_h));
static lv_disp_drv_t dd;
lv_disp_drv_init(&dd);
dd.hor_res = (lv_coord_t)s_w; dd.ver_res = (lv_coord_t)s_h;
dd.flush_cb = flush; dd.draw_buf = &db; dd.full_refresh = 1;
lv_disp_drv_register(&dd);
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x202428), 0); // what it covers
CstTheme th = {0x000000, 0x15B6A6, 0x19D6C2, 0xE6EBED, 0x8A969C, 0x5C686E,
&lv_font_montserrat_12, &ui_semibold_12};
cstShow(lv_layer_top(), th);
// Index: 0 repeater (direct) 1 repeater (via 0)
CstNode n[24];
int c = 0;
n[c++] = node(0xA1000001, 40, 330, CST_REPEATER, 3, -1, "BE-3001 RPT01");
n[c++] = node(0xA1000002, 75, 620, CST_REPEATER, 3, 0, "GNK \xF0\x9F\x93\xA1 Hill");
n[c++] = node(0xA1000003, 210, 420, CST_REPEATER, 2, -1, nullptr);
n[c++] = node(0xA1000004, 300, 700, CST_REPEATER, 1, 2, nullptr);
n[c++] = node(0xB2000001, 95, 820, CST_COMPANION, 3, 1, "Jade \xE2\x9C\xA8");
n[c++] = node(0xB2000002, 20, 560, CST_COMPANION, 3, 0, "pisti87");
n[c++] = node(0xB2000003, 160, 260, CST_COMPANION, 2, -1, "Kaj T-Deck");
n[c++] = node(0xB2000004, 230, 760, CST_COMPANION, 2, 2, nullptr);
n[c++] = node(0xB2000005, 330, 900, CST_COMPANION, 1, 3, nullptr);
n[c++] = node(0xC3000001, 120, 680, CST_ROOM, 2, 1, "Room \xC3\xA9t\xC3\xA9");
for (int g = 0; c < 24; ++g) {
n[c++] = node(0xD4000000u + (uint32_t)g, (g * 47 + 13) % 360, 500 + (g * 97) % 480,
g % 3 == 0 ? CST_REPEATER : CST_COMPANION, g % 2, -2, nullptr);
}
cstSetNodes(n, c);
CstStatus st = {};
snprintf(st.clock, sizeof st.clock, "21:47");
st.clock_ok = true;
snprintf(st.date, sizeof st.date, "TUE 6 OCT");
st.unread = 3;
snprintf(st.unread_word, sizeof st.unread_word, "new");
st.heard = 14;
snprintf(st.info, sizeof st.info, "nodes \xC2\xB7 heard 12 s ago");
st.batt = 86;
st.charging = false;
st.north_up = true;
cstSetStatus(st);
run(1200);
if (ping) {
cstPing(0xB2000001); // two hops in, through both repeaters
cstPingSelf();
run(420);
}
lv_refr_now(nullptr);
if (!writeBmp(argv[3])) { fprintf(stderr, "cannot write %s\n", argv[3]); return 1; }
printf("ok: %dx%d -> %s\n", s_w, s_h, argv[3]);
return 0;
}
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Renders the Constellation screensaver at every board's logical screen size
# (both orientations where the UI can rotate) and writes PNGs for review.
#
# Usage: test/constellation_render/run.sh [outdir] [path/to/lvgl]
# Defaults to the LVGL that PlatformIO fetched for any env (run a pio build first).
set -euo pipefail
here="$(cd "$(dirname "$0")" && pwd)"
root="$(cd "$here/../.." && pwd)"
out="${1:-$root/doc-shots/constellation}"
lvgl="${2:-}"
if [ -z "$lvgl" ]; then
for d in "$root"/.pio/libdeps/*/lvgl; do
[ -f "$d/lvgl.h" ] && { lvgl="$d"; break; }
done
fi
[ -n "$lvgl" ] && [ -f "$lvgl/lvgl.h" ] || {
echo "LVGL sources not found; run a PlatformIO build first or pass the lvgl path" >&2
exit 2
}
mkdir -p "$out"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
flags=(-O1 -w -DLV_CONF_INCLUDE_SIMPLE -I"$here" -I"$lvgl")
mkdir -p "$work/obj"
jobs="$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4)"
export CC_CMD="${CC:-clang} -c ${flags[*]}" OBJ_DIR="$work/obj"
find "$lvgl/src" -name '*.c' -print0 |
xargs -0 -P "$jobs" -I{} sh -c \
'$CC_CMD "$1" -o "$OBJ_DIR/$(printf %s "$1" | cksum | cut -d" " -f1).o"' _ {}
cxx="${CXX:-clang++}"
"$cxx" -std=c++17 -c "${flags[@]}" "$root/src/ui-touch/Constellation.cpp" -o "$work/cst.o"
"${CC:-clang}" -c "${flags[@]}" "$root/src/ui-touch/clock_font_40.c" -o "$work/f40.o"
"${CC:-clang}" -c "${flags[@]}" "$root/src/ui-touch/ui_semibold_12.c" -o "$work/semi12.o"
"${CC:-clang}" -c "${flags[@]}" -DHAS_TANMATSU "$root/src/ui-touch/clock_font_96.c" -o "$work/f96.o"
"$cxx" -std=c++17 -c "${flags[@]}" "$here/render.cpp" -o "$work/render.o"
"$cxx" "$work/render.o" "$work/cst.o" "$work/f40.o" "$work/semi12.o" "$work/obj"/*.o -o "$work/render"
# The Tanmatsu build swaps in the 96 px clock face.
"$cxx" -std=c++17 -c "${flags[@]}" -DHAS_TANMATSU "$root/src/ui-touch/Constellation.cpp" -o "$work/cst96.o"
"$cxx" "$work/render.o" "$work/cst96.o" "$work/f96.o" "$work/semi12.o" "$work/obj"/*.o -o "$work/render96"
# name width height [binary]
while read -r name w h bin; do
[ -n "$name" ] || continue
exe="$work/${bin:-render}"
for mode in still ping; do
arg=""; [ "$mode" = ping ] && arg=ping
"$exe" "$w" "$h" "$work/$name-$mode.bmp" $arg >/dev/null
sips -s format png "$work/$name-$mode.bmp" --out "$out/$name-$mode.png" >/dev/null
done
echo "ok: $name ${w}x${h}"
done <<'SIZES'
tdeck-v4-m9 320 240
tdeck-portrait 240 320
crowpanel-rak 480 320
crowpanel-port 320 480
pager 480 222
tdisplay-p4 284 616
tanmatsu 800 480 render96
SIZES
echo "PNGs in $out"
+24 -6
View File
@@ -120,6 +120,12 @@ int main() {
// survive, and both opt-ins must come back OFF rather than inheriting whatever
// byte followed the blob — an inherited 1 would spend boot time on a Wi-Fi
// session the user never asked for.
// EVERY sum below runs to the end of the struct, so each one has to list every
// field appended since. v64's report_ping + report_done_n were not added, so
// all five assertions stopped compiling -- and a test that does not compile is
// not a guard. This is the check that catches a mid-struct insert, which is
// what broke v44 and shipped the poisoned beta_57 layout. Append a field, add
// it here.
constexpr size_t v53_size = offsetof(Config, boot_wifi_time);
static_assert(v53_size + sizeof(Config::boot_wifi_time) + sizeof(Config::boot_wifi_open)
+ sizeof(Config::loud_alerts) + sizeof(Config::theme_mode)
@@ -131,7 +137,9 @@ int main() {
+ sizeof(Config::ble_kbd_mode) + sizeof(Config::ble_kbd_layout)
+ sizeof(Config::ble_kbd_addr) + sizeof(Config::ble_kbd_addr_type)
+ sizeof(Config::ble_kbd_name)
+ sizeof(Config::ble_kbd_back) == sizeof(Config),
+ sizeof(Config::ble_kbd_back)
+ sizeof(Config::report_ping) + sizeof(Config::report_done_n)
+ sizeof(Config::condense_nav) == sizeof(Config),
"v53 is the current layout minus every byte appended since");
Config v53 = safeDefaults();
@@ -217,7 +225,9 @@ int main() {
+ sizeof(Config::ble_kbd_mode) + sizeof(Config::ble_kbd_layout)
+ sizeof(Config::ble_kbd_addr) + sizeof(Config::ble_kbd_addr_type)
+ sizeof(Config::ble_kbd_name)
+ sizeof(Config::ble_kbd_back) == sizeof(Config),
+ sizeof(Config::ble_kbd_back)
+ sizeof(Config::report_ping) + sizeof(Config::report_done_n)
+ sizeof(Config::condense_nav) == sizeof(Config),
"v55 is the current layout minus every byte appended since");
Config v55 = safeDefaults();
v55.ver = 55;
@@ -244,7 +254,9 @@ int main() {
+ sizeof(Config::ble_kbd_mode) + sizeof(Config::ble_kbd_layout)
+ sizeof(Config::ble_kbd_addr) + sizeof(Config::ble_kbd_addr_type)
+ sizeof(Config::ble_kbd_name)
+ sizeof(Config::ble_kbd_back) == sizeof(Config),
+ sizeof(Config::ble_kbd_back)
+ sizeof(Config::report_ping) + sizeof(Config::report_done_n)
+ sizeof(Config::condense_nav) == sizeof(Config),
"v57 is the current layout minus the Attaky notification fields");
Config v57 = safeDefaults();
v57.ver = 57;
@@ -268,7 +280,9 @@ int main() {
+ sizeof(Config::ble_kbd_mode) + sizeof(Config::ble_kbd_layout)
+ sizeof(Config::ble_kbd_addr) + sizeof(Config::ble_kbd_addr_type)
+ sizeof(Config::ble_kbd_name)
+ sizeof(Config::ble_kbd_back) == sizeof(Config),
+ sizeof(Config::ble_kbd_back)
+ sizeof(Config::report_ping) + sizeof(Config::report_done_n)
+ sizeof(Config::condense_nav) == sizeof(Config),
"v59 is the current layout minus the Home-key drawer option");
Config v59 = safeDefaults();
v59.ver = 59;
@@ -287,7 +301,9 @@ int main() {
static_assert(v60_size + sizeof(Config::ble_kbd_mode) + sizeof(Config::ble_kbd_layout)
+ sizeof(Config::ble_kbd_addr) + sizeof(Config::ble_kbd_addr_type)
+ sizeof(Config::ble_kbd_name)
+ sizeof(Config::ble_kbd_back) == sizeof(Config),
+ sizeof(Config::ble_kbd_back)
+ sizeof(Config::report_ping) + sizeof(Config::report_done_n)
+ sizeof(Config::condense_nav) == sizeof(Config),
"v60 is the current layout minus the Bluetooth keyboard settings");
Config v60 = safeDefaults();
v60.ver = 60;
@@ -308,7 +324,9 @@ int main() {
// v62 appends the extra Back key. A v61 blob keeps the paired keyboard and
// its settings; only Esc goes back until the user picks another key.
constexpr size_t v61_size = offsetof(Config, ble_kbd_back);
static_assert(v61_size + sizeof(Config::ble_kbd_back) == sizeof(Config),
static_assert(v61_size + sizeof(Config::ble_kbd_back)
+ sizeof(Config::report_ping) + sizeof(Config::report_done_n)
+ sizeof(Config::condense_nav) == sizeof(Config),
"v61 is the current layout minus the Back key");
Config v61 = safeDefaults();
v61.ver = 61;
+37 -1
View File
@@ -34,6 +34,10 @@
namespace {
constexpr uint32_t kHotplugMs = 1500; // how often the expansion is looked for
static bool s_scanned = false; // one-shot bus scan (see p4KeyboardPoll)
constexpr uint32_t kHotplugIdleMs = 15000; // probe rate once the expansion looks absent
constexpr uint8_t kMissesBeforeBackoff = 4;
static uint8_t s_misses = 0; // consecutive probes that found nothing
constexpr uint32_t kIdlePollMs = 120; // backstop drain when INT says nothing is waiting
constexpr uint32_t kModTimeoutMs = 1500; // a one-shot modifier lapses after this
constexpr uint32_t kRepeatDelayMs = 450;
@@ -309,9 +313,41 @@ void p4KeyboardPoll() {
if (probe || drain_due) {
if (TwoWire* w = p4I2c1Acquire(P4_I2C1_KEYBOARD, kBusWaitMs)) {
if (probe) {
s_next_probe_ms = now + kHotplugMs;
// Back off while nothing is there. The probe is a real I2C transaction,
// and on an empty bus Arduino's Wire layer logs TWO [E] lines for every
// attempt -- at 1.5 s that is a permanent drip down the same USB-CDC the
// companion app talks over, for a board most people have never clipped
// on. After a few misses, look every kHotplugIdleMs instead; a hit puts
// it straight back to the responsive rate, so hot-plug still works, it
// just takes up to that long to notice.
s_next_probe_ms = now + (s_misses >= kMissesBeforeBackoff ? kHotplugIdleMs
: kHotplugMs);
uint8_t cfg = 0;
const bool there = regRead(w, P4KB_EXP_ADDR, XL_CFG0, cfg);
if (there) s_misses = 0;
else if (s_misses < kMissesBeforeBackoff) ++s_misses;
// One-shot bus scan on the first probe. "It does not work" on this
// expansion has three very different causes and they are impossible to
// tell apart from the outside: the board is not clipped on, it is
// clipped on but unpowered (it carries its own cells, and the firmware
// has no enable for it), or it answers and the bring-up fails. An
// unpowered board does NOT give a clean NACK either: with its pull-ups
// dead the lines sit low through its ESD diodes and every transaction
// comes back ESP_ERR_INVALID_STATE, which reads like a driver fault.
// So print what is actually on the bus, once, and let the log say which
// of the three it is. Same one-shot scan the touch bring-up does.
if (!s_scanned) {
s_scanned = true;
char sc[96]; int n = 0;
n += snprintf(sc, sizeof sc, "[P4KB] i2c46/45 scan:");
for (uint8_t a = 0x08; a < 0x78 && n < (int)sizeof sc - 6; a++) {
w->beginTransmission(a);
if (w->endTransmission() == 0) n += snprintf(sc + n, sizeof sc - n, " %02X", a);
}
if (n < (int)sizeof sc - 24 && !there)
snprintf(sc + n, sizeof sc - n, " (expansion absent)");
printf("%s\n", sc);
}
if (there && !s_present) {
if (!attach(w)) printf("[P4KB] keyboard expansion found, bring-up failed\n");
} else if (!there && s_present) {