Commit Graph
105 Commits
Author SHA1 Message Date
Kaj Schittecat 4e73bcc34a Merge PR #645 (oumike): T-Deck Pro / Max: Normal, Large and Huge text sizes, as on the T-Deck (#639) 2026-10-13 09:18:17 +02:00
Kaj Schittecat 449b8abb67 Merge PR #649 (oumike): Contacts: show the chat icon, picked or the first emoji in the name (#646) 2026-10-13 09:18:14 +02:00
Michael A. CojocariandCopilot c05153ef85 web app: add Reply to the channel message long-press sheet
Long-pressing someone else's message in a channel chat in the remote web
app now offers Reply (above Copy and Info). It prefills the composer with
the device's mention syntax '@[sender] ', keeps any draft, clamps to the
160-char limit and focuses the input. DMs and our own messages are
unchanged.

Fixes #642

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-11 13:46:37 -04:00
Michael A. CojocariandCopilot b0a6f59ea3 Contacts: show the chat icon, picked or the first emoji in the name (#646)
The Contacts list now gives each contact the same icon as its chat: the
chat icon picked for it (keyed on the advertised name), else the first
emoji in its shown name, as #640 does for the chat list. Picking or
resetting a chat icon marks the Contacts list for a rebuild.

Fixes #646

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-11 13:00:53 -04:00
Michael A. CojocariandCopilot 67680763ed T-Deck Pro / Max: Normal, Large and Huge text sizes, as on the T-Deck (#639)
Enable CAP_UI_SIZE and the font-only text-size presets (montserrat
12/14/16, 16/18/20, 18/20/24) for HAS_TDECK_PRO boards, compile the
18/20/24 px UI fonts for them, and size the status bar to match.
Docs list the T-Deck Pro and Max under Text size.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-10 21:44:37 -04:00
Kaj SchittecatandClaude Opus 5.5 2dfe3727cd docs: beta_93 in the guide and the repo docs
Guide (wadamesh.com/docs.html): contact nicknames, the redesigned control
panel with the Battery saver tile, keyboard tooltips and the amber cursor,
automatic chat icons, the default lock wallpaper, the GPS serial speed and
battery notes, the V4 built-in apps and the Lua send budget, and a new
T-LoRa Pager section (hotkeys, navigation memory, Power off confirm, power
saving). Three new screenshots, GPS position and IP masked.

Repo docs: the Pager and M9 key guides describe tooltips, per-tab memory
and Power off, and README, TLORA_PAGER_SHORTCUTS and THINKNODE_M9_SHORTCUTS
no longer use em dashes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 23:38:01 +02:00
Kaj SchittecatandClaude Opus 5.5 d68f276b06 touch: beta_93, contact nicknames, new control panel, tooltips, Pager shortcuts, speed and battery
Contact nicknames: TouchPrefsStore nick blob (48 x 38 B), display-only lookups
(contactShownName, threadShownName, senderShownName), Contact options >
Nickname on the shared text prompt (an empty OK clears it).
Control panel restyled in the launcher's tile language, with a Battery saver
tile; keyboard tooltips (navSetTip), an amber cursor and touch long-press
tooltips (the T-Deck touch read hid them on every poll).
T-LoRa Pager: M/C/A/S on Home, H/S tap and hold, Backspace = Back, N = jump to
new, held Enter = long press, per-tab focus memory, Power off confirm; LVGL
-O2 and IRAM, INT-gated keyboard, idle yield, status-bar setter guards, instant
tabs; PagerGps (u-blox software standby for rests, GPS rail cut when off),
panel sleep, amp off at boot, power-off parks the radio (NSS held), GPS, panel
and keyboard light.
Fixes: the DM duplicate check keys on the attempt and a path reset clears it,
the direct timeout masks the packed path byte, web remote hops (#636);
changelog over http (#638); Lua SDK send budgets and duplicate guard (#606);
GPS speed note (#613, CAP_GPS_BAUD_AUTO); emoji chat icon (#640); default
wallpaper (#626).
Heltec V4: Tetris, 2048 and Breakout unseeded and the built-in Lua stripped of
comments (identical bytecode), 27 KB freed; the baked language table is pinned
to beta_92's files (scripts/build/lang-pin-beta92) so it still fits.
Translations: 39 new keys in 14 languages, language store v24.
Site: theme, dark mode, aurora and guide refresh, SDK send limits, nicknames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 22:08:16 +02:00
Kaj SchittecatandClaude Opus 5.5 9d0301b80a touch: beta_92, faster screens on most boards, Arabic fixes, GitHub fixes
Frame rate: full-width PSRAM draw band (6 bands a frame instead of 14 on the
T-Deck), the still aurora drawn once and copied, LVGL at -O2 with its blend and
label loops in IRAM (scripts/build/perf_lvgl.py, 1 KB more loop stack), circle
and gradient caches, newlib memcpy. Rolled out to the M9, Attaky, RAK, Wio,
CrowPanel and V4-R8; the Pager gets the band only, the V4 the caches only.
Frame benchmark tour (DOC_PERF_BENCH) for measuring it.

Fixes: hop counts read from the packed path length (#621); Arabic shaping
rows patched in LVGL (patch_lvgl_arabic.py), Arabic 101 shift layer, kaf,
symbols, text sent as standard letters (#582); info popup colour runs that
never wrap (#602); keypad nav reaches the chat header (#623); the browser
opens the changelog (#622, gen-changelog.py at every site deploy); the
Theme background colour picker no longer edits the accent; flasher entries
for P4 units with factory ESP-AT Wi-Fi chips (#612, #619).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:53:33 +02:00
Kaj SchittecatandClaude Opus 5.5 218bc6ed3e touch: beta_91, Home becomes widgets, backgrounds, GPS in the bar
Home is a grid of arrangeable widgets (24 kinds, presets, a layout per
orientation, Lua app widgets via wada.widget); Unread, Chat and Mentions
get a large size. Theme > Background puts an aurora (still by default, or
moving), a colour, a picture or nothing behind every screen, with glass
cards and a smoked-glass top bar. A GPS icon in the bar, a GPS battery
saver (u-blox GNSS stop/start where there is no power switch), and a
battery saver that no longer waits for Bluetooth and Wi-Fi to be off.
Also: navigation bar position, every settings page on the redesign's
cards, a faster app drawer, slower screensaver drift for burn-in, text
wrapping at spaces only, the V4-R8 Store Apps tab, and SDK Test marked
extended-SDK only (keeps the 2 MB V4 at 99.8%).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:14:04 +02:00
Kaj SchittecatandClaude Opus 5.5 39f342c67e site: user guide for beta_90, colour and the Theme page
A Colours & theme section: the Theme page (preview, Apply and restart on the
box and in the bar under the title), MORE COLORS! in Regular, Neon and Pastel,
Taste the rainbow, chat icons and colourful bubbles, with a gallery of the
looks. A Theme card in Settings pages; the look rows left the Display card.
Chats describes the per-chat colours and the Initials option.

The main screenshots (Home, Chats, conversation, Contacts, Settings, Map) are
retaken in the new default look. The map is fully loaded this time (the beta_89
shot was taken mid-download), with its coordinates pixelated as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:44:21 +02:00
Kaj SchittecatandClaude Opus 5.5 6fb66d6872 site: user guide for the redesigned interface (beta_89)
New sections for the lock screen and PIN, the Constellation screensaver,
new-message cards, the control panel, text size and the T-Deck trackball;
Home, Chats, Contacts and Settings rewritten for the new screens; Settings
pages regrouped as on the device; every screenshot recaptured on a T-Deck
running beta_89 (location, Wi-Fi name and pairing code masked).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 00:29:59 +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 76a6e86896 touch: emoji in any text field, and the status bar fits the bigger type
Jade asked for three things for Lua apps. Two already existed and the answer is
documentation, not code: wada.sys.epoch() and wada.sys.datetime() have returned
the clock since #245, and wada.ui.input() is the firmware's own field, so it
already carries the long-press Cut/Copy/Paste/All/Sym menu, which is exactly the
"copy/paste from chats into a Lua text field" that was asked for.

The third is here. The long-press menu gains Emoji, so the picker reaches every
field in the firmware rather than only the chat composer: a Lua app's text entry,
a node name, a Wi-Fi password. The row is now sized to the panel instead of a
fixed 48 px per cell. It was already 268 px at five cells, wider than a 240 px
screen, so a sixth would have hung off the M9 and the V4 entirely.

Status bar: the right-hand ladder (battery, signal bars, Wi-Fi, Bluetooth, sleep,
clock) is a chain of fixed offsets tuned for 12 px text. The boards with
font-only UI-size presets grow the font and deliberately leave geometry at 100%,
so the labels widened and the ladder did not, and the battery percentage ran into
the signal bars (jd1227, M9, beta_85 -- the release that gave that board the
presets). SBX() scales those offsets by how much the text actually grew and
returns exactly SC() at Normal, so nobody who kept the default size sees any
change. Verified on the M9, V4-R8, Pager, T-Deck and V4.

SDK docs: corrected the app path. They said to copy the file to /apps/ on the SD
card; a card-backed device roots everything at /meshcomod, so it is
/meshcomod/apps/ there and a bare /apps/ only on boards that store internally.
Following the old wording cost gottlieb8849 an evening.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 18:40:58 +02:00
Michael A. CojocariandClaude Opus 5.5 dbecef0b6f feat(crowpanel-35): add Elecrow CrowPanel 3.5" (ESP32-S3, ILI9488 + GT911) touch target
- New crowpanel_35 variant: display, SD, board and target files, plus board JSON
- Portrait mode is drawn rotated 180°; landscape stays at rotation 3
- Share the GT911 touch driver between Wio Tracker L2 and CrowPanel
  (WioTrackerL2Touch.cpp -> src/helpers/input/LgfxGt911Touch.cpp)
- platformio envs, flasher/site entries, release and merge-bin support
- Tests for the CrowPanel SD codec and merge-bin

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132MkJ7RUV32mV9F7o4PJMm
2026-10-02 20:16:06 -04:00
Michael A. Cojocari 500b24e38c feat(tdisplay-p4): support LR2021 radio variants (#504) 2026-09-26 21:59:58 -04:00
Michael A. Cojocari a3090aebbe fix(tdeck-pro): split v1.0 and v1.1 targets 2026-09-23 16:43:29 -04:00
Kaj SchittecatandClaude Opus 5 c9ecaf383f reports: say how long they ran it, not just a span
The first real report read "works, about a day, 57 min ago" and the two halves
looked like they disagreed. They answer different questions: "about a day" is
how long the tester ran the build, "57 min ago" is when they sent the report.
Now it says "ran it for about a day, reported 57 min ago".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 15:58:23 +02:00
Kaj SchittecatandClaude Opus 5 cf0c8da0ff touch: the report form swallowed taps meant for it, and its name was odd
Two things Kaj hit on a T-Deck within a minute of the first flash.

Taps that missed a checkbox went through the card to whatever sat behind the
popup. The card had LV_OBJ_FLAG_CLICKABLE cleared, copied from the version
picker, and a non-clickable object is invisible to hit-testing, so the tap
carried on to the page underneath. The card is clickable again, and tap-outside
now compares the point against the card's own area rather than assuming nothing
inside it can reach the backdrop. Events do not bubble in LVGL 8 unless asked,
so the clear was never needed for its stated purpose.

The card is also explicitly scrollable and capped to the panel height. Eleven
rows do not fit 240 px, and a card taller than its backdrop puts its own
controls outside the area that catches taps.

"Report this build" reads like reporting a fault with it, which is the opposite
of what the button is for. It is "Send a test report" now, "Send another test
report" once you have, and the form is titled "Test report: <tag>". Renamed in
the docs, the site, the issue template and the tracking-issue text too, so the
instruction people are given matches the button they are looking for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 13:39:58 +02:00
Kaj SchittecatandClaude Opus 5 ee5685a91f reports: every build since stable, and silence that means something
A promote spans every test build published since the last stable, and reports
keep arriving on a build after the next one has been cut, so showing only the
newest beta hid exactly the evidence the decision needs. The page now carries a
chip per build above stable and switches between them.

They never merge. A report vouches for the build it was sent from and for
nothing cut after it; averaging them would let somebody sign off code they
never ran.

The verdict got stricter, because testing it showed it was hollow. "Is anybody
running this board" was answered by the opt-in install count, which most people
will leave off, so a single green board could declare a whole release ready.
A board now also counts as out there once anybody has ever reported on it, on
any build: reports are not opt-in, and somebody who reported on beta_85 still
owns that board when beta_86 lands. So silence on a board we have heard from
before blocks the promote, and silence on a board nobody has ever reported on
does not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 12:02:59 +02:00
Kaj SchittecatandClaude Opus 5 b2030528a0 site: a test-matrix card on the front page, and no GitHub token by choice
The matrix was only reachable from a corner button and a line buried in the
channel explainer. It now has a card in "Docs, tools and help", next to the
others, which is where people actually look.

The tracking issue is dropped: the matrix lives on the site and no write
credential for it exists anywhere. The publishing code stays in place and
dormant, so it is one line in /etc/wadamesh-reports.env if that changes, and
the service and deploy script now say publishes:false is the intended state
rather than a missing piece.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 11:57:18 +02:00
Kaj SchittecatandClaude Opus 5 a80a48988e touch: report a build from the device, and a QR that files a bug
Settings, About gains "Report this build": works or a problem, five tick
boxes for what you actually exercised, how long you have run it, an optional
line. It posts to the report service and lands on wadamesh.com/beta, which is
what the next promote is decided on. Ticking matters: "it works" from somebody
who watched it boot and "it works" from somebody who messaged on it all week
are not the same claim, and a board only goes green on two of the second kind.

"Report a bug" draws a QR for a prefilled GitHub issue form. The issue is filed
from the reporter's phone under their own account, so no write token has to
exist in a public firmware image and the reporter gets the replies.

"Count this device" is off by default. It lets the update check say which board
and version it is running, so a board with no reports can be told apart from a
board nobody owns. Those need opposite responses and nothing could tell them
apart before.

Mechanics: the POST rides the existing core-0 tile/update worker and its
WiFiClient/HTTPClient, like the version check, because a second pair on that
~8 KB stack overflows it. The device id is a salted SHA-256 of the public key
truncated to 64 bits, so a second report replaces the first without saying who
sent it. Sending is two taps and the second lists every field that leaves.
Prefs v64 appends report_ping + report_done_n at the tail, which the schema's
trailing static_assert now checks.

Also here: scripts/build/matrix-check.sh prints what testers reported for a tag,
and release.sh runs it before a --promote. It never blocks; a board nobody owns
can never go green. With no reports at all it now says so rather than reporting
a clean bill of health, which is the habit this is meant to replace.

Built on all eleven S3 envs and the T-Display P4.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 11:29:20 +02:00
Kaj SchittecatandClaude Opus 5 3729c821b3 reports: a test matrix a beta can be promoted on
A beta goes stable today because it has been out a while and nobody shouted.
With 13 board images, most of them in hands we never hear from, "nobody
shouted" mostly means nobody with that board was listening. This makes the
evidence explicit: testers report per board per tag from the device, and the
promote decision reads off a grid.

The device cannot talk to GitHub itself. TLS does not fit (mbedTLS wants ~30 KB
of heap for a handshake and ~5 KB survives Wi-Fi association on the 2 MB
boards), and a token in a GPL image is a token everybody has. So the split is:

  * Structured test reports go to a small service on the firmware VPS over the
    plain HTTP the device already speaks, and it holds the only write
    credential. SQLite is the database; the per-tag tracking issue is rendered
    from it and rewritten in place, so GitHub can be down or rebuilt at will.
  * Bug reports do not come through here at all. The device draws a QR that
    opens .github/ISSUE_TEMPLATE/bug.yml prefilled with board, version and
    diagnostics, and the reporter files it under their own GitHub account:
    right attribution, GitHub's own spam controls, no token.

Green needs two separate devices whose owners ran the build for a day or more.
A board nobody runs cannot go green and does not block a promote; a board
somebody runs and has not vouched for does. That rule is the whole point: it
distinguishes untested from unowned, which "nobody shouted" cannot.

wadamesh.com/beta.html renders the same JSON for the promote decision.

Two things found while deploying, both repo-vs-box drift that would have bitten
somebody eventually:

  * The repo's copy of the firmware vhost was missing /apps/ and /bringup/,
    which had been added on the box and never committed. Deploying it would
    have 404'd the Lua app and language store for every device. The repo copy
    is now taken from the box, and the deploy script refuses to push a vhost
    that has fewer locations than the live one.
  * The repo's tile-transcode unit named a venv interpreter that does not exist
    on the box (python3.14 there has no ensurepip, so venv cannot bootstrap pip
    at all). The live unit uses the system python; the repo now says so too.

The firmware side (the report page, the QR, the opt-in ping) is not in this
commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 11:06:07 +02:00
Kaj SchittecatandClaude Opus 5 bf9d8f775c site: the language count was three releases out of date
The front page still advertised 11 languages and listed the original
built-in set; the docs said twelve in two places and thirteen in a third.
langs.json carries 14 downloadable languages, so 15 with English.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 15:21:47 +02:00
Kaj SchittecatandClaude Opus 5 9c47a9e50d touch: fixes on the merged PRs, and ship the T-Deck Max
Review of #531 turned up four things that had to be fixed before release:

- An unusable region name no longer discards the radio settings. The save
  returned early, so frequency, bandwidth, SF, CR and TX power were thrown
  away, and the rule rejects names with capitals or a "$private" scope, so
  anyone carrying an older region could not change their radio at all. On the
  pager that path is the silent blur save, so it failed with nothing on screen.
  Now only the region is skipped, and it says so.
- Restored useChainedFont() on the telemetry Show button: without the fallback
  chain that label is boxes in Russian, Ukrainian, Bulgarian, Serbian, Greek.
- Region scanning installs unknown repeaters as transient contacts so their
  encrypted replies can be matched. They were never removed, so each scan left
  up to 16 nameless entries in the contact table (and in Contacts). They are
  dropped when the scan ends or the page closes.
- One radio request per scan tick. The loop ran through all 16 repeaters in a
  single 200 ms tick, which is that many key derivations back to back.

The V4-R8 also did not build (its src/ files see a vendored lv_conf.h, so the
new text-size fonts were missing) and then did not fit at 101.3%. It builds at
85.6% now: the board no longer carries the compiled-in translations it
inherited from the V4, which it never needed, since with 8 MB of PSRAM its
store works like any other 8 MB board.

T-Deck Max (#545) joins the release matrix: build list, flasher manifest,
DEVICES.md and the site's board list. Board names in the flasher lost their
em dashes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 14:04:24 +02:00
Kaj SchittecatandClaude Opus 5 6fe6b9f063 touch: beta_83, USB Files (files.wadamesh.com over the USB cable)
A built-in USB Files app (T-Deck and Heltec V4 for now) lets the page at
files.wadamesh.com browse, upload, download, rename and delete files on the
SD card, internal storage and the map tiles over Web Serial. File level, not
USB mass storage: the device keeps its filesystems mounted, the radio keeps
running, and the firmware enforces what may change. Live data (identity,
contacts, settings, history; /meshcomod on the card) is download-only.

- UsbFilesProtocol.h: framed messages (E7 5A, CRC32), a resyncing parser,
  path checks and the access policy; host tests in test/.
- UsbFilesSession: one request at a time (both Arduino USB serial drivers
  drop bytes when their RX queue is full), RX queue sized per session, SD
  data through a DMA-capable bounce buffer, FatFs listings with sizes, a
  beacon so the page never talks first, SD Scan's malware verdict per file.
- The companion link and MyMesh's console step off USB while it runs.
- Website: labelled side buttons, a USB Files button and card; the
  files.wadamesh.com page, vhost and deploy script.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 11:52:34 +02:00
Kaj SchittecatandClaude Opus 5 6868911f59 touch: SD Scan, find and remove Windows malware on the SD card
Some ThinkNode M9 cards shipped with a dormant Windows worm (Elecrow
security advisory, September 2026). An infected card seen since carries
autorun.inf in the root, launching xlfqf.pif on open, explore and autoplay
with random-junk comment lines in between: the Sality autorun pattern.

- SD Scan store app (deploy/apps/sdscan/1.0, requires "sd", not seeded):
  walks the card a small page per tick, lists what it finds and why, and
  removes it after a confirmation screen with Cancel first. It says on
  every screen that it only removes files it recognises and that
  formatting the card is the safe fix. On older firmware it still finds
  threats by name but cannot remove them.
- Firmware: wada.sd.check(path) and wada.sd.remove(path), plus paging for
  wada.sd.list(path, start, max) and caps().sd_clean. What counts as a
  threat lives in SdThreat.h: autorun.inf, Windows program, script and
  shortcut extensions, or a real MZ+PE header under any name. remove()
  classifies again in firmware and refuses anything else, so no app can
  use it to delete tiles, backups or chat history. It clears read-only,
  hidden and system first, because FAT refuses to delete a read-only file.
- A warning when a card with Windows malware in its top folder is mounted,
  at boot or on insert, offering SD Scan (or the Store).
- Tests: test/test_sd_threat.cpp, and SD Scan harness scenarios including
  the real infected card's root. Removal checked on a T-Deck.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 13:33:30 +02:00
Kaj SchittecatandClaude Opus 5 ae7aebfe3e Site: mark the Wio Tracker L2 as partially supported
It was listed as fully supported, but it is community-maintained and not
tested on every release, and every build before beta_80 fetched the wrong file
when updating itself. The badge now says so, in the same form as the V4-R8
card, and points owners on older builds at the website to update.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 12:53:23 +02:00
Kaj SchittecatandClaude Opus 5 20d79edae9 release: publish the LilyGo T-Deck Pro as experimental
Adds LilyGo_TDeck_Pro_companion_radio_touch to release.sh (as
wadamesh-tdeck-pro), a flasher manifest labelled "(experimental)", and a
website card following the V4-R8 pattern: experimental in the heading,
"Partially supported", beta only.

The card says plainly what is known: contributed through #469, all the main
hardware works, but it has been tested on a single v1.1 unit, v1.0 units are
the least proven, and the e-paper display redraws rather than updating
instantly.

The site is not deployed with this commit: the card's install button points at
manifest-tdeck-pro.json, which only exists once a beta containing the board is
published.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 12:20:09 +02:00
Kaj SchittecatandClaude Opus 5 c641aa7158 Site: remove the MeshCore donation banner
The MeshCore founders' givealittle fundraiser has closed (reported on
Discord by jason000008), so the banner was sending visitors to a page that no
longer takes donations. Removed the markup, its styles and its dismiss script
together.

The corner button rails need no change: .fab-rail already falls back to
top:16px, which is exactly what the banner script set once it was dismissed,
so everyone now sees the layout returning visitors already had.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 11:36:07 +02:00
Kaj SchittecatandClaude Opus 5 6ef5ee061d Site: collapse "What's new" by default
A release with twenty-odd notes pushed the whole install flow off the screen, so
the box that is meant to be a summary had become the page.

It is a native <details> now, collapsed on load, reading "See what's new in
beta_78 - 24 changes". The count matters: a bare "click here" asks people to
gamble a tap, while a number lets them decide whether they care. Native rather
than scripted so it opens with the keyboard, works without JavaScript, and does
not need a click handler; the JS only fills the count and forces it closed on
each channel switch, so flipping stable/beta cannot leave a stale panel open.

The default disclosure triangle is hidden and replaced with one that rotates,
because that marker renders differently in every browser.

Markup verified as balanced and the element confirmed to carry no "open"
attribute, which is what makes it start collapsed. I could not render it in a
browser to check visually: the harness refuses to navigate to localhost, and a
file:// page is served as a static snapshot with no JavaScript, so the count and
the collapse behaviour are unverified on screen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 09:04:34 +02:00
Kaj SchittecatandClaude Opus 5 2ee888c2c0 Lua SDK: the T-Deck's missing keys, its trackball click, and send repeats
Three things Jade reported, the first of which is blocking an app submission.

Missing keys on the T-Deck. A Lua app there never received A, P, Q, Enter or
Backspace. The rule that withholds them exists so an app can never swallow its
own exit, which is right on a board where a reserved key IS the only way out.
On the T-Deck it is not: an app page is closed by tapping the back bar, and the
app host is not in the popup registry, so those keys closed nothing while an app
was running. They were simply dead inside every app. They are now forwarded.
The M9 keeps its reservation, because its reserved keys are sentinel bytes for
Back and Home that are never typed text and it has no touch to tap out with, and
the Pager already reserved nothing. That is why this was T-Deck-only.

The trackball click. With an app open the trackball branch forwarded motion and
explicitly discarded the button, so an app could be steered but never clicked.
It now delivers the same synthetic centre tap the touchless boards' select key
sends, on the press edge: the flag is a per-frame "held" level, so forwarding it
raw would have fired a tap every frame the button stayed down.

Repeat counts. The firmware already counts repeaters heard rebroadcasting an
outgoing flood, keyed on a fingerprint taken at transmit time; it is the refresh
glyph on a sent bubble. wada.mesh.send() now returns that fingerprint as its
second value on success, and wada.mesh.repeats(fp) reads the count back. Additive,
so an app reading only the first return is unaffected. The count grows as repeats
arrive, so it is polled rather than read once, and a direct message has none
because it is not flooded. Documented on the SDK page.

Built on all nine S3 envs and both ESP32-P4 targets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 09:28:19 +02:00
Kaj SchittecatandClaude Opus 5 92b2ed12c9 Ship the Wio Tracker L2: release plumbing, not just the build
The PR added the board and its env, which makes it build. It does not make it
ship. Three lists decide that and none of them knew about it: release.sh ENVS
(what gets built and named into a release), gen-flasher-meta.py BOARDS (which
manifests the web flasher gets) and the flasher page itself, which lists boards
by hand.

Without these the board compiles cleanly forever and never appears in a release
or on the flasher, which looks like nothing is wrong.

Named wadamesh-wio-tracker-l2, its own Seeed Studio section on the flasher,
marked beta-channel-only like the Attaky was on arrival, since stable is still
beta_65 and this board has never been in a stable build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 23:59:06 +02:00
Kaj SchittecatandClaude Opus 5 f49a950e30 lua: wada.ui.clear(), so an app can have more than one screen
The SDK documented building widgets in on_open() and gave no way to take them
down, so an app with a second screen drew it on top of the first. pisti87 hit
this building a multi-screen app and had been working around it with buttons;
he also noted that ui.list() has clear() while the page it sits on does not.

The reason this is not just lv_obj_clean() is the handles. WidgetUd holds a raw
lv_obj_t*, guarded only by `if (u->obj)`, so a Lua variable still referring to a
label from the screen just cleared would sail past that check into freed memory.
Every handle now records the generation it was created in, clear() bumps the
generation, and the accessors null the pointer of any handle from an older one.
The null guard that every method already has then turns a stale call into a
no-op. Given the day this codebase has had with use-after-frees, a clear() that
invited one would not have been worth shipping.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 13:02:15 +02:00
Kaj SchittecatandClaude Opus 5 80b9fd1693 lua: let an app reach all its contacts, and write a bigger app
Both reported by Jade, and both were arbitrary caps rather than real limits.

wada.mesh.contacts() scanned the first 200 contacts, returned at most 100, and
said nothing about having truncated. On a device holding 2000 contacts most of
them were simply unreachable. It takes an offset and a limit now, defaulting to
the old 100 and capped at 250 per call, because every entry is an eight-field
Lua table and building thousands in one go would exhaust the per-app heap. The
window moves, so everything is reachable by asking more than once, and the new
wada.mesh.contact_count() means an app can page without probing for the end.

The app source limit was 64 KB, which an ordinary program can reach. It is
192 KB now. The source sits in PSRAM only until luaL_loadbuffer has compiled it,
so this is a transient allocation, not a per-app cost, and the compiled chunk
still has to fit the 256 KB app heap. The Store's download buffer was the other
half of that ceiling and moves with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 12:23:16 +02:00
Michael A. Cojocari acdcc6e7fc Work 2026-08-23 20:32:26 -04:00
Kaj SchittecatandClaude Opus 5 e3c9f9b2cb Merge PR #292: ThinkNode M9 compass (QMC6309), GPS motion in the SDK, GPS Compass app
cvhviz. The M9's magnetometer was documented on the board and nothing had ever
talked to it. The driver is written from the datasheet's register map rather
than SensorLib, whose setOutputDataRate() writes the ODR into the OSR bits, and
the axis orientation is measured on hardware at four headings rather than
inherited from a declaration Meshtastic marks unverified and never uses. The
+-32 G range looks absurd for a 0.5 G planet until you measure the board's own
hard-iron bias at about 7x Earth's field.

Also carries several fixes found while testing on hardware: every Lua app opened
on a white page on keypad-nav boards (the focus highlight harvested the app body
as a target and reverse-video filled the page), a use-after-free in the Lua net
worker when an app closed mid-request, an unfreed http_get buffer, canvas pixel
buffers GC'd while LVGL still drew from them, one RTC I2C read per contact, and
map re-open costing 2.5 s on every visit.

Three changes on merge:

  - The map tile-keep gate read `total && total < 4 MB`, so a board reporting
    zero PSRAM -- the most constrained case there is -- landed on the roomy side
    of the test and kept its tiles. Dropped the non-zero guard.
  - gpscompass is 55 KB of Lua, more than every other app combined, and it wants
    a magnetometer the seeded boards do not have. The author deliberately left
    it out of lua_builtin.h; that intent now lives in the catalog as
    "seed": false rather than in whether someone remembers to regenerate, since
    the generator runs from a pre-build hook as of this branch.
  - consoleModeToggleCb was defined inside a !HAS_TANMATSU region while the
    Settings row that binds it compiles on every board, so the Tanmatsu link
    broke. Moved it out. The console boot path is gated on CAP_CONSOLE alone, so
    the switch now does what it says there too.

Built on all seven S3 envs plus both ESP32-P4 targets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:54:26 +02:00
Kaj SchittecatandClaude Opus 5 eb439c8baa i18n: find the strings the audit could not see, and the apps it never checked
pisti87 reported a long list of text that stays English whatever the language,
and said the strings were in his language file and still did not appear (#257).
Both halves are true, and the reason is the audit.

The extractor only ever recognised TR("literal"). Three very common shapes were
therefore invisible:

  mk_row_btn("Reload tiles in view", cb)      // helper TR()s its parameter
  for (auto& r : rows) TR(r.label)            // literal lives in a local table
  TR(contactsSortOptName(m))                  // helper returns one of several

All three translate correctly at runtime, so the source looks properly wrapped.
But the literal at the call site was never emitted as a key, so it never entered
a .lang file, so no translator could ever supply it -- and adding it by hand
did nothing, because the audit's key list is what the files are checked against.
That is 51 strings across the map options sheet, the sort sheets, the contacts
filters and the home launcher.

The audit now understands all three, plus tr("...") in the Lua apps, and the
newly visible keys are in all thirteen files as placeholders so translators can
see them. 1017 keys, up from 966.

Four strings were genuinely raw and are now wrapped: the reader's idle status,
the Discover empty feed, the crash-report export button and Paste (move/copy).

Lua apps had no way to translate anything at all, so every built-in was hard
English regardless of the device language. wada.sys.tr() gives them the same
table the interface uses; airtime 1.4 is the first to use it, with the
`sys.tr or identity` fallback so it still runs on older firmware.

Two more instances of the drift this issue is really about:

  - gen-lua-builtin.py read out/firmware/apps/, which nothing writes -- the
    deploy rsyncs deploy/apps/ straight to the VPS. So the mirror was stale and
    the two apps added in beta_68 were never baked in: boards that cannot reach
    the Store shipped without them. It reads the canonical directory now, and
    regenerates from the same pre-build hook as the language table.
  - Baking a row whose translation equals its key does nothing, since TR()
    returns the key on a miss. Skipping them takes the header from 1.11 MB to
    939 KB and gives the V4 back 16 KB of flash, which matters at 89%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:29:05 +02:00
Michael A. Cojocari b04ae358b2 feat(lua): add read-only SD directory listing (#309) 2026-08-22 15:17:54 -04:00
Christopher Van HooseandClaude Fable 5 144c4123ad Merge upstream (SDK text measurement, tall-bar and Discord fixes)
Two small conflicts, both additive on each side: upstream added ui.text_w /
ui.text_lines and a "measure" capability while this branch added the panel
colour and the compass/accel caps. Everything from both is kept and the table
hints match the merged key counts.

GPS Compass now uses ui.text_w where the firmware offers it and keeps the
character estimate as the fallback. That estimate is what once put the heading
digits off centre -- it counted UTF-8 bytes, so the degree sign read as two
characters -- and measuring removes the guess entirely, for the dial's centred
text and the satellite meter's reserve alike.

M9, V4, V4-R8, T-Deck and Pager all build; the app harness passes, including
the tilt check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 10:26:10 -04:00
Christopher Van HooseandClaude Fable 5 a650d7874e M9: quieten the bring-up logging, and record the measured IMU frame
Both sensors' 1 Hz debug lines were still on from the axis measurements; off
now (the flags stay, they are how the next board gets measured). Documents the
QMI8658 in M9_PORT.md -- the three attitudes and what each proved, the ADDR_AI
trap, and why tilt compensation was needed at all -- and corrects the
calibration instruction from "tumble it" to "rotate it in one place", which is
the distinction that was corrupting the fit. SDK page gains wada.sys.accel()
and wada.sys.keep_awake().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 10:22:10 -04:00
Kaj SchittecatandClaude Opus 5 0259264a74 touch: fix the batch reported over Discord (#304 #305 #306 #307, part of #257)
* #305 the home Wi-Fi status was decided from WiFi.getMode(), not from what the
  user asked for, so anything leaving the radio in STA made it announce
  "Starting…" indefinitely with Wi-Fi switched off. Gate on
  wifiConfigGetRadioEnabled().

* #304 the Wi-Fi and Bluetooth status strings were raw literals and stayed
  English everywhere. Wrapped in TR(). Most already had translations sitting
  unused in the language files, so this costs translators nothing for
  "Connecting…", "SSID not found", "Off" and "Built-in"; only "Starting…",
  "Auth failed", "Link lost" and "Init…" are new keys.

* #257 "Built-in" had two code paths and only the one you do NOT see went
  through TR(), which is why it looked inconsistent to the reporter.

* #307 the theme-colour buttons were fixed at 124/84 px, measured against
  "Save & restart" / "Reset". Hungarian's "Mentés és újraindítás" ran off both
  edges. They size to their label now, in a wrapping row, so an over-long pair
  drops to two lines rather than clipping.

* #306 the contact action sheet decided scrollability from a hand-maintained
  grid_items tally that the location-sharing button (#266) was never added to.
  When the tally is short the body is left unscrollable and the overflow is
  unreachable. Decide from the height the buttons actually reached instead, so
  the tally can drift harmlessly.

SDK: wada.ui.text_w / text_lines. An app could ask how tall a line is but not
how wide, so anything laying out its own rows had to guess whether a string
would wrap; guessing wrong draws the next row on top. That is exactly what
happened to the SDK Test app (fixed separately as store version 1.4, with
Nearby hardened the same way).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 15:15:56 +02:00
Christopher Van HooseandClaude Fable 5 8910c62d3c Lua: repeating named timers pause with the display too
beta_68 added named timers, so pausing only on_tick left the same problem one
API over: a repeating timer polling a sensor kept running into a dark screen.
One-shots still fire -- those are scheduled app logic, and skipping one would
drop the work rather than defer it, since the slot is released around the call.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 09:03:20 -04:00
Christopher Van HooseandClaude Fable 5 9677dcfe19 Merge upstream beta_68 into the M9 compass / GPS work
beta_68 expanded the Lua SDK (map, lists, packet delivery, discovery, private
messages/rooms, native crypto) across the same files as this branch, so four
files conflicted. Nothing was dropped from either side:

- wada.sys.gps(): both widenings merged into one binding. Upstream's
  fix_time / lat_e6 / lon_e6 and our speed_kmh / course now share a signature,
  and our stricter gate wins -- the call returns nil when the user has GPS
  switched off, not just when there is no fix.
- Altitude is upstream's `alt_m` alone. The resolution first carried `alt`
  beside it to protect a shipped app, but gpscompass has never been published
  to the store (it exists only in this branch and on a bench device), so
  carrying a duplicate key into the API forever was the wrong trade: the app
  reads alt_m instead.
- sysCaps() carries all twelve feature flags (upstream's seven, our compass,
  the four originals) with a matching table hint.
- wada.geo (upstream) and wada.sys.compass (ours) both survive; upstream's
  "no board has a magnetometer" note is corrected in the code and on the SDK
  page, since the M9 now does.
- hostTeardown frees upstream's new POST payload buffer as well as the fetch
  buffer, under the same in-flight guard.
- The catalog keeps all three new apps: upstream's wardrive and nearby, ours
  gpscompass (8 total, every referenced file present).

M9, V4, V4-R8, T-Deck and Pager all compile; the Lua host harness passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 02:43:55 -04:00
Christopher Van HooseandClaude Fable 5 9d477d32df Lua apps: implement the manifest icon; GPS Compass gets the location glyph
Every installed Lua app drew the same generic play glyph in the drawer, and
the manifest's "icon" -- promised in LUA_APPS.md since the plan was written --
was never parsed. It is now read from <id>.json and mapped to a glyph by NAME
(gps / radio / chart / game / ...), not by codepoint: a name is reviewable in
a store submission, the device's JSON scanner only takes quoted strings
anyway, and an app can never ship a glyph the UI fonts lack -- anything
unrecognised falls back to the generic symbol. A bare side-loaded .lua has no
manifest and keeps that symbol too.

GPS Compass declares "icon":"gps", so it now shows the location pin the Map
tile uses.

Also corrected deploy/site/sdk.html, whose manifest table documented
"version", "min_api", "description" and "boards" -- the device has never
parsed any of them -- and described icon as "a single character".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 02:30:32 -04:00
Christopher Van HooseandClaude Fable 5 d91737bb3f Lua apps on the firmware's black; GPS Compass: one-press north, swapped sides
The Lua app page painted itself 0x0E1216 while the rest of the firmware
paints pure black (COLOR_BG), so every app read as a lighter panel floating
over the UI. The page is now black and wada.ui.colors gains `panel` for the
raised surface an app draws on top of it -- the compass dial uses it, so the
instrument stands out instead of the page doing it.

Rotate/flip are gone from the user's side. They existed because the QMC6309's
axis orientation on the M9 is undocumented, which is not the user's problem to
solve by trial and error. `A` now does the whole job: whether the heading runs
clockwise or anticlockwise follows from which way the sensor's Z axis faces,
and that shows in the sign of the vertical field -- Earth's field dips down
north of the magnetic equator and up south of it -- so with a position (a fix,
or the node's last known one) the app reads the handedness off the sensor and
the press only has to set the offset. `F` stays as the fallback for a flat
field or no position at all.

Layout: stats column on the left, dial on the right (its status line above
it, the hint centred along the bottom).

The host harness now tumbles the simulated device during calibration rather
than spinning it flat -- min/max on an axis that never moves subtracts the
true field along it, which is precisely the case the handedness check has to
detect and refuse, and the flat mock was hiding it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 01:52:27 -04:00
Christopher Van HooseandClaude Fable 5 44b824ff3d GPS Compass: status over the dial, target detail, centred hints; label align
Layout, from on-device feedback: the magnetometer/heading-source line moved
from the stats column to a centred line over the dial; the stats panel now
starts at the top of the column, and the rows that freed up went to the
target -- name, range + bearing, how far to turn ("56 deg right", "ahead")
and when the contact was last heard. The key hint is centred along the
bottom edge of the view and spells the actions out ("C calibrate  O rotate
F flip  <> target"). The heading's DIGITS are centred with the degree sign
hanging off their right edge, so the number does not appear to shift as the
reading crosses 100/200; the width estimate also counts characters rather
than bytes now, which is what put it half a glyph off (the degree sign is
two bytes in UTF-8).

Host: label:width(px) takes an optional alignment ("center"/"right") -- an
app cannot measure glyphs, so this is the only way for it to centre a line
exactly. Also excluded the app ROOT from keyboard-nav focus: excluding only
the body moved the reverse-video highlight up one level instead of removing
it, which is why the page was still white.

sideload_app.py retries fput/fend as well as fadd -- the same UART byte loss
that garbles a long line can garble a short one ("Error: unknown command").

Calibration now reports the measured field strength in its toast, which is
the number that says whether the calibration is any good (Earth: 0.25-0.65 G).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 01:45:44 -04:00
Christopher Van HooseandClaude Fable 5 0771e15452 Serial sideload for Lua apps: fput/fadd/fend CLI + scripts/sideload_app.py
The ThinkNode M9's microSD is soldered on, so the SDK's "drop it on the card"
route does not exist there and the Store can only fetch from its own host.
Three console commands write into the same /apps (or /lang) the Store uses:

  fput /apps/<name>                 open (truncate); names [A-Za-z0-9._-]
  fadd <off> <len> <sum> <base64>   append a chunk, every field verified
  fend                              close

Same physical-access trust level as the existing "rm"/"erase", narrower scope
(two directories). DataStore gains a root-aware mkdirRooted(); the CLI line
buffer grows from 80 to 200 bytes.

Why the chunks are self-checking and short: the UART interrupt is not
IRAM-resident, so while the loop is inside a flash-cache pause only the
128-byte hardware FIFO buffers console input and the middle of a longer line
is lost -- observed on the M9 as a 197-char line echoed back as 120. Offset,
decoded length and byte sum reject a damaged line; the host re-sends. The
host script also waits for "[BOOT] ui ready" because the CH34x bridge resets
the board whenever the port is opened and the console is not serviced before
the UI is up.

Verified on the M9: gpscompass.lua (19055 B) + .json pushed, sizes confirmed
by the device and by `ls /apps` after a reboot. M9 and V4-R8 envs compile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 01:15:56 -04:00
Christopher Van HooseandClaude Fable 5 b273f20e26 M9 compass (QMC6309) + GPS motion for Lua apps; GPS Compass Store app
Firmware
- variants/thinknode_m9/M9Compass.{h,cpp}: QMC6309 at 0x7C on the peripheral
  bus. Probe by chip id, soft reset with the explicit clear, CTRL2 0x20
  (50 Hz, +/-32 G, set/reset on), CTRL1 0x61 (normal, OSR 8/8), read-back
  verified. Synchronous read from the Lua host bridge, 1 s cache, lazy
  re-probe while the rail-powered part is still in POR, OVFL kept + flagged +
  logged. HAS_M9_COMPASS=1 in the env -> new hardware gate CAP_COMPASS.
- src/helpers/WadaNmeaLocationProvider.h: Wadamesh-owned copy of the core
  MicroNMEALocationProvider that also exposes RMC speed/course (the core
  keeps its parser private; no libdeps patch). M9 target.cpp builds on it,
  HAS_GPS_MOTION=1, wadaGpsMotion() for UITask.
- wada.sys.gps(): + alt (m), + speed_kmh/course where the board provides
  them (course only while moving -- an empty RMC course parses as 0), and
  nil while the user has GPS switched off. wada.sys.compass(): {x,y,z,ovfl}
  Gauss, sensor frame, uncalibrated, registered only where CAP_COMPASS;
  caps().compass. Calibration, axis mapping and the heading maths live in
  the app so they can be adjusted per user without a firmware cut.
- Host: luaHostContactAt reads the RTC once per contacts() walk instead of
  once per contact (an I2C transaction each on the M9); pressCb reports
  press coordinates in body content space (scroll offset folded in).

App
- deploy/apps/gpscompass/1.0 + apps.json: rotating rose with a fixed index,
  live fix readout, bearing/range to a selected contact, magnetometer
  heading with hard-iron calibration (C), frame rotate/mirror (O/F, the
  M9's sensor orientation is undocumented), GPS-course fallback on every
  other board, saturation warning. Not baked into lua_builtin.h on purpose
  (CAP_BUILTIN_LUA_APPS also removes the Store > Apps tab).

Verified: M9, V4, V4-R8, T-Deck compile (M9 flash +2 KB); two adversarial
review passes, all confirmed findings fixed; host Lua harness (vendored
Lua, LUA_32BITS) -- calibration recovers a simulated bias exactly, heading
error 0 deg, worst tick ~10k of the 100k budget. Hardware validation list
(axis orientation, bias magnitude, 0x7C ACK) in M9_PORT.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 00:53:16 -04:00
Kaj SchittecatandClaude Opus 5 b15d422bdc touch: beta_68 — Lua SDK gains the map, lists, packet delivery and discovery
The four capabilities that kept third-party apps a sketch of a built-in one:

* wada.map      the firmware's own tiles, projection and cache inside an app
                page, with its own capped pool so it never evicts the Map tab's
* wada.ui.list  the missing "pick one of N" widget; rows are real buttons, so
                keyboard and trackball nav walk them for free
* on_packet     each frame delivered once instead of polling a 16-deep ring,
                which sampled rather than observed
* wada.mesh.discover  the active zero-hop probe, behind its own permission
                because it spends every neighbour's airtime, not just ours

Plus the surface those need to be useful: packet identity reported only where
the frame actually carries it, exact micro-degree coordinates (Lua is built
LUA_32BITS, so its floats were quietly costing a metre), altitude and satellite
time, wada.geo, wada.ui.input, named and one-shot timers, http_post, windowed
fs.read, and wada.sys.env on a hardware gate rather than the memory one.

Fixes:
* Both ESP32-P4 targets could not link. g_wifi_last_disc_reason was defined in
  src/main.cpp, which the IDF builds never compile, so all nine S3 envs stayed
  green while Tanmatsu and T-Display P4 were dead.
* Map zoom level was invisible in +/- buttons mode; the readout was hidden with
  the slider it was anchored to.
* Hungarian and Dutch held each other's "No SD card" translation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 21:50:01 +02:00
Kaj SchittecatandClaude Opus 5 a6464e7128 docs: explain Discover and wardriving
Neither was documented. The Discover app has been in since beta_47 and the user
guide never mentioned it, so the only way to find out what it did was to open it.

The section leads with the thing that makes it different from everything else in
the firmware: Discover ASKS rather than listens. It broadcasts a request that
neighbouring repeaters answer directly, so a reply proves reachability from the
exact spot you are standing, which is also what makes the wardrive data mean
something.

Explicitly separates two names that are one word apart and are not the same
thing: the "Discovered" list in the Contacts overflow is passive, built from
overheard adverts, while the "Discover" app is active. Cross-linked from the
Contacts section where that confusion actually happens, and from the Map section
where the coverage dots show up with no explanation of where they came from.

Covers what a user needs to actually do one: SD card in, wait for a GPS fix, open
Discover and leave it open, then read the map. States the sampling rule (~15 m of
movement, or 20 seconds standing still) so the behaviour is predictable rather
than mysterious, and says plainly that it transmits continuously while open, so
it costs power and airtime and is not something to leave running all day.

Documents the two real limitations rather than leaving them to be discovered: the
map keeps only the newest 160 samples in memory, and the CSV on the card is the
only part that survives a reboot; and there is no on-device viewer for that log,
so reading it back means taking the card to a computer.

Live on wadamesh.com/docs.html.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 19:42:38 +02:00