Commit Graph
87 Commits
Author SHA1 Message Date
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 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
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 a3090aebbe fix(tdeck-pro): split v1.0 and v1.1 targets 2026-09-23 16:43:29 -04:00
Kaj SchittecatandClaude Opus 5 df99548a10 touch: text sizes on the M9, landscape on the T-Display P4
The M9 gets the V4-R8's text-size presets verbatim, because it is the same
240x320 panel and two tables for one screen size is two things to keep in step.
Fonts grow, geometry does not: rows, cards and controls keep their dimensions
and stay reachable, which is why the labels say "Large text" rather than a
percentage.

⚠️ The migration matters more than the feature. ui_scale has been in the saved
blob since v8 and every M9 carries the large-screen default of 1 that this board
never applied, so exposing the selector without resetting it once would bring
every M9 back from the update rendering Large text nobody chose. Prefs go to v65
with that one-time reset, which is the same correction v63 had to make for the
V4-R8 after it happened to them.

The P4 can run landscape. Its MIPI-DSI panel has no MADCTL to rotate, so LVGL
rotates each flushed area into panel coordinates, exactly as the Tanmatsu
already does on the same silicon; the difference is that the P4 reads the saved
preference instead of pinning one rotation. Portrait stays the default and the
existing Orientation button does the rest.

The rotated branch of the P4 touch mapping had never run: it was carried "for
parity" while the panel was portrait-only, and it clamped rotated coordinates
against the portrait bounds, so every landscape touch past x=284 would have
pinned to the screen edge. It clamps against the swapped space now.

Landscape is UNTESTED on hardware: Kaj did not have his P4 with him, and it
ships that way deliberately, with the release notes saying so and asking P4
owners to report smoothness and touch accuracy near the edges.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 13:58:31 +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 4df42eedf4 Merge PR #531 from oumike: device usability, input and accessibility
M9 Back and Bluetooth-keyboard Esc stop at the locked app-drawer root, M9
focus fixes, optional M5Stack CardKB on the V4 and V4-R8, V4-R8 touch and
text-size accessibility, Wio L2 SD retry, high-contrast day and night themes
(#544), channel region discovery (#541) and the Czech translation
contributed in #540 by brebtatv.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 13:54:09 +02:00
pelgraine 8dec5cf78d Add LilyGo T-Deck Max target
Signed-off-by: pelgraine <140762863+pelgraine@users.noreply.github.com>
2026-09-21 20:57:46 +10:00
Michael A. Cojocari bf114b4618 feat(heltec-v4): support external CardKB 2026-09-18 15:30:44 -04:00
Michael A. Cojocari e796984ad4 fix(heltec-v4): improve expansion kit usability 2026-09-17 11:55:24 -04:00
Kaj SchittecatandClaude Opus 5 2f6984443d touch: fix the chat page-scroll panic on long chats (#428, #475)
Pressing down past the last message page-scrolls the chat list with an
LVGL animation. In a long chat the list uses compressed scroll
coordinates, and chatVirtRemap1To1Scroll re-anchored the position on
every LV_EVENT_SCROLL with lv_obj_scroll_to_y(LV_ANIM_OFF). That deletes
the running animation from inside its own step. LVGL 8.4 reads the
animation again after the step, and when the step was also its last
(the UI loop was busy for longer than the animation) it finished the
freed animation and freed it a second time. By the next round that
memory belonged to something else, and anim_timer called a garbage
get_value_cb: the jump to 0x00020000 in the beta_79 and beta_80 M9 dumps,
both at the same call site.

- chatVirtRemap1To1Scroll follows a scroll that an animation drives and
  leaves the re-anchor to chatVirtOnScrollEnd. The page scroll now also
  completes on long chats; the early re-anchor stopped it after a frame.
- scripts/build/patch_lvgl_anim_uaf.py skips LVGL's completion check when
  the animation list changed during the step, as LVGL 9 does. Applied to
  every touch env as a pre-script and to the vendored P4 copy
  (fetch-deps.sh, build.sh). Idempotent, fails closed on source drift.
- test/lvgl_anim_uaf/run.sh reproduces it on the host under
  AddressSanitizer: stock LVGL with the old handler reports the
  use-after-free in anim_timer, and either fix alone runs clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 14:43:14 +02:00
Michael A. Cojocari f3136ea951 Merge branch 'main' into feat/tdeck-pro-target
# Conflicts:
#	deploy/apps/lang/bg.lang
#	deploy/apps/lang/de.lang
#	deploy/apps/lang/el.lang
#	deploy/apps/lang/es.lang
#	deploy/apps/lang/fr.lang
#	deploy/apps/lang/hu.lang
#	deploy/apps/lang/it.lang
#	deploy/apps/lang/nl.lang
#	deploy/apps/lang/pt-br.lang
#	deploy/apps/lang/ro.lang
#	deploy/apps/lang/ru.lang
#	deploy/apps/lang/sr.lang
#	deploy/apps/lang/uk.lang
#	src/ui-touch/UITask.cpp
2026-09-08 15:41:45 -04:00
Kaj SchittecatandClaude Opus 5 c106d47b9b At-a-glance body drops to 20 px on the M9 (#446)
Buko84: the notification text is too big on the M9. It is, and the fix was
already made once for the same reason.

28 px was chosen so the message is legible from across a room, which it is, but
on a 240x320 panel it fills the glance and crowds out the message it is supposed
to be showing. The T-Deck was moved to 20 px for exactly this and the M9 has the
same size panel; nobody widened the gate at the time.

Three things had to change, not one: the extern declaration, the 20 px Latin
fallback font's own build gate, and a LV_FONT_MONTSERRAT_20 flag on the M9 env,
which was not building that size at all. Missing any of them is a link error
rather than a small font, which is why it is worth naming.

All nine S3 envs and both ESP32-P4 targets green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 16:32:51 +02:00
Michael A. Cojocari 3a2781bb6e Work 2026-09-04 21:04:44 -04:00
Kaj SchittecatandClaude Opus 5 e374031056 Merge PR #391: Seeed Wio Tracker L2 support
oumike. ESP32-S3, 16 MB flash, 8 MB octal PSRAM, quad-SPI 320x240 panel via
LovyanGFX, GT911 touch, SX1262 at 22 dBm, GPS, microSD on SD_MMC. New board
file, new variant, one new env. Seeed start selling it next week and the
maintainers have the hardware; we do not.

Merged on their hardware bring-up rather than ours: display, touch, GPS,
microSD, battery and buttons are confirmed on-device by the author. What this
side can prove is that it does not cost anyone else anything, so the whole
matrix was built: all nine S3 envs including the new one, plus both ESP32-P4
targets.

The PR also carries accumulated touch-UI work from the author's development
line, which he flagged himself and offered to split out. Merged as-is because
the three-way merge came through with zero conflicts and nothing from today was
reverted: the prefs schema is still v54, and kb_force_legacy, boot_wifi_open,
the P4 gpsEnsureBigRxRing fallback and the chat-store migration logging are all
still present. Verified explicitly rather than assumed, since the diff against
main showed 1706 deletions purely because the branch predates today's merges.

Two bring-up findings worth keeping: Arduino's Serial1.setPins() takes (rx, tx),
so PIN_GPS_TX is the pin we receive on -- the same naming trap the V4-R8 and
Pager envs already carry, and it left this board's GPS listening on a silent
pin. And the panel is fixed landscape (CAP_ROTATABLE 0), so the keyboard's
rotate arrows are not built there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 23:55:30 +02:00
Michael A. Cojocari 19d557e70a Work 2026-09-03 12:52:03 -04:00
Michael A. Cojocari 0b7afc282b Fix randomized direct-message timestamps 2026-09-01 16:56:23 -04:00
Michael A. Cojocari 9ba2fc2222 Work 2026-08-29 15:57:23 -04:00
Michael A. Cojocari de778ff09d Working on fantastical square. 2026-08-29 14:42:49 -04:00
Michael A. Cojocari ed29da087a Work 2026-08-27 13:53:55 -04:00
Kaj Schittecat eaae4374fb Merge PR #319 2026-08-27 10:28:15 +02:00
Michael A. Cojocari b5a8c99f55 Complete ThinkNode M9 keyboard parity
Signed-off-by: Michael A. Cojocari <michael.cojocari@gmail.com>
2026-08-25 16:59:15 -04:00
Tesso M Costa 190e73498c Merge branch 'main' into at-glance
# Conflicts:
#	src/ui-touch/UITask.cpp
#	src/ui-touch/extras_lat_20.c
#	src/ui-touch/extras_lat_28.c
2026-08-24 09:39:34 -06: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
Kaj SchittecatandClaude Opus 5 a9cff6f113 i18n: translate the map credits sheet, and make the baked table follow the files
The map About/credits sheet was 820 bytes of raw English built with snprintf and
no TR() anywhere in it, so it stayed English in every language (#257). It is now
three keys: the two attribution headers (the OpenTopoMap variant is credited
separately because its style is CC-BY-SA) and the body, kept whole rather than
split per paragraph so translators get prose instead of fragments. The buffer
grows 820 -> 2048 because Hungarian runs about 1.5x English here and the
Cyrillic and Greek files are two bytes a letter.

Hungarian text from pisti87 (#257). Two edits to what he posted, both flagged on
the issue: the hard line breaks he inserted at the English wrap points are gone,
because the label wraps itself and a fixed break lands mid-sentence on any other
panel width; and the header reads "Terkep adatok" rather than "Map adatok",
which looked like a copy-paste artifact given the rest is fully translated. The
OpenTopoMap variant is derived from his own wording and is his to correct.

Also raw, from the same report: the Discovered auto-add hint and the four type
words it interpolates. The hint buffer goes to 240 bytes and the type list to
128, since the translated plurals are longer than "chats, repeaters".

The reason none of that would have shipped: gen-lang-builtin.py exists so the
baked-in table and the .lang files the store serves cannot drift, and its
docstring promises a pre-build step that runs it. Nothing ran it. Editing a
.lang and building produced an image carrying the OLD translations, silently.
It is now a real pre: hook on all seven PlatformIO envs and a line in both IDF
build scripts, regenerating only when a .lang is newer than the header.

deploy-apps.sh grew the matching check for the other half of that path: a
catalog version that disagrees with the file's own "# ver:" publishes
translations to a version no device asks for.

All thirteen languages snapshot to v17 -- the merged region and SD work added
keys to every file, not just Hungarian.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:06:39 +02:00
Christopher Van HooseandClaude Fable 5 fcc146f57a M9 IMU (QMI8658) + tilt-compensated heading
A two-axis magnetic heading assumes the device is level. At this latitude the
field dips ~60 degrees, so the vertical component is 1.6x the horizontal one
and tipping the device leaks it into the pair the heading is made from: about
1.5 degrees of heading per degree of tilt. That is what "it drifts" was once
the calibration was sound -- it was the hand holding it.

Driver: variants/thinknode_m9/M9Imu.{h,cpp}, QMI8658 at 0x6B on the peripheral
bus, accelerometer only (the gyro is most of the power budget and nothing here
needs it): +/-2 g at 62.5 Hz with the low-pass on, soft reset with a 160 ms
wait that covers both die variants, and the same idle-suspend as the compass so
it costs nothing when unused. CTRL1's ADDR_AI bit is set and read back -- with
it clear the burst read silently returns six copies of one byte, which looks
like a working sensor reporting nonsense. HAS_M9_IMU -> CAP_IMU ->
wada.sys.accel(), caps().accel.

Axes MEASURED, not guessed, by holding three attitudes and logging:
  flat, screen up        z = -1.02  -> +Z into the screen (down)
  on bottom edge, top up x = +0.97  -> +X at the top edge (forward)
  on left edge, right up y = +1.08  -> +Y at the right edge
So the IMU is already in the aerospace body frame, and it agrees with the
magnetometer's independently measured +Z-into-screen. (Meshtastic's M9 driver
passes both sensors through untransformed and mirrors the heading on the sign
of accel Z, so its compass flips when the device is turned over. Not copied.)

Heading now rotates the field back into the horizontal plane using gravity
(NXP AN4248 / ST AN3192) before taking the angle, and reports the tilt angle;
past 55 degrees it says "too steep to read" rather than lying. Calibration
returns to a 3D fit because tilt compensation needs the vertical offset too --
but the instruction is now "turn it every way ON ONE SPOT", and the
accelerometer VERIFIES it: coverage is measured by how far gravity swung, so a
flat spin is refused by name ("turn it nose over tail") instead of silently
fitting a degenerate sphere. Simulated in the harness against a modelled M9 in
a known attitude: offsets recovered exactly, and the heading holds within 3
degrees through 20 degrees of roll and pitch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 10:00:44 -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
Christopher Van HooseandClaude Fable 5 844d8c3357 V4-R8 perf pass + M9 UI polish (prefs v49/v50)
Heltec V4-R8 — "are we getting full performance?" audit. Compute side was already
at spec; six gaps closed (record: variants/heltec_v4/R8_AUDIT.md "Perf pass"):
- FEM LNA defaulted OFF on a KCT8103L board: prefs v49 defaults it ON on the R8
  (one-time flip of existing installs, no new field). Toggle stays in Radio & Mesh.
- Display SPI 40 -> 80 MHz (LGFX_SPI_WRITE_HZ; -D to 40000000 steps back).
- Async DMA band flush (LGFXDisplay::flushBandRGB565): swap into a 12 KB internal
  DMA buffer, one no-convert DMA per band, lv_disp_flush_ready at once so LVGL
  renders band N+1 while band N drains; lv_disp_flush_is_last closes the frame
  transaction so the shared micro-SD gets the bus between frames. Sync fallback.
- Wi-Fi modem power save is a pref (wifi_ps; toggle in Wi-Fi settings, live once
  associated). Default ON everywhere (unchanged), OFF on the R8.
- micro-SD 4 -> 20 MHz after the proven mount, read-verified (probe-file head
  captured at 4 MHz and byte-compared after the raise; falls back). Wired at all
  three mount sites; no-op without SD_SPI_FAST_HZ. Note: SDFS::begin returns true
  if already mounted, so every clock change goes through SD.end() first.
- R8 env boots at ESP32_CPU_FREQ=240 (setup() no longer runs at the V4's 80 MHz).
- Settings -> About "Perf:" row on the R8 (no serial console on that board):
  "CPU 240 · TFT 80 MHz DMA · SD 20 MHz · LNA on" is the all-green reading.

ThinkNode M9 UI:
- Spectrum page root gets NAV_SKIP_FLAG: nothing on it is actionable, so the
  d-pad no longer highlights the chart / scale box; Back closes it as before.
- Map "Show tile z/x/y" line defaults OFF (prefs v50, one-time flip; toggle kept).
- The tab bar is bypassed entirely on the M9: the accent "glow" indicator lived on
  the screen, not in the bar, so hiding the zero-height bar left it glowing over
  the content's bottom edge. No bar styling / gestures / key hints / indicator
  are built on the M9 now.

Cross-compiled: R8, V4, M9, T-Deck. Flashed + booted on the user's R8 and M9.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 13:51:11 -04:00
Christopher Van HooseandClaude Fable 5 1b7d793ceb M9: audit pass 2 — 29 verified fixes (nav soft-locks, gate parity, deep sleep, build hardening)
Second 7-dimension adversarially-verified audit over the 08-19 tree.
Headliners: SUB_MAP over a display-only Lua app orphaned the app page
(key-only soft-lock); Back-ladder z-order redesign (CC/power always-
frontmost, confirm-modal deference, Lua key-forward suppressed while a
confirm is up — the send-permission dialog was unanswerable); map pan
flag could go stale across tab jumps/popups; null-close progress rows
now genuinely block the registry dismiss (shared fix); terminal RX
mirror, fullscreen title, wallpaper caption, storage-error guidance
widened to M9; accent/@-mention pickers suppressed (dead chrome on a
touchless board); kb-backlight cache only latches ACKed duties (0xFF
sentinel == duty 255 skipped the first write every boot); deep sleep
actually drops the rails now (display refcount, LEDC pin re-route,
RTC/digital holds) and powers off radio+GPS; TCXO fallback no longer
codifies the disproven 0.0f; RadioLib old-FW patch fail-closes at
link; ENV_SKIP_GPS_DETECT + CORE_DEBUG_LEVEL=0 added to the env.

Full round log in M9_PORT.md 'Audit pass 2 (2026-08-20)'. All four
touch envs (M9, T-Deck, V4-R8, pager) compile clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 08:07:37 -04:00
Christopher Van HooseandClaude Fable 5 68e3b45932 M9 + V4-R8: 2026-08-19 audit passes
ThinkNode M9 (see M9_PORT.md audit section): restore nav-mode HW Back,
Lua-app key forwarding + d-pad support, popup-registry gates widened,
SD-backed data store + chat history, ADC2 battery sample filter,
keyboard poll throttle + backlight, map tile cache to SD, spectrum
sweep LR1110 fix, power-menu cleanup for buttonless hardware.

Heltec V4 R8 (see R8_AUDIT.md): chat history/battery log follow SD
adoption, reinsert watch no longer unmounts live store, tile cache
prefers SD, CAP_ROTATABLE reboot trap defused, SD CS parked at boot.

Shared: atomic threads-index write (tmp+rename), recursive
scroll-into-view during keypad nav, textarea focus highlight.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 07:25:18 -04:00
Michael A. Cojocari fa0f426f5b Fixed. 2026-08-14 15:57:08 -04:00
Kaj Schittecat 48a71695e7 touch: pin core-v1.17.4 — polled receive restores the T-Display P4
Verified on device: the P4 receives channel messages and delivery
confirmations again, and the About RX counters move off zero.
2026-08-10 12:39:00 +02:00
Kaj SchittecatandClaude Opus 5 335a64c118 touch: core-v1.17.1 (beta_60 boot loop + unreachable contacts) + release firmware-data gate
Core (core-v1.17.1, meshcomod 5406093): 1.17 reserved MAX_ANON_CONTACTS slots
at the head of contacts[], which broke two things in beta_60 —

- resetContacts() claimed those slots while the lazily-allocated PSRAM table was
  still NULL, so the new bootstrapRTCfromContacts() NULL-deref'd at boot on any
  device whose contact store loaded nothing: fresh install, erase-flash, or SD
  not mounted yet (#249). Boards with saved contacts booted fine, which is how
  it passed bench testing.
- getContactByIdx() stayed raw while getNumContacts() excludes the reserved
  slots, so every pairing of the two — contact list, action sheets, phone-app
  sync, getContactForSave — read empty slots and could not reach the newest 8
  real contacts. A just-added contact was invisible and its action-sheet
  operations resolved to a blank slot (#252).

Fork side:
- loadContacts() skips blank records, clearing the placeholder contacts beta_60
  wrote into the contacts file (the next save drops them permanently).
- Chat threads follow a peer's rename. The thread is matched by key but its name
  was never updated, while inbound messages are filed by sender name — so the
  first message after a rename created a duplicate thread (#252).
- release.sh derives FIRMWARE_VERSION / build date / core version from the tag
  and the pinned core, keeps the in-tree dev default in step, and ABORTS the
  release if any staged image does not embed its own tag. beta_60 shipped
  reporting v1.16.0-touch because that value was hand-maintained.

All 8 S3 envs build green on core-v1.17.1.

Reported-by: Pierre747, rustinmyeye, myshoeisonfire

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 08:20:00 +02:00
Kaj SchittecatandClaude Fable 5 5526246683 core-117-prep: pin the real core-v1.17.0 tag (lean f453c7d on meshcomod)
All 8 S3 envs rebuilt green against the GitHub tag; the tag's tree is the
PR #46 merge commit (609c903) minus prebuilt/ — identical content to the
local test pin the branch was validated on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:29:13 +02:00
Kaj SchittecatandClaude Fable 5 31e373a4b1 core-117-prep: finish the ColorVal migration on the IDF display headers + pager core-scope includes
TanmatsuDisplay.h / RM69A10Display.h / HI8561Display.h still had the 1.16
Color signatures (missed in the first sweep); pagers additionally hand the
core lib scope the TFT_eSPI include path (target.h -> ST7796LCDDisplay.h).
All 8 S3 envs + the Tanmatsu now build green on the 1.17 test pin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:15:08 +02:00
Kaj SchittecatandClaude Fable 5 e3c5c4237b core-117-prep: 1.17 core compile fixes across all targets (TEST PIN — not for main)
- UIColor migration: DisplayDriver::Color enum -> ColorVal/UIColor statics in the
  6 driver headers + 3 driver .cpps + all main.cpp/UITask.cpp call sites.
- ONE palette definition: src/helpers/ui/UIColorPalette.cpp. Envs whose
  DISPLAY_CLASS is a core driver (v4_tft, attaky, tdeck, M9) take the palette
  the core driver .o carries; envs on our drivers (r8, rak via src_filter;
  pagers via helpers/ui glob; both IDF apps via the whole-archive src glob)
  compile ours. Never both — same-name core objects collide (core LGFXDisplay).
- 1.17 ESP32Board.cpp includes <target.h>: hand the core lib scope the libdeps
  include paths it can't chain to (MicroNMEA everywhere, SensorLib on pagers).
- CustomLR1121Wrapper: setRxBoostedGainMode void->bool (1.17 base).
- IDF: fetch-deps.sh gains durable vendor-time patches (ESP32Board minimal
  deep-sleep — no target.h on IDF; SerialBLEInterface esp-nimble-cpp 2.x port
  incl. esp_mac.h). helpers/ethernet/ excluded in both meshcore CMakeLists.
- platformio.ini pins point at a LOCAL test tag (mc117-clean#core-v1.17.0-test);
  swap to the real core-v1.17.0 before merging to main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 16:13:09 +02:00
Pixel Perfect c027dd04cb fix(ble): drain NimBLE timers before teardown
Signed-off-by: Pixel Perfect <me@pixp.cc>
2026-08-05 17:56:10 -07:00
Pixel Perfect 5604bfa697 fix(tpager): use vendor TCXO voltage
Signed-off-by: Pixel Perfect <me@pixp.cc>
2026-08-01 20:33:03 -07:00
Kaj Schittecat 67b863d93e Merge branch 'main' of https://github.com/ALLFATHER-BV/wadamesh 2026-08-01 11:29:52 +02:00
Pixel Perfect 05f4ba6542 feat(touch): add T-Pager UI size presets
Signed-off-by: Pixel Perfect <me@pixp.cc>
2026-08-01 02:14:24 -07:00
Kaj SchittecatandClaude Opus 4.8 cdd2f21cbb touch: V4-R8 buzzer — wire the Expansion Kit V2's PAM8904 DIN (GPIO4)
The R8 env shipped HELTEC_V4_BUZZER_PIN=-1 because the Kit V2's PAM8904 wiring
was unconfirmed, so R8 users have never heard a notification. Heltec's
Expansion_board_V2.03.pdf schematic settles it: DIN = GPIO4, and EN1/EN2 are
hardware-strapped high (10K pull-ups, 3x boost) with DIP switch SW1 as a
hardware mute -- no enable code needed, the stock V4 tone() chime drives DIN
directly. GPIO4 is unclaimed on the R8 (board I2C moved to 17/18).

Tester-verify: chime on message. If still silent, SW1 on the expansion board
is the hardware mute -- check it is not switched to ground.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 09:59:03 +02:00
Tesso M Costa 14d5fdb8b3 touch: add "at a glance while locked" opt-in, fix glance multi-line body
New Options > Lock screen toggle lets the at-a-glance overlay fire while
the device is manually locked too, not just unlocked+idle-dimmed -- off
by default since it makes message text readable off a locked device.
Persisted via TouchPrefsStore (touchPrefsGetGlanceWhenLocked). When it
fires from a locked state, mirrors lockscreenReveal()'s peek path (panel
lit, _manual_lock left alone) rather than wakeScreen() (which would
silently clear the lock).

Also fixes the glance body label: LV_LABEL_LONG_DOT only wraps/dot-
ellipsizes across multiple lines when the label has a fixed height, so
left at auto-height it was effectively single-line everywhere, cropping
longer messages instead of continuing to a second line. Now sized to
the actual space available below it for the current board's resolution
and font.

Adds a T-Deck-only experiment shrinking the glance body font from 28px
to 20px (extras_lat_20 gate widened past Tanmatsu-only to cover it);
easy to revert in isolation per the comments at each site.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-30 09:12:05 -06:00
Attaky b1ad449147 attaky_mesh_series: board fixes - crystal clock, GPS acquisition, sensor addresses 2026-07-21 11:32:12 -07:00
Attaky b67f559d15 Attaky mesh series (#158)
* attaky_mesh_series: add board variant with FT6636 touch and landscape UI

* attaky_mesh_series: P3 battery (MAX17048) + GPS + shared-I2C mutex

- AttakySharedI2C: one Wire.begin() + a mutex serializing the shared board
  I2C bus (SDA=2/SCL=1) across the FT6636 touch poll (core 0), the MAX17048
  fuel gauge (core 1), and the future AW9523 keyboard. Arduino's TwoWire
  buffers are not concurrency-safe.
- board.begin() owns the authoritative bus init; the touch driver now takes
  the lock around every transaction instead of its own Wire.begin().
- getBattMilliVolts(): real MAX17048 VCELL read (78.125 uV/LSB -> mV = raw*5/64),
  2 s cache + lock, replacing the 4000 mV stub. Upstream maps mV -> % itself.
  Hardware-verified ~4058 mV.
- GPS: rename the dead GPS_BAUD flag to GPS_BAUD_RATE (the core reads that; 9600
  only worked via the #else default). Pins verified on hardware: ESP RX = IO17
  receives ATGM336H NMEA; MeshCore's module-centric PIN_GPS_TX=17 maps correctly.

* attaky_mesh_series: Control_Keyboard_(Bottom) dual-AW9523 keyboard

Add the Control_Keyboard_(Bottom)_1.0 module as an ADDITIVE hardware
keyboard on the touch board. Two AW9523 expanders (0x5A left / 0x5B
right) each scan a 5x5 matrix over the shared board I2C bus, serialized
through the AttakySharedI2C mutex alongside the FT6636 touch poll and the
MAX17048 fuel gauge.

The driver emits final bytes (printable ASCII, backspace, enter, space)
with momentary Shift resolved to uppercase in-driver. UITask injects them
into the focused textarea exactly like the web-mirror keystroke path, so
CAP_KEYBOARD stays 0 and the on-screen keyboard is not suppressed (touch
typing does not regress). Focus-gated: the matrices are only scanned
while an editable field is bound, so a key has zero side effect when
nothing is focused.

Hardware-verified, both halves: q/w/e/r/t (0x5A) + y (0x5B), Shift
uppercase, backspace, space, enter; on-screen keyboard intact.

* attaky_mesh_series: use reliable 115200 upload speed

* attaky_mesh_series: use core default GPS baud

* attaky_mesh_series: restore GPS_BAUD setting

* attaky_mesh_series: rename PlatformIO environment
2026-07-19 13:58:05 +02:00
Kaj Schittecat be0a49255c Merge PR #149: LilyGo T-LoRa Pager port (LR1121 + SX1262) by @codemonkeybr
Full keyboard+encoder-driven board port: TCA8418 QWERTY matrix, rotary encoder
focus-nav, ST7796 display, ES8311 notification sound, both radio variants.
Hardware-verified by the author against a live mesh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

# Conflicts:
#	src/ui-touch/UITask.cpp
2026-07-15 19:16:51 +02:00
Kaj SchittecatandClaude Opus 4.8 2353c56adc p4: T-Display P4 port — web VNC/remote, time-sync hardening, brightness + sound
LilyGo T-Display P4 (ESP32-P4 + factory ESP-AT C6) reaches POC: LoRa mesh, Wi-Fi
companion (TCP:5000), GPS, SD, battery gauge, chat persistence, and now:

- Web VNC/REMOTE/terminal via a first-byte router on the single ESP-AT listener
  (HTTP verbs -> WS server, '<' frames -> phone companion; both live at once).
  C6Server gains a _begun gate so never-listening instances stay inert.
- VNC freeze fixed: bounded one-CIPSEND-chunk drains under the WS client mutex
  (a whole-band blocking write starved the main loop), fast-drop of stalled
  mirror clients, SEND-OK wait 8s->4s.
- Wi-Fi robustness: CWJAP? miss-streak tolerance, CIPSERVER liveness verify +
  re-arm (a re-association silently killed the listener until reboot).
- Time sync: HTTP-Date fallback when SNTP is blocked; ClockFloorRTC gains a
  MAX_PLAUSIBLE_EPOCH guard (all boards) after a garbage-future hardware RTC
  read (2043) latched the ratchet.
- Brightness: RM69A10 DCS 0x51 driven live (CC slider + persisted pref).
- Sound: ES8311 + NS4150B notification chimes (P4Audio, esp-bsp-derived
  register sequence on Wire1), full HAS_UI_SOUND surface + CC Screen/Sound
  sliders; ccVolumeReleaseCb's hardcoded tanBeep -> uiSoundPreview.
- tdisplay_p4/: IDF project (build.sh, c6_at AT-over-SDIO driver, sdkconfigs,
  fetch-deps.sh vendoring, .gitignore for local payloads).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:13:23 +02:00
Tesso M Costa 5c97d02fbe Merge remote-tracking branch 'origin/main' into tlora-pager-port-lr1121
Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>

# Conflicts:
#	NOTICE
#	platformio.ini
#	src/ui-touch/UITask.cpp
#	src/ui-touch/device_caps.h
2026-07-14 10:45:53 -06:00
Kaj SchittecatandClaude Opus 4.8 687eb3865c touch: add Heltec WiFi LoRa 32 V4-R8 board (8 MB octal PSRAM + Expansion Kit V2 SD)
The V4-R8 uses an ESP32-S3R8 (8 MB OCTAL PSRAM) which claims GPIO33-37, so
Heltec moved every control signal off them and the Expansion Kit V2 rebuilt the
display/touch/SD block onto a hardware-SPI bus:
  Vext 36->40 (active-LOW) . GPS_EN 34->42 . LED 35->46 . ADC_Ctrl removed
  TFT (LovyanGFX HW-SPI): MOSI=15 SCK=16 MISO=45 CS=47 DC=48 RST=21 BL=44
  touch (CHSC6x, shares board I2C 17/18) . micro-SD (shared TFT SPI, CS=3)

The build defines HELTEC_LORA_V4_TFT too, so it reuses all the V4 touch-UI code;
HELTEC_LORA_V4_R8 gates only the deltas. 8 MB enables the on-device web browser
(CAP_WEB_BROWSER) and the SD card (CAP_SD). Also wires the board into the release
+ web-flasher pipeline (release.sh, gen-flasher-meta.py, the flasher picker).

UNTESTED — no V4-R8 hardware yet. Pin sources: Meshtastic heltec_v4_r8 variant,
Heltec V4-R8 pinmap, and the Expansion_board_V2.03 schematic. All three touch
envs build clean; V4/T-Deck unregressed. On-device TODO: CHSC6x shared-Wire on
17/18, SD_CS=3, and the V2 PAM8904 buzzer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 11:27:40 +02:00