Commit Graph
108 Commits
Author SHA1 Message Date
Pratik Patel 8dbb349fc6 thinknode_m9: pick GPS enable polarity from the board revision
The GPS enable pin (GPIO11) is active-LOW on the original ThinkNode-M9 but
active-HIGH on the newer revision (keyboard controller at 0x6D, e.g. Elecrow's
Meshtastic SKU LMM15409D2). Driven LOW on those units the GPS never gets a
fix: on one unit, same spot, 0 satellites with EN active-LOW vs 15 with
active-HIGH, and 34 after 20 minutes outdoors with the GPS toggled off and on
again from the UI. Meshtastic's ELECROW-ThinkNode-M9 variant makes the same
split, keyed on the same keyboard address (m9Version > 1 ? HIGH : LOW), and
Elecrow engineers have confirmed the split.

WadaNmeaLocationProvider: keep the enable level in a member that defaults to
the compile-time GPS_EN_ACTIVE, add setEnActive() to override it at runtime
(re-driving the pin so a running GPS stays on), and an optional
setPostEnableHook() run at the end of begin(). Every other board is unaffected.

thinknode_m9: m9KeyboardAddr() and a one-shot found-callback on the keyboard
driver; target.cpp uses it, when the controller is at 0x6D, to call
gps.setEnActive(true) and to register a GPIO10 wake pulse (50 ms, HIGH 5 ms,
LOW, 50 ms) after enabling, as Elecrow engineers confirmed. Any other address
keeps the existing default (LOW, no pulse), so the 0x6C path is unchanged; it
is not tested here, no 0x6C unit was available.

Depends on the keyboard 0x6D probe change.

Signed-off-by: Pratik Patel <Pratik@MakerNova.io>
2026-10-13 08:22:55 +02:00
bfindlay 04d8a606c5 no-mistakes(document): Consolidate M9 keyboard address documentation 2026-10-13 14:59:30 +11:00
bfindlay ad13d42a12 Fix M9 keyboard detection at the V1.1 I2C address
Signed-off-by: bfindlay <bfindlay@acm.org>
2026-10-13 14:43:28 +11: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 b52c71f4a5 p4: stop retrying a C6 that is never coming back
7ce1b3f turned off CONFIG_ESP_HOSTED_TRANSPORT_RESTART_ON_FAILURE so a wrong-
firmware C6 could not reboot the board. It stopped the boot loop and I called
it fixed on a 45 s capture. That was the wrong test. Disabling the restart did
not stop esp-hosted, it only removed its exit: sdio_read_task posts
ESP_HOSTED_EVENT_TRANSPORT_FAILURE and then `continue`s straight back into a
register read that is going to fail again. Measured on the same board:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Also here, both needed before this can ship:

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

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

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 10:03:19 +02:00
Michael A. 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. CojocariandClaude Opus 5.5 ad91b1cb3f feat(tdisplay-p4): clip-on keyboard expansion, plus keyboard and layout fixes
The T-Display P4 keyboard expansion (TCA8418 + its own XL9555, ported from
camillia-mt) now works. It is looked for every 1.5 s, so it can be clipped on
or off while running, and is routed like the CardKB and Bluetooth keyboards:
it types into the focused field and keeps the on-screen keyboard down. The
external-keyboard routing was gated on Bluetooth, which the P4 lacks, so it
now follows a new CAP_EXT_KEYBOARD; the Bluetooth-only parts stay on
CAP_BLE_KEYBOARD.

- Keys: Shift/Sym/Alt one-shot, Caps Lock lights the expansion LEDs, arrows
  and Backspace repeat, Alt+Backspace backs out, F1-F5 open the tabs left to
  right, and F11 steps the keyboard light through off/low/medium/high (saved
  across reboots).
- Wire1 is shared with the ES8311 codec on other pins; P4I2c1 switches it
  under a lock, and the codec's lazy bring-up now goes through it.
- With the keyboard attached the tab bar labels F1-F5 beside each icon, and
  Enter on a chat bubble opens its action menu.

Also, on any keyboard build, Enter runs the command in the terminal and the
repeater admin console (the admin console's Enter hook sat on its Send button,
where nothing raised it). And on the P4: the status-bar name shrinks to fit the
bar, the Discover buttons are spaced out, contact names clear their icon in
landscape, and leaving an app returns the focus to its tile in the app drawer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 20:10:12 -04:00
Michael A. Cojocari 500b24e38c feat(tdisplay-p4): support LR2021 radio variants (#504) 2026-09-26 21:59:58 -04:00
Claude e1f6c75091 docs(tdisplay-p4): full-reset restarts, landscape touch and the landscape Cmdr layout
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FbY4FsGuX2fNDpNttB6a7R
2026-09-25 01:28:50 +00:00
Claude 6c6600add1 Merge remote-tracking branch 'origin/main' into fix/touch-ui-followups
# Conflicts:
#	variants/tdisplay_p4/target.cpp
2026-09-25 01:17:45 +00:00
Claude 1501394fe5 T-Display P4: a settings reboot no longer leaves the screen off
After a setting that reboots (theme, rotation, language, remote mode...) the
P4 came back with the panel dark, and only a second reboot brought it back.

esp_restart() on the ESP32-P4 is a CPU reset: esp_system_reset_modules_on_exit()
resets DMA, UARTs, SDMMC and crypto, but not the MIPI-DSI host or its DPI path.
The display driver then initialises over a host still half-configured from the
previous session and the panel stays dark. A full chip reset always recovers it.

So every software restart now becomes a full system reset: a shutdown handler,
installed first thing in app_main so it runs last, arms the RTC watchdog with
RESET_SYSTEM. Being a shutdown handler it covers rebootDevice(), OTA, the
console and factory reset without touching any of them. Same fix as
camillia-mt's T-Display P4 port.

Those restarts now arrive as ESP_RST_WDT, so an RTC_NOINIT marker records that
the restart was deliberate, and the crash-report prompt and Settings -> About
read the reset reason through bootResetReason(), which reports ESP_RST_SW for
them. Otherwise a lingering coredump would re-prompt "restarted after a crash"
on every settings reboot.

Not built here (no ESP-IDF toolchain in this environment); needs a P4 build and
an on-device check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FbY4FsGuX2fNDpNttB6a7R
2026-09-25 01:08:59 +00:00
Michael A. CojocariandClaude Opus 5.5 1c3576e1d6 fix(tdisplay-p4): GPS the camillia-mt way, 9600 start with baud probing
camillia-mt's working T-Display P4 GPS wakes the L76K through the XL9535
before opening UART1 on RX 22 / TX 23, starts at 9600 and probes other
rates until valid NMEA arrives. Wake and pins already match here; the
difference was the rate. The P4 used the core MicroNMEALocationProvider
locked to one rate (115200), so a receiver at any other rate gave an
open but silent UART.

Build the P4's provider as WadaNmeaLocationProvider with UART probing
(already on main via PR #517, used by the T-Deck Pro), starting at 9600:
it walks 9600/19200/38400/57600/115200 until a checksummed sentence
arrives and saves the working rate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:28:50 -04:00
Michael A. CojocariandClaude Opus 5.5 b60c26ac85 feat(tdisplay-p4): esp-hosted C6 backend for Wi-Fi
Current T-Display P4 V1 units ship the C6 with esp-hosted firmware
(2.12.3), and main's P4 build only spoke ESP-AT, so Wi-Fi could not come
up on them at all. This is a one-time import of the esp-hosted backend

- TDP4_C6_HOSTED build switch (default hosted; WADA_P4_LEGACY_AT=1 keeps
  the ESP-AT path for older units), with the ESP-AT socket/WiFi facades
  and the single-listener TCP router guarded to the legacy build.
- tdisplay_p4: build.sh hosted patches and fullclean handling,
  idf_component.yml esp-hosted pin, sdkconfig, main.cpp hosted bring-up
  and SNTP, c6_at updates, and the C6 reset hook in target.cpp.
- P4 Wi-Fi scan wait in UITask.
- TDISPLAY_P4_C6_FIRMWARE_PLAN.md.

Not imported from that commit: UI-scale default, GPS log lines, UI

build-upload-monitor.sh: run build.sh's own fullclean for the P4 (idf.py
fullclean rejects the patched managed components).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:28:50 -04:00
Michael A. CojocariandClaude Opus 5.5 4d86f66c39 fix(tdisplay-p4): draw the Remote UI placeholder on the physical panel
In Remote UI mode the UI renders off-screen for the browser and the
physical panel shows a status placeholder drawn through the
DisplayDriver API. On the P4 every fill/text call was an empty stub, and
writePixelsRGB565 takes half-resolution LVGL coordinates and upscales
2x, so only the logo appeared, twice as big and pushed towards the top
right, over whatever was on screen before.

Add P4PanelPainter: native-resolution fills, rectangles and text for
both P4 panels (RM69A10 AMOLED, HI8561 LCD), rasterising LVGL's
Montserrat fonts at the UI's 2x scale, written through a new
non-upscaling writeNativeRGB565. Give the placeholder a layout for the
tall portrait panel: logo, title, browser address or Wi-Fi status, and
the touch-and-hold exit hint, matching the other builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:28:04 -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
Michael A. Cojocari 6af981fe57 fix(tdeck-pro): restore setup input on 4G model
Keep keyboard navigation available through first-run setup and latch CST3530 touch interrupts on T-Deck Pro as well as Max.
2026-09-22 19:28:47 -04: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 e796984ad4 fix(heltec-v4): improve expansion kit usability 2026-09-17 11:55:24 -04:00
Michael A. Cojocari 4b7882ca44 fix(tdeck-pro): harden touch and space holds 2026-09-17 10:34:18 -04:00
Michael A. Cojocari 9542756f91 fix(pager): recover SD after host use 2026-09-16 19:45:27 -04:00
Michael A. Cojocari cfb09edcf1 docs: record T-Deck Pro test status 2026-09-08 16:57:00 -04:00
Michael A. Cojocari 9715cac742 Work 2026-09-08 16:31:52 -04: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
Michael A. Cojocari 28fb57ef4c Fix Attaky usability issues (#423) 2026-09-06 16:55:41 -04:00
Michael A. Cojocari d4ed6df92e Work 2026-09-05 15:16:56 -04:00
Michael A. Cojocari 3749df2907 Work 2026-09-05 12:23:54 -04:00
Michael A. Cojocari 420e13cafb Improve M9 message actions and unlock gesture (#402)
Open the selected message action menu with a normal Enter press. Replace the lock-screen long wait with a d-pad-center double press while preserving directional behavior and the controller long-press fallback.

Signed-off-by: Michael A. Cojocari <michael.cojocari@gmail.com>
2026-09-04 14:00:17 -04:00
Kaj SchittecatandClaude Opus 5 17ce1ae018 DS3231 real-time clock support on the T-Deck (#378)
jrote1 is fitting a battery-backed DS3231 to the T-Deck's I2C bus so the device
keeps time across a full power-off without needing GPS or Wi-Fi. The RTC adapter
already had the hard parts, refusing an untrustworthy read and revalidating every
write, so this teaches it a third chip rather than adding a second path.

Three things differ from the soldered PCF parts and all three are handled by
normalising into the existing decoder rather than duplicating its rules:

- It lives at 0x68 rather than 0x51. That is also what makes it safe to add: it
  cannot be mistaken for either chip an existing board carries.
- Day-of-week and day-of-month are swapped in its calendar block.
- It reports "my time is not trustworthy" as OSF in a status register instead of
  in bit 7 of seconds, so that bit is folded into the one the decoder already
  refuses on. A module whose backup cell went flat is therefore rejected the
  same way a powered-off M9 is, rather than having its frozen calendar believed.

0x68 is a busy address, so identification requires a positive DS3231 trait
rather than an ACK: its temperature register pair, whose low byte is a quarter
degree in the top two bits and zero elsewhere, with a high byte in a range a
working chip could be at. A module that has never been set by us can also arrive
in 12-hour mode, which is converted rather than decoded as if it were 24-hour.

Transparent when no module is fitted, which is nearly every T-Deck: reads come
from the software clock, writes stop there, and the probe logs that nothing
answered. CAP_HARDWARE_RTC stays 0 for the board, since this is an aftermarket
addition rather than something a T-Deck has, so the cold-boot network time sync
stays on offer for everyone else.

Unverified on hardware: neither of us has the module wired yet. The host tests
for the shared decoder still pass.

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

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 01:24:59 +02: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
Kaj Schittecat 3a23d65b88 Merge PR #386 2026-09-03 23:08:48 +02:00
Michael A. Cojocari 19d557e70a Work 2026-09-03 12:52:03 -04:00
Michael A. Cojocari 092528a8fb Fix RTC retention and cold-boot time sync (#383) 2026-09-02 21:37:35 -04:00
Michael A. Cojocari 7cf9621fec Merge remote-tracking branch 'upstream/main' into square 2026-09-02 21:08:41 -04:00
Christopher Van HooseandClaude Fable 5 d59ce2c634 M9 battery pass: reachable saver, GPS-off leakage, mid-session UART ring
Three battery fixes plus the record in M9_PORT.md:

- The touchSleep battery saver was unreachable on the M9: the settings
  switch and its callback were compiled only for the T-Deck and V4-R8,
  and the pref defaults OFF with no other setter. Both #ifs now include
  HAS_THINKNODE_M9 (the M9 is in batteryIsCharging's #else branch, so
  the "USB powered" gate can never wrongly block there).

- WadaNmeaLocationProvider parked GPS_RESET asserted while the GPS was
  stopped, per the core's convention. On the M9 that polarity is
  inverted through R46 into NPN Q16, so "asserted" sourced base current
  the entire time the GPS was OFF. Park the line LOW instead — the
  zero-current level for both circuit styles; every start path still
  pulses reset() explicitly, so the clean-reset guarantee holds.

- A mid-session GPS enable ran the whole session on the stock 256 B
  Serial1 RX ring (the 4 KB TTFF ring is boot-gated on the persisted
  pref, and setRxBufferSize is a no-op on a running UART) — and with
  the saver newly reachable, one 50 ms park overflows that ring at the
  M9's 115200 baud. gpsEnsureBigRxRing() cycles the UART to 4 KB from
  both "gps on" funnels (toggleGPS, CMD_SET_CUSTOM_VAR) right after a
  successful start, so a refused enable never spends the DRAM.

Also corrects both stale "keyboard S2 on the switched rail" comments
(schematic truth: always-on 3V3 via R3 — it decides the deep-sleep
drain budget). All 8 envs compile; on-device VERIFY list in M9_PORT.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MYDmpc53KpdmTLKzUNsX2s
2026-09-02 15:32:27 -04:00
Kaj Schittecat 8f9e9993c8 Merge PR #370 2026-09-01 10:49:15 +02:00
Michael A. Cojocari f70a491480 Work 2026-08-30 14:42:46 -04:00
Michael A. Cojocari de778ff09d Working on fantastical square. 2026-08-29 14:42:49 -04:00
Christopher Van Hoose 8fbbac3b66 M9_PORT.md: record audit pass 3
Cold map open, Back navigation, the SD map-pack deletion, the label overlaps
and the measurements behind them - including the correction to the earlier
"the remaining cold-open cost is the SJPG decode" note, which was reasoning
about the two smaller halves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Christopher Van Hoose <cvhvisuals@icloud.com>
(cherry picked from commit 68fe4dbc09f450cc1e1545573cf9c53658bcf840)
2026-08-28 20:37:17 -04:00
Michael A. Cojocari 107367bf82 Merge remote-tracking branch 'upstream/main' into attaky 2026-08-27 20:41:39 -04:00
Michael A. Cojocari fe6741b26d Work 2026-08-27 20:37:59 -04:00
Michael A. Cojocari 5893c44e98 Work 2026-08-27 20:01:12 -04:00
Michael A. Cojocari ed29da087a Work 2026-08-27 13:53:55 -04:00
alphalord336 30bbefc066 Merge branch 'ALLFATHER-BV:main' into main 2026-08-27 18:17:41 +02:00
alphalord336 3dee9d62e7 Update battery voltage calculation formula 2026-08-24 19:49:47 +02:00
Michael A. Cojocari acdcc6e7fc Work 2026-08-23 20:32:26 -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
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 5a4a5a8ab6 Park the magnetometer when idle, and stop Lua apps ticking into a dark screen
Two answers to "does the compass app matter for battery life", both yes-ish
and both now fixed.

The driver put the QMC6309 into normal mode at boot and left it there, so it
converted continuously from power-on whether or not anything read it: ~1 mA at
100 Hz / OSR 8, forever, on a 2300 mAh battery. It is now parked in suspend
after configuration and woken on demand, with m9CompassIdleTick() (called from
the M9's existing per-loop branch) suspending it again two seconds after the
last read. Waking is a single register write since suspend preserves the
configuration; the waking call reports "nothing fresh" and the caller's next
poll gets data, which callers already handle because the chip may be absent.

Separately, lv_timer_handler() runs unconditionally, so a Lua app's timer kept
firing while the display slept -- GPS Compass polled the magnetometer at 10 Hz
into a dark screen, which would have held the sensor awake even after the fix
above. The host now skips the tick while the screen is off and resumes on wake;
dt comes from millis(), so an app sees one long frame rather than a broken
clock. Documented on the SDK page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 09:01:19 -04:00