1554 Commits
Author SHA1 Message Date
Thomas Göttgens e0cf782131 Build xiao_nrf54l15_lr2021 on every PR as the nrf54l15 canary and mark it community supported (#11856) 2026-09-15 13:00:34 +02:00
Ethac.chen 2fe711115e feat(nrf52): add RAK3401 + LR2021 (RAK13700) variant (#11819)
* feat(nrf52): add RAK3401 + LR2021 (RAK13700) variant
WisBlock core with IO-slot LR2021: DIO RF switch, board LF PA table, and 1.6 V TCXO. Keep extra/unsupported until it has its own hw_model.

* fix(lr2021): log custom PA setOutputPower in fullBegin
Match init(): a calibration miss stays a warning so band-hop keeps the begin() PA config.

* refactor(lr2021): share custom LF PA table helper

init() and fullBegin() both re-install the board table after begin(); keep the warn-only setOutputPower miss.
2026-09-15 08:15:33 +00:00
Thomas Göttgensandvidplace7 ea7d4aa410 Port nRF54L15 to the s145 SoftDevice Arduino core (#11842)
* Remove the Zephyr based nRF54L15 port

* Add nRF54L15 port on the s145 Arduino core: nrf54l15dk and xiao_nrf54l15 variants

* nRF54L: errno-style nrfx results, flush console before assert reset

* nRF54L: log the SoftDevice status on Bluefruit failure, ignore the seed request event

* Support the Wio-LR2021 LoRa Plus expansion board with OLED and K1 on the XIAO nRF54L15 variant

* Consume the nRF54L15 platform, core and bootloader from their repositories

* Pin the nRF54L15 platform to v0.2.0

* nRF52: forward SoftDevice flash events taken by the main loop to the flash driver, log the pairing failure status

* Pin the nRF54L15 platform to v0.2.1

* Pin the nRF54L15 platform to v0.3.0

* Split the XIAO nRF54L15 variant into SX1262 and LoRa Plus environments, seed the SoftDevice on request, add the nrf54l15 CI build script

* Pin the nRF54L15 platform to meshtastic/platform-nordicnrf54 v0.3.1

* NRF54: Fix mtjson generation

---------

Co-authored-by: vidplace7 <vidplace7@gmail.com>
2026-09-15 07:16:33 +00:00
Austin 9d27b276aa NRF54: Update to toolchain-gccarmnoneeabi@1.90301.200702, align NRF52840 (#11850)
Version 1.90201.191206 supports arm64 MacOS, but not arm64 Linux (1.90301.200702 supports both)

Also change the fuzzy match for gccarmnoneeabi to an exact match for nRF52840 (for reproducible builds), this is effectively a no-op change, today.
2026-09-14 14:00:55 -04:00
Austin 2826c6712b Pin nrf54 platform, update toolchain-gccarmnoneeabi for arm64 build hosts (#11848) 2026-09-14 12:19:56 -04:00
Tadayoshi MIURA f5158f50be feat(rp2040/rp2350): Update earlephilhower/arduino-pico to 6.1.0, bump maxgerhardt/platform-raspberrypi to latest (#11814)
* Update earlephilhower/arduino-pico to 6.1.0

* feat(rp2040/rp2350) bump platform-raspberrypi

* fix(rp2040/rp2350) recover printf and scanf
2026-09-14 13:02:57 +00:00
Manuel 5c113c730b feat: add exFat support for SDIO SD cards (#11805)
* exFat for SDIO

* update device-ui commt
2026-09-10 15:08:33 +00:00
Thomas Göttgens 546b678d50 fix(motion): drive screen wake from the accelerometer interrupt (#11758)
* fix(motion): drive screen wake from the accelerometer interrupt

The BHI260AP ISR body was empty and BHI_IRQ was never set or read, so the
attach only consumed a GPIO slot. ICM20948 could not reach its interrupt
path at all: the ICM_20948_INT_PIN fallback in the header is guarded on
ICM_20948_WOM_THRESHOLD, which the block above it always defines, so the
pin was never defined and the config, attach and interrupt-driven
runOnce() were dropped by the preprocessor on every board.

BHI260AP now configures the FIFO interrupt, attaches an ISR that sets a
flag, and enables the wrist tilt gesture so runOnce() can call
wakeScreen(). BMA423 arms INT1 push-pull active-high, which the BMA4
reset default leaves disabled, and drains on the interrupt instead of
every 50 ms. Both keep a slow keepalive drain so a pin that never
asserts degrades to polling rather than losing tilt and tap wake.

MOTION_WAKE_INT_PIN resolves whichever motion interrupt a variant
declares. doLightSleep() arms it as a GPIO wake source and lsIdle()
attributes the resulting wake to motion, which it previously charged to
BUTTON_PIN and dropped. Both are gated on
config.display.wake_on_tap_or_motion, matching MotionSensor::wakeScreen().

Closes #11755

* fix(motion): use the ICM20948 interrupt without dropping the compass

The ICM_20948_INT_PIN build of runOnce() was a full replacement for the
polled one and kept only wake-on-motion, so defining the pin would have
dropped the magnetometer fusion that feeds screen->setHeading(), the
calibration flow and the IMU sleep handling. providesHeading() returns
true for this part, so that is the compass.

Merge the two: the pin now selects the wake-on-motion mechanism only.
The status register poll stays compiled in behind a keepalive, since no
shipped firmware has exercised this line, so a pin that never asserts
costs latency rather than wake-on-motion.

Declare the pin on t-echo-card. Sensor_INT is P1.13, open drain with a
10K pullup to VDD3V3, matching the driver's active-low config and
FALLING attach. The schematic's SCL P1.02 / SDA P1.04 match PIN_WIRE_SCL
and PIN_WIRE_SDA.

* Revert the t-echo-card ICM20948 interrupt pin

Sensor_INT is not the IMU. In both T-Echo-Lite_V1.0 and
T-Echo-Lite-Card_V1.0 it appears only on the unannotated 5-pin expansion
header (P?, 5PIN_PA1.0) carrying SDA_P1.04, SCL_P1.02, VDD3V3, GND and
Sensor_INT with its 10K pullup, and it leaves the sheet as an off-sheet
port. Neither schematic contains an ICM20948 symbol at all, and the
vendor pin map declares only ICM20948_SDA, ICM20948_SCL and
ICM20948_ADDRESS for the part.

The interrupt belongs to whatever plugs into that header, so the onboard
IMU has no reason to drive it. The driver keeps polling.

* Poll until an ICM20948 interrupt pin proves itself

A variant that declares ICM_20948_INT_PIN is asserting routing no vendor
firmware has ever exercised, so treat the line as unproven: keep polling
the wake-on-motion status register at full rate, and only back off to the
keepalive once the pin has actually fired. A wrong pin then behaves
exactly as before rather than trading wake latency for the guess.

* feat(t-impulse-plus): drive ICM20948 wake-on-motion from its INT pin

The LilyGO pinmap documents the IMU's INT on P0.07, and variant.cpp
already maps and names it as D27, but the pin was never handed to the
driver, so wake-on-motion polled the status register every 50 ms.

Use the D number: pinMode() and attachInterrupt() index
g_ADigitalPinMap, where a raw 7 selects P1.13, the LoRa RF_VC1 TXEN
line. The driver polls until the pin proves itself, so an ICM20948 that
turns out not to drive it keeps working as before.

* Derive MOTION_WAKE_INT_PIN after the build exclusions

MESHTASTIC_MINIMIZE_BUILD defines MESHTASTIC_EXCLUDE_I2C further down the
file, so the guard read as unset and a minimized build defined the pin
anyway. doLightSleep() would then arm a GPIO no motion driver configures,
since every driver is compiled out with I2C.

Latent rather than live: nothing sets MESHTASTIC_MINIMIZE_BUILD today,
and the variants that pass -DMESHTASTIC_EXCLUDE_I2C were already correct
because a build flag is defined before this file is parsed.

* fix(motion): keep the BMA423 INT1 config failure non-fatal

Restores the resolution made when feature/sensorlib-0.4.1 was merged into
this branch. That merge is gone after the rebase, and neither parent
carried this: the interrupt path is an optimisation over the existing
poll, so a pin-config failure should log and fall back rather than be
ignored outright.
2026-09-10 11:50:37 +00:00
renovate[bot] 06f2111784 chore(deps): update rak13800-w5100s to v1.0.4 (#11783)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-10 11:04:28 +00:00
Manuel 27afe1159b fix vbus detection (#11801) 2026-09-10 11:02:58 +00:00
103463e26d fix(esp32): identify LilyGo T5 S3 ePaper Pro targets (#11368)
* fix(esp32): identify LilyGo T5 S3 ePaper Pro targets

* fix(esp32): mark T5 S3 ePaper Pro targets actively supported

---------

Co-authored-by: George <509474+giannoug@users.noreply.github.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: rcarteraz <robert.l.carter2@gmail.com>
2026-09-10 06:42:39 -07:00
Tom 777c79f6d8 revert a conflict regression and utilise the full power of the lr2021 lna (#10633)
* revert a conflict regression and introduce the DCDC workaround from semtech example code.

* fix: Adjust DCDC workaround placement for

* clod fixes stuff

* clod fixes some more things
2026-09-10 10:35:33 +00:00
Benjamin FaershteinandManuel 2c595e935d feat(native): add macOS MUI simulator target (#11739)
Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>
2026-09-09 20:50:46 +00:00
Thomas Göttgens fa81b47ecb refactor(sensorlib): unify on 0.4.1 and move the PCF RTCs to PCF8xRTC (#11754)
* chore(deps): unify SensorLib on 0.4.1 and port the 0.4.x API changes

The 19 SensorLib declarations were split across 0.3.1, 0.3.4 and 0.4.1.
Pin all of them to 0.4.1 and fix the renovate datasource on ThinkNode-M9
(custom -> custom.pio).

API changes in 0.4.x:

- BMA423Sensor: the configAccelerometer/enableFeature/readIrqStatus
  surface is gone. SensorBMA423 now derives from SensorBMA4XX and
  dispatches tilt and tap through callbacks driven by update().
- BHI260APSensor: SensorRemap is a scoped enum in sensor/SensorDefs.hpp,
  and BoschSensorInfo members are protected, so read them through the
  accessors.
- ExtensionIOXL9555 is renamed to IoExpanderXL9555 and the touch drivers
  moved under touch/. Point the includes at the current paths instead of
  the compatibility shims, which emit warnings on every build.

Drop four lewisxhe/PCF8563_Library declarations. Nothing in the tree
includes pcf8563.h; all RTC code goes through SensorLib.

Drop the BMA423_INT block. No variant defines BMA423_INT (t-watch-s3
defines BMA4XX_INT), so it has never been compiled. Interrupt-driven
wake on BMA423 is unimplemented rather than regressed by this change.

Drop the T_WATCH_S3 branch in BHI260APSensor. That file requires
HAS_BHI260AP, which T_WATCH_S3 does not define.

SensorQMC6309.hpp does not exist in 0.3.4, so src/motion/QMC6309Sensor.cpp
compiles for the first time on 0.4.1.

* fix(motion): fail BMA423 init when the sensor rejects its configuration

configAccelerometer, enableTiltDetector and enableTapDetector return false
only on an I2C or driver-level failure, so treat them the way QMC6309Sensor
treats configMagnetometer rather than initializing a sensor that never took
its settings.

Trim the t-watch-ultra placement comment to the two lines the coding
guidelines allow.

* refactor(rtc): drive the PCF clocks from PCF8xRTC instead of SensorLib

SensorLib reaches its PCF8563 and PCF85063 drivers through a comm layer
spanning Arduino, ESP-IDF, SPI and custom callbacks, which is a lot of code
to link for four calls on an I2C RTC. Measured against develop, the boards
that pull SensorLib grew about 10 KB moving from 0.3.4 to 0.4.1, while the
nRF52 boards that do not pull it moved by 100-300 bytes.

meshtastic/PCF8xRTC covers both parts in one class over Adafruit BusIO,
which every board with a PCF part already links. Ten boards used SensorLib
for nothing but the RTC and now drop it entirely; the remaining seven keep
it for a BMA423, BHI260AP, QMI8658, XL9555 or touch controller and take the
new driver for their RTC.

Behaviour changes with it. Both parts latch an oscillator-stop flag on power
loss, which the old path ignored: readFromRTC() now refuses a calendar the
chip has marked invalid rather than feeding a plausible wrong date to
BUILD_EPOCH, the result of begin() is checked, and a failed set is logged.

The isBitSet workaround moves from configuration.h to MMC5983MASensor.h.
It worked only because configuration.h pulled SensorLib.h in first, so the
later include was a no-op and the macro stayed undefined; with the global
include gone it has to sit where SensorLib and the SparkFun header actually
meet.

* fix(rtc): report a missing PCF chip separately from a stopped oscillator

lostPower() reads a register, so it also returns true when the chip cannot be
reached at all. Folding it into one warning meant a failed begin() reported
"oscillator stopped", which is a different fault.

* fix(t5s3): read GT911 touches through getTouchPoints

0.4.x dropped the default argument from getPoint(x, y, count) and marked
it deprecated, so the two-argument call no longer resolves:

  variant.cpp:618:27: error: no matching function for call to
  'TouchDrvGT911::getPoint(int16_t*, int16_t*)'

Use getTouchPoints(), which is what the deprecation points at, rather than
passing the count to a call that is on its way out.
2026-09-09 16:25:49 +00:00
Thomas Göttgens cccefa09a4 fix(rp2xx0): match the real library name when ignoring iLabs_Hearth (#11761)
lib_ignore matches the name from the library manifest, and iLabs_Hearth
declares "iLabs Hearth" with a space. The directory name used in #11757
therefore never matched, and the Pico builds still compile the library and
still fail on its ESP_SERIAL_PORT #error.
2026-09-07 16:02:30 +02:00
Austin fd623f64fd Revert "fix(ci): pin tool-scons to 4.8.1 for ESP targets (#11756)" (#11763)
This reverts commit a8912b1eb5.
2026-09-07 10:02:04 -04:00
Austin 7b8a02bb4c Revert "fix(ci): build with pioarduino core instead of upstream platformio (#…" (#11760)
This reverts commit f631428309.
2026-09-07 09:23:15 -04:00
Thomas Göttgens f631428309 fix(ci): build with pioarduino core instead of upstream platformio (#11759)
* fix(ci): build with pioarduino core instead of upstream platformio

The espressif32 platform is the pioarduino fork, but setup-base installed
upstream platformio and then ran pio upgrade, so every firmware build ran
a core the platform is not built against.

Upstream 6.2.0, released 2026-09-05, moved its tool-scons core dependency
to ~4.41101.0 (SCons 4.11.1). The lazy "import SCons.Tool.FortranCommon"
that smart_link() uses to choose a linker fails there, so every ESP target
died at link-action resolution before compiling a file. Core resolves
tool-scons as a core dependency, so a platform_packages pin cannot help:
core installs its own version and removes the pinned one.

pioarduino core pins SCons 4.8.1 by URL rather than by range, so upstream
releases cannot reach it. Dropping pio upgrade as well, since it re-pulled
the latest upstream core regardless of what pip installed.

Only setup-base changes. The matrix-generation jobs install platformio to
parse the ini files and never build firmware, and the native test suites
pass on upstream core because that platform does not reach smart_link.

* Revert "fix(ci): pin tool-scons to 4.8.1 for ESP targets (#11756)"

This reverts commit a8912b1eb.

The pin never took effect. PlatformIO Core resolves tool-scons as a core
dependency, so it installs its own version and removes the pinned one:

  Installing platformio/tool-scons @ 4.40801.0
  Installing platformio/tool-scons @ ~4.41101.0
  Removing tool-scons @ 4.40801.0

Switching to pioarduino core in the preceding commit fixes this properly,
and leaving a platform_packages entry that fights a core dependency would
only be misleading.
2026-09-07 12:24:12 +02:00
Thomas Göttgens b46aec31f4 fix(rp2xx0): ignore iLabs_Hearth so Pico targets build again (#11757)
arduino-pico gained a bundled iLabs_Hearth library that supplies its own
Preferences.h. NodeDB.cpp includes <Preferences.h> inside an ARCH_ESP32
guard, and the library dependency finder runs in chain mode, which matches
include directives without evaluating the preprocessor. It therefore pulls
the library into every Pico build, and Hearth.cpp refuses to compile on a
board that does not define ESP_SERIAL_PORT:

  iLabs_Hearth/src/Hearth.cpp:121:2: error: #error "iLabs Hearth requires a
  board variant that defines ESP_SERIAL_PORT ..."

The library only reaches the build because the platform resolves
framework-arduinopico from an arduino-pico master commit; the
platform_packages entry here pins the name arduino-pico, which is a
different package and does not override it.

Ignoring the library is enough, since no Pico target uses Matter. The
sibling iLabs_ESP-NOW ships ESP32_NOW.h and ATLink.h, which nothing
includes, so it needs no entry.
2026-09-07 10:45:17 +02:00
Thomas Göttgens a8912b1eb5 fix(ci): pin tool-scons to 4.8.1 for ESP targets (#11756)
PlatformIO Core now pulls tool-scons ~4.41101.0 (SCons 4.11.1), which
overrides the 4.8.1 the pioarduino espressif32 platform asks for. In
4.11.1 the lazy "import SCons.Tool.FortranCommon" that smart_link() uses
to pick a linker raises ModuleNotFoundError, so every ESP environment
fails at link-action resolution, before a single file is compiled:

  *** [.pio/build/<env>/firmware-<env>.elf] ModuleNotFoundError :
      No module named 'SCons.Tool.FortranCommon'

The package is not at fault; FortranCommon.py is present in
tool-scons-4.41101.0 and imports cleanly outside SCons. The failure comes
from the module state SCons's own tool loader leaves behind.

Scoped to esp32_common, which all six ESP architectures extend. nRF52,
STM32 and rp2040 are unaffected and keep the toolchain they have.
2026-09-07 10:44:51 +02:00
renovate[bot] 3520e4fa8c chore(deps): update pschatzmann_arduino-audio-driver to v0.3.1 (#11738)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-06 18:31:10 +00:00
Thomas Göttgens bd19fa8e48 feat(t-connect-pro): add LilyGo T-Connect-Pro variant (#11746)
* feat(t-connect-pro): add LilyGo T-Connect-Pro variant

ESP32-S3R8, 16MB flash, 8MB octal PSRAM. SX1262 LoRa, 480x222 ST7796 LCD
with CST226SE touch, W5500 ethernet and a 10A relay on EXT_NOTIFY_OUT.

LoRa, display and ethernet share one SPI bus (SCK 12 / MISO 13 / MOSI 11),
so every peripheral stays on SPI2_HOST.

board_level is extra and HW_VENDOR falls through to PRIVATE_HW until a
HardwareModel enum value is allocated.

* fix(w5500): serialize shared-bus SPI access with spiLock

Arduino's ETHClass reaches SPI through SPIClass, whose mutex is invisible to
LovyanGFX. On a board where both share a bus the MAC reads glitched frame
headers, and the resulting ESP_LOGE flood blocks the W5500 RX task on the
console UART until the task watchdog reboots the device.

SharedBusEthernet installs esp_eth directly so its custom_spi_driver
callbacks can take spiLock, the mutex the radio, display, SD and sensors
already share. It derives from NetworkInterface, so localIP(), connected(),
config() and the GOT_IP events are unchanged.

Selected by ETH_SHARED_SPI; boards without it keep the stock ETHClass path.

Measured on T-Connect-Pro under a 150 x 1472 byte flood with the display
active: 34948 truncated frames, 3 reboots and 20% packet loss before,
none after.

* fix(cst226se): honour reset pin, screen rotation and skip wrong-model probes

Drive TOUCH_RST when the variant defines one, and stop passing I2C pins to
begin() so SensorLib does not re-init a bus the scan already owns.

Derive touch geometry from SCREEN_ROTATE the way TFTDisplay does, so a
rotated panel maps to the landscape UI rather than the raw panel size.

Use TouchDrvCST226 rather than the TouchDrvCSTXXX wrapper. Pinning the model
does not stop the wrapper walking CST816 and CST92xx, whose retries cost
about 3.5s of boot. T-Beam behaviour is unchanged.

* style: trim comments to the two-line limit

Follows the comment rule in .github/copilot-instructions.md, which the
original commits missed.

* feat(t-connect-pro): use the T_CONNECT_PRO hardware model

Depends on meshtastic/protobufs#1062. Does not build until that merges and
the generated headers are synced, since meshtastic_HardwareModel_T_CONNECT_PRO
does not exist yet.

Drops -D PRIVATE_HW, which becomes a no-op once HW_VENDOR resolves, and
promotes board_level to release.

* chore(t-connect-pro): mark as community supported

Support level 3, matching the other unlicensed LilyGo boards.

* fix(w5500): roll back partial init when begin() fails

begin() returns early when ethHandle is set, so a failure after
esp_eth_driver_install() left the handle populated and every later call
returned true with no working driver.

teardown() releases the event handler, netif glue, netif, driver, PHY and MAC
in reverse creation order, and every failure path now uses it.

* refactor(w5500): drop config the custom SPI driver never reads

spi_devcfg and spi_host_id are only read by w5500_spi_init, which esp_eth
skips when custom_spi_driver is set, so the device config fields were dead.

Also drops the handle() accessor, its only caller is the class's own event
handler, the eventRegistered flag, since unregistering an unregistered
handler is safe, the redundant _esp_netif guard around destroyNetif(), and
the TFT_CS indirection, which this panel path does not read.

* fix(w5500): fail begin() when the event handler cannot register

The return value was ignored, so a failed registration still started Ethernet
and returned true while onEthEvent never fired. WiFiAPClient would then miss
ETH_CONNECTED, GOT_IP and DISCONNECTED, leaving the link up with the firmware
believing it was down.
2026-09-06 15:12:58 +00:00
Thomas GöttgensandClaude fb8319efaa fix(t-deck-pro-v1_1): link variant.cpp so the LoRa radio is powered on (#11715)
The T-Deck Pro V1.1 boots into Critical Fault #3 (NoRadio) because
LORA_EN (GPIO 46) is never driven high, so the SX1262 never receives
power and is never detected.

Before #9438 this pin was driven from src/main.cpp under
`#elif defined(T_DECK_PRO)`, which covered both the t-deck-pro and the
t-deck-pro-v1_1 environments, since both build with -D T_DECK_PRO.
#9438 moved that block into variants/esp32s3/t-deck-pro/variant.cpp and
linked it with a build_src_filter added only to the t-deck-pro
environment. t-deck-pro-v1_1 had been added five days earlier and has
neither a variant.cpp nor a build_src_filter, so it silently lost the
pin setup: earlyInitVariant() is a weak symbol with an empty default in
main.cpp, so a missing strong override produces no compile or link
error.

Give t-deck-pro-v1_1 the same variant.cpp as t-deck-pro, plus the
build_src_filter needed to actually link it. This also restores the
LORA_CS, SDCARD_CS and PIN_EINK_CS pre-init lost at the same time; all
three share the SPI bus and need their chip selects deasserted before
the bus is used.

Fixes #11708

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-03 11:29:52 +00:00
rcarterazandThomas Göttgens 3f4b96c24d Fix architecture name for Seeed Wio Tracker L2 (#11713)
* Fix architecture name for Seeed Wio Tracker L2

* Normalize custom_meshtastic_architecture against the board MCU

The declared value reached the manifest unchecked, which is how esp32s3 shipped
here and in the -tft env that extends it. infer_architecture() already derives
the canonical spelling from the board MCU, so prefer it when the two disagree
and print the override.

Scanned all 113 envs declaring an architecture; this variant was the only
mismatch.

* Simplify the architecture override

---------

Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
2026-09-03 10:25:22 +00:00
renovate[bot] 51ed88100e chore(deps): update platformio/ststm32 to v20 (#11685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-03 08:13:48 +00:00
renovate[bot] 077fe4823d chore(deps): update esp32-ch390 to v1.2 (#11712)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-03 08:13:05 +00:00
renovate[bot] fa65b0797e chore(deps): update lovyangfx to v1.2.28 (#11615)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-02 08:58:56 +00:00
Manuel 80c6d2f8da Update device-ui library version in platformio.ini (#11694) 2026-09-02 01:10:54 +00:00
renovate[bot] a2c919d2a2 chore(deps): update platformio/nordicnrf52 to v11 (#11684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-01 22:25:58 +00:00
Manuelandcoderabbitai[bot] 36c89fa3a7 feat: Support Seeed Wio Tracker L2 (#10909)
* initial commit

* enable power save

* implement mesh LED

* add ADS1115+AW35615 for wio tracker L2

* add ES8311, GT911, AW35615, LP5814 to I2C scanner

* update commit references

* move variant.cpp to extras

* update hw_model

* update lovyanGFX

* point to device-ui commit

* trunk fmt

* fix IO expander (have to take from SensorLib for now as long as AudioThread has the limitation to only support SensorLib and the previous IO expander clashes with duplicate names in arduino-audio-driver)

* workaround duplicate defined symbol

* remove SensorLib; add lightweight Pca9555 class and use unified USE_PCA95X5; add wake button detection

* keep TP_INT disabled(OUTPUT) as we use wake button for wakeup

* PA off by default, enabled when playing sound; add some delay because typical class-D amps (NS4150 family) spec 20–50ms for the output stage to reach full swing after power-on

* refactored AW35615 into new external library

* local revert of PR10571 as this PR completely breaks the alert sound

* fix detection of ADS1115

* update device-ui commit reference

* add synchronisation to IO expander and call toggleDisplay() on wake button press

* add battery curve, fix io expander sync

* add SPILock, simplify macro usage

* update device-ui

* fix wakeup from sleep

* revert because of #11604

* use new AUDIO_AMP_SETTLE_MS

* remove test logs

* enable BaseUI

* use touch screen

* refactor wakekey thread

* fix wake button toggle screen on/off

* fix battery percentage and plugIn state

* Update src/graphics/TFTDisplay.cpp

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* consider to return I2C errors to make coderabbi happy

* fix warnings

* use Throttle for millis comparison

* fix endTransmission in write

* make the rabbit happy

* spli targets -tft / non-tft

* fix compile

* revert forced use of Throttle

* remove MeshLED

* add HW_MODEL

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-08-28 23:30:27 +00:00
Andrew YongandBen Meadors db84bdf3b4 Reduce ExternalNotificationModule flash usage (RTTTL + InputBroker) (#10989)
* Generalize RTTTL exclusion into MESHTASTIC_EXCLUDE_RTTTL

ExternalNotificationModule already stubbed out RTTTL playback for
STM32WL/portduino/ESP32C6 via a raw ARCH/CONFIG_IDF check, but the
ringtone config plumbing around it (protobuf message, encode/decode
tables, /prefs/ringtone.proto persistence, admin get/set-ringtone
handlers) still compiled in even though it can never do anything on
those platforms.

Introduce MESHTASTIC_EXCLUDE_RTTTL and gate the dead ringtone plumbing
behind it too. The flag is set in each architecture's *_base build_flags
(stm32_base, esp32c6_base, portduino_base) rather than in the module
itself - this matches how every other MESHTASTIC_EXCLUDE_* flag in the
tree is set (e.g. stm32_base already sets ten of them directly, and
esp32c6_base already excludes PAXCOUNTER for an analogous
platform-can't-support-this reason), rather than introducing a new
per-architecture C header pattern. Behavior is unchanged on all three
platforms; overridable via -D like every other MESHTASTIC_EXCLUDE_*
flag.

Also guard the two HAS_I2S ringtone-playback call sites with
!MESHTASTIC_EXCLUDE_RTTTL alongside HAS_I2S, since rtttlConfig itself
is now only declared when RTTTL is not excluded. No current platform
defines both HAS_I2S and MESHTASTIC_EXCLUDE_RTTTL simultaneously, so
this has no effect today, but prevents a future HAS_I2S platform that
also excludes RTTTL from failing to compile.

Saves 368 bytes flash / 236 bytes RAM on wio-e5 with no loss to the
GPIO on/off notification toggle itself, which does not depend on
RTTTL.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* Skip unused InputBroker observer in ExternalNotificationModule

The inputObserver CallbackObserver member was declared unconditionally,
even though its only use site was already gated behind
MESHTASTIC_EXCLUDE_INPUTBROKER (set for all of stm32 in stm32.ini).
Because it's a non-trivial member, the compiler still generated its
constructor/destructor as part of ExternalNotificationModule's own
lifecycle even when InputBroker is compiled out entirely.

Gate the member and its only consumer, handleInputEvent(), behind the
same flag as their use site, and match the codebase's dominant
!MESHTASTIC_EXCLUDE_X style (used ~330 times) rather than
!defined(MESHTASTIC_EXCLUDE_X) (used ~20 times) while touching this
flag's other call site. Saves an additional 288 bytes flash on wio-e5,
no RAM change, no functional impact since InputBroker was already
unused on this platform.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(native-wasm): exclude RTTTL to unbreak build

The browser node builds its own build_flags from arduino_base rather
than inheriting portduino_base, so it did not pick up the
MESHTASTIC_EXCLUDE_RTTTL flag added to portduino_base. With the inline
ARCH_PORTDUINO stub in ExternalNotificationModule.h now replaced by that
flag, native-wasm tried to include the unavailable NonBlockingRtttl.h.

Set MESHTASTIC_EXCLUDE_RTTTL=1 directly in the native-wasm env
alongside its other exclusion flags.

Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>

---------

Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-08-28 19:58:23 +00:00
Andrew Yong 78219e09cb fix(stm32wl): add TCXO-optional support and fix hardcoded TCXO voltage (#10964)
* stm32wl: consult SX126X_DIO3_TCXO_VOLTAGE instead of hardcoding 1.7V

Every STM32WL variant except rak3172 got setTCXOVoltage(1.7) unconditionally,
regardless of what the board's hardware actually needs, and rak3172 got no
TCXO configuration at all - so a real RAK3172-T (populated TCXO) failed
radio init outright.

Read SX126X_DIO3_TCXO_VOLTAGE per variant instead. When TCXO_OPTIONAL is
also defined, retry once on XTAL if the TCXO attempt fails, mirroring the
existing pattern in LR11x0Interface.cpp, LR20x0Interface.cpp, and the
SX1262/SX1268 paths in RadioInterface.cpp.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl(rak3172): support both non-T and -T hardware via TCXO-optional

RAK3172 is XTAL-only; RAK3172-T has a populated 3.0V TCXO, matching RAK's
own reference radio_conf.h. One PlatformIO environment now serves both:
tries the TCXO first, falls back to XTAL if not populated.

Hardware-verified on a TCXO-equipped board electrically equivalent to
RAK3172-T. Genuine non-T hardware not available to re-verify the fallback
path; reasoned from RadioLib source instead (see PR description).

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl(wio-e5): declare the module's 1.7V TCXO explicitly

Matches Seeed's own reference radio driver. wio-e5 previously relied on
the hardcoded 1.7V fallback being removed by the preceding commit, which
would have broken it - declare the voltage explicitly instead.

Hardware-verified via SWD: without this define, the radio interface fails
to come up at all (sendtext NAKs with NO_INTERFACE, meaning rIf is null).
With it, NO_INTERFACE goes away and the device sends/receives normally.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl(CDEBYTE_E77-MBL): mark TCXO voltage optional, hardware varies by unit

EByte changed the E77-MBL hardware in early 2024: units with serial number
>= 3202995 have a TCXO, older units have a ceramic crystal oscillator
instead. Both ship under the same module name, so probe for the TCXO and
fall back to XTAL rather than assuming either.

https://github.com/olliw42/mLRS-docu/blob/main/docs/EBYTE_E77_MBL.md

Not hardware-tested - no E77-MBL board available this session.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl: trim TCXO comment blocks to repo's 1-2 line guideline

Per review feedback on PR #10964 (CodeRabbit nitpicks) - the rak3172 and
CDEBYTE_E77-MBL variant.h comments were 4-line blocks, exceeding the
repo's comment-length convention. Condensed to one line each, same
information and links retained.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Signed-off-by: Andrew Yong <me@ndoo.sg>
2026-08-28 18:21:58 +00:00
Andrew YongandTom 57d17cfd44 fix(stm32wl): recover from littlefs internal corruption instead of hanging (#11230)
LFS_ASSERT (src/platform/stm32wl/littlefs/lfs_util.h) was a plain assert(),
which on STM32WL hangs forever with no diagnostic (__wrap___assert_func is
while(true);, see main-stm32wl.cpp). STM32_LittleFS::begin() is already
designed to treat corruption as recoverable - format and retry, see
fsFormat()/NodeDB::saveToDisk() - but that only works if lfs_mount() cleanly
returns an error. An internal littlefs consistency check failing (metadata
pair/CRC/block-allocator invariants) never returns at all, so a bad flash
sector or power loss mid-write could permanently brick a device that would
otherwise have recovered via the existing reformat path.

nRF52 already hit this and fixed it (LFS_NO_ASSERT + a custom lfs_assert()
that reboots into a reformat, see meshtastic/firmware#3818). Port the same
approach to STM32WL: LFS_NO_ASSERT routes LFS_ASSERT through a custom
lfs_assert() instead of disabling the check outright, and lfs_assert()
requests a reformat-on-next-boot via a .noinit SRAM magic value (the same
mechanism already used for the DFU bootloader redirect in this file, chosen
specifically because backup/TAMP registers don't reliably survive a soft
reset in this toolchain) and reboots, rather than trying to reformat
littlefs from inside its own possibly-mid-operation callback.

Unlike nRF52 (a third-party Adafruit library patched via a -include
override so as not to fork it), STM32WL's littlefs copy is already a
project-owned vendored file, so lfs_util.h is edited directly.

Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Tom <116762865+NomDeTom@users.noreply.github.com>
2026-08-28 17:37:32 +00:00
IxitxachitlandManuel 7aa8ad3510 fix(t-watch-ultra): build with the esp32s3 flags, not the classic-ESP32 ones (#11619)
* fix(t-watch-ultra): build with the esp32s3 flags, not the classic-ESP32 ones

The env was the only esp32s3 variant extending ${esp32_base.build_flags} (since
#8171). That base adds -D ESP32_FORCE_IRAM_MEMSET -Wl,--wrap=memset
-Wl,--wrap=memcpy, and the wrappers in IramMemcpy.c/IramMemset.c decide whether
the cache is on by reading 0x3FF00040 - DPORT_PRO_CACHE_CTRL_REG on the classic
ESP32, an address the S3 does not map at all (soc.h: DRAM 0x3FC88000-0x3FD00000,
DROM 0x3C000000-0x3E000000, IRAM 0x40370000-0x403E0000, peripherals 0x60000000).

--wrap is link-wide, so every memcpy/memset in the image - including inside the
precompiled WiFi, lwIP and flash driver libraries - branched on that undefined
read. Two long-standing board-specific bugs came from it, both dating to #8171,
which introduced the wrong base and the first workaround in the same commit:

* WPA2 networks associated and completed the 4-way handshake, then never got a
  DHCP lease, while open networks worked normally (#11513).
* Direct flash reads returned 0x00 for data that was correct on flash, so NVS
  came up empty every boot and dropped BLE bonds (#11530).

Switching the env to esp32s3_base fixes both on hardware: WPA2 gets a lease, and
NVS survives a reboot with the bond intact. The read workaround that #11530
needed - -Wl,--wrap=esp_partition_read, -Wl,--wrap=esp_flash_read and
esp_partition_read_mmap_wrap.c - is therefore removed as well.

The module excludes the env inherited from esp32_base go with it, so the board
now matches every other esp32s3 variant: web server and paxcounter are built
(paxcounter still only runs when enabled in config), and MESHTASTIC_EXCLUDE_AUDIO
was already inert here because AudioModule additionally requires USE_SX1280.
-UMESHTASTIC_EXCLUDE_ACCELEROMETER goes too, having only existed to undo an
inherited -D.

Also guards ESP32_FORCE_IRAM_MEMSET behind CONFIG_IDF_TARGET_ESP32, so a variant
cannot enable the classic-ESP32 probe on another target again.

* Update platformio.ini

added missing ${device-ui_base.custom_sdkconfig}

---------

Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>
2026-08-27 18:27:02 +00:00
Thomas Göttgens 9fbc176e91 Extend userPrefs coverage to the whole channel table and the missing config fields (#11624)
* Extend userPrefs coverage to the whole channel table and the missing config fields

initDefaultChannel() handled only indices 0-2, so USERPREFS_CHANNELS_TO_WRITE above 3 produced live secondary channels carrying the public default PSK; it now covers all eight slots, with bin/platformio-custom.py completing every field of a configured index so indices 0-2 stay byte-identical. Adds USERPREFS_CHANNEL_<n>_IS_MUTED, USERPREFS_CONFIG_DEVICE_REBROADCAST_MODE, USERPREFS_CONFIG_DEVICE_NODE_INFO_BROADCAST_SECS, USERPREFS_CONFIG_LORA_CONFIG_OK_TO_MQTT, USERPREFS_CONFIG_SECURITY_IS_MANAGED and USERPREFS_CANNED_MESSAGES, applied after installRoleDefaults() and validated the way AdminModule validates a set-config. Adds test_userprefs_channels, covering the configured table under coverage-channel-table and the stock defaults under every other env.

* Address review: hex channel count, PSK width assert, canned-message termination

USERPREFS_CHANNELS_TO_WRITE now parses 0x-prefixed hex, matching the format
userPrefs.jsonc documents, without int(x, 0)'s rejection of a leading-zero
decimal such as "03". A static_assert rejects a USERPREFS_CHANNEL_<n>_PSK
literal wider than psk.bytes, which memcpy would otherwise write over the fields
after it. The USERPREFS_CANNED_MESSAGES copy keeps strncpy's zero-padding and
terminates explicitly, rather than shortening the length, which would have left
the last byte unwritten.
2026-08-27 15:00:59 +00:00
cd6ac90f7e Add waypoint & geofence support with notifications for BaseUI and InkHUD (#10920)
* Implement GeofenceModule for waypoint crossing notifications and integrate with existing modules

* Waypoint Applet Initial Support on InkHUD

* undo tile change

* Update screen when Waypoint shows or dissapears

* Merge branch 'develop' into waypoint-geofence

* Geofence on InkHUD

* Update MapTile.h

* Update WaypointStore.cpp

* Notifications

* remove GF from waypoint screen

* Prevent Focus from closing the notifiaction banner

* Trunk fix

* cleanup

* undo merge conflix mistake

* Waypoint screen on BaseUI

* Focus preserve fix

* UI bugs

* Allow Inkhud to remove waypoint

* Respect Locked Waypoints

* Trunk fix

* Update WaypointStore.cpp

* Use 8-digit hex formatting for waypoint IDs.

0x%x was inconsistent with the repo's own convention (0x%08x for 32-bit IDs, used elsewhere in this file). Fixed here and in two other spots I found with the same issue (WaypointModule.cpp, GeofenceModule.cpp).

* Update ExternalNotificationModule.cpp

* Reject invalid surrogate codepoints in waypoint icon rendering

* Update WaypointModule.cpp

* Update WaypointStore.cpp

* Update WaypointStore.cpp

* Update WaypointStore.cpp

* trunk fix

* fix warnings

* power.h rename to Power.h

* Update Power.h

* Fix executable bit on bin/lint-ifdef-complexity.sh

Lost during a prior merge from develop (Windows checkout doesn't
preserve file mode), causing "execve failed: Permission denied" in
the Trunk Check Runner CI job. develop has this file at 100755;
restoring that here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Update README.md

* Clean up waypoint and geofence integration

* Minimize waypoint and geofence implementation

* removed unnecessary gating

* Geofence alert

* trunk fix

* Update test_main.cpp

* Update WaypointStore.cpp

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 16:17:28 +00:00
Thomas Göttgens 8eda86045b fix(audio): amp settle window, and start melody after codec init (#11604)
* fix(audio): amp settle window, and start melody after codec init (#11597)

* chore(audio): condense the new code comments to two lines
2026-08-25 20:40:39 +00:00
Thomas Göttgens 7b0004806a fix(graphics): drive GPIO backlights from the stored brightness level (#11588)
* fix(graphics): drive GPIO backlights from the stored brightness level

Screen::handleSetOn restored PIN_EINK_EN only when screen_brightness was
exactly 1. The field is 0..255 and defaults to 153, so the frontlight stayed
off after a screen timeout until the next reboot.

InputBroker read screen_brightness as "currently lit" for the touch backlight,
so a stored level made touch-to-light a no-op. The HAPTIC_FEEDBACK_PIN block
then reassigned touchConfig.onPress and onRelease, dropping those handlers on
any variant defining both.

MINI_EPAPER_S3 names its panel power rail PIN_EINK_EN. It was switched off with
the screen and never restored.

graphics::Backlight gains a GPIO backend covering PIN_EINK_EN and
PCA_PIN_EINK_EN, so Screen, MenuHandler and InputBroker call backlightOn,
backlightOff, backlightToggle and backlightIsLit instead of touching pins.
backlightIsLit reports the driven state, separate from the configured level.

Power-up state is declared per variant with GPIO_BACKLIGHT_DEFAULT_ON rather
than hardcoded in the e-ink driver. The backend stores only 0 or 255, so any
other stored level falls back to the variant default and no board changes its
existing behaviour. MINI_EPAPER_S3 is excluded and keeps its rail powered.

Touch handlers are merged so backlight and haptic feedback compose.

Verified on ThinkNode M1: lit at boot, off on timeout, lit on wake, and an
explicit off surviving both wake and reboot.

* chore(thinknode_m1): correct the LED pin comments

P0.13 drives the blue indicator, not a green one. P1.06 is a second drive for
the same red LED as LED_POWER, which is why it stays disabled.

* fix(graphics): clamp GPIO backlight levels at the setter

backlightSet stored whatever level it was given, so a caller passing an
intermediate value left backlightGet and the persisted config holding a level
the rail cannot drive. Clamp to off or on in the setter, which keeps the
invariant at the single write point instead of only at init.
2026-08-25 08:49:37 +00:00
Thomas Göttgens c45b66352b Idle the Wio Tracker L1 buzzer pin at boot (#11601) 2026-08-25 08:31:48 +00:00
48357538ba Meshnology W10: enable the AXP2101 power key as a second button (#11593)
* Meshnology W10: enable the AXP2101 power key as a second button

SW3 is wired to the AXP2101 PWRON pin (via R44 510R, schematic W10-MB-V1.1
pg3), but the key did nothing in firmware.

Power::runOnce() already polls the PMU IRQ status registers over I2C and maps
a PEK short press to INPUT_BROKER_CANCEL when PMU_POWER_BUTTON_IS_CANCEL is
set. However the matching PMU->enableIRQ() lives inside #ifdef PMU_IRQ, while
PMU init runs disableIRQ(ALL) first. Without PMU_IRQ the PKEY_SHORT status bit
is never armed, so the polled read is always false and the define alone is
inert.

AXP_IRQ on this board reaches only expander EXIO5 and is not routed to any
ESP32 GPIO, so define PMU_IRQ as the MCP23017 virtual pin, mirroring how
LORA_DIO1 is handled on this variant. The attachInterrupt() and
gpio_wakeup_enable() uses of PMU_IRQ are inert on a non-GPIO value (both are
unchecked calls, so an invalid pin is ignored rather than fatal); what the
define buys is the enableIRQ() they gate.

Tested on Meshnology W10 hardware: short presses of SW3 now log
"[Power] Input: Corona Button Click", the existing GPIO0 user button continues
to work independently, and the board boots normally. Events surface on the 20s
Power::runOnce() cadence, since with no real interrupt the ISR's
setIntervalFromNow(0) never runs to force an immediate poll.

* style: apply clang-format to meshnology-w10 variant.h

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2026-08-25 01:44:06 +00:00
IxitxachitlandBen Meadors e8d4573af7 fix(t-watch-ultra): wrap esp_flash_read so NVS survives, keeping BLE bonds (#11583)
* fix(t-watch-ultra): wrap esp_flash_read so NVS survives, keeping BLE bonds

The IDF 5.5 manual-read regression on this board's flash is already worked
around for esp_partition_read, but nvs_flash does not use that API: it reads
the NVS partition through the lower-level esp_flash_read, which still returns
0x00. NVS therefore initialised empty on every boot -- zero entries, zero
namespaces -- even though the data was intact on flash.

Everything stored through NVS was lost each boot, including NimBLE's bond
table. A phone that had already paired was not recognised on reconnect, so
the device ran a fresh pairing and displayed a new passkey every time. The
PIN worked, but the bond never persisted.

Wrap esp_flash_read the same way, using the raw (non-partition) spi_flash_mmap
so it serves callers that never go through the esp_partition_t API. Reads for
any chip other than the default fall back to the real implementation, as do
mmap failures. Gated on T_WATCH_ULTRA; no other board is affected.

* fix(t-watch-ultra): keep the raw-read contract when flash encryption is on

esp_flash_read is specified to return raw, still-encrypted bytes; the flash
cache is what decrypts transparently. Reading through spi_flash_mmap therefore
hands back plaintext where the caller asked for ciphertext.

No target here enables CONFIG_SECURE_FLASH_ENC_ENABLED, so nothing is affected
today, but --wrap is a global interposition and encryption can be burned into
efuse independently of the build config. Check at runtime and leave encrypted
flash to the real implementation.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-08-24 23:02:04 +00:00
Thomas Göttgens ee48094ea8 Fix backwards GPS_RX_PIN/GPS_TX_PIN direction comments (#11585)
GPS.cpp passes GPS_RX_PIN as the MCU's RX pin and GPS_TX_PIN as its TX
pin. Nine variants documented the opposite, which reads as if the pins
were swapped on working hardware (see #11584).

Comment-only change; no pin assignment is touched.
2026-08-24 11:29:54 +02:00
Ben MeadorsandQuency-D bfd1e1a231 Add Heltec RC32, RC52 and RCC6 boards, and LC760CA GNSS support (#11572)
* refactor(graphics): select Arduino_GFX panels with a capability flag

TFTDisplay tested `defined(HACKADAY_COMMUNICATOR)` in a dozen places to mean
"this panel is driven by Arduino_GFX rather than LovyanGFX". Every new
Arduino_GFX board had to be appended to all of them.

Move the decision into the variant as USE_ARDUINO_GFX so the display code
stops naming individual boards. No behaviour change: the Hackaday Communicator
is still the only board that sets it.

* feat(boards): add Heltec RC32, RC52 and RCC6

Three boards around the same 128x220 NV3001B panel: RC32 (ESP32-S3), RCC6
(ESP32-C6) and RC52 (nRF52840). They differ only in how the panel bus is
wired, so they share one branch in TFTDisplay behind TFT_NV3001B.

RC32 and RC52 also carry a rotary encoder on a TCA6408 I2C expander. That
lands as its own input source rather than as board conditionals inside
i2cButton, which is the M5Stack UnitC6L button driver and stays untouched.

On RC52 and RCC6 the panel is an add-on module, so probe it before reporting
a screen. The probe reuses the bit-banged SPI helper that already backs the
T114 ST7789 check.

Arduino_GFX is pinned to the upstream commit that added the NV3001B driver;
it has not shipped in a tagged release yet.

Co-Authored-By: Quency-D <55523105+Quency-D@users.noreply.github.com>

* feat(gps): detect and configure the LC760CA GNSS module

The LC760CA is another Unicore part, so it joins the $PDTINFO probe family
and reuses the CM121 message-rate setup. It answers with CC1161W.

GNSS_MODEL_LC760CA goes immediately before GNSS_MODEL_GENERIC_NMEA: the
sentinel has to stay last because isValidGnssModel() uses it as the exclusive
upper bound on values the probe cache may hold. Placing the new model after
it would leave LC760CA permanently uncacheable.

Co-Authored-By: Quency-D <55523105+Quency-D@users.noreply.github.com>

* fix(graphics): re-init the NV3001B after the panel rail comes back

DISPLAYOFF de-asserts VTFT_CTRL, which cuts power to the panel, so the
controller loses MADCTL, COLMOD and gamma. displayOn() only sends sleep-out
and cannot restore them, leaving the panel dark or in the wrong format after
wake. Re-run begin() once the rail has settled, and repaint in full since the
re-init leaves display RAM undefined.

Also stop the TCA6408 rotary polling from two threads at once. Registering as
an InputPollable meant InputBroker's pollSoon task could call pollOnce() while
runOnce() was mid-transfer on the main thread, with nothing serialising Wire
or the decoder state. Drop InputPollable and have the interrupt wake the
thread instead, the way ButtonThread does, so the bus and the decode stay on
one thread.

* fix(graphics): skip the NV3001B wake when re-init fails

begin() reports whether the bus came up. Ignoring it meant a failed re-init
still lit the backlight and drove a full-screen repaint at a panel that was
never initialised.

* chore(boards): ship the Heltec RC boards at release level

release is the normal level for a variant; the matrix generator still builds
each of these in this PR because they add a new platformio.ini.

---------

Co-authored-by: Quency-D <55523105+Quency-D@users.noreply.github.com>
2026-08-23 11:00:37 +00:00
zelo533andBen Meadors 05f6474108 meshnology-w10: define HAS_SPI_TFT so the TFT screen initializes again (#11042)
#10803 refactored main.cpp to key SPI-TFT Screen creation on HAS_SPI_TFT
instead of the per-controller define list. The W10 variant (#10911) was
written before that refactor and crossed it mid-air, so it never defines
HAS_SPI_TFT and develop builds fall through to the I2C-OLED autodetect
branch: no Screen is ever constructed and the display stays dark, while
everything else (radio, GPS, BLE) works.

Verified on a real W10: with the define, the boot log shows TFTDisplay
creation, backlight power-on and the boot screen, and the ST7789 panel
renders the UI again.

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-08-22 19:06:33 -05:00
Ben Meadors f6f116a39d Fill in device registry metadata for recently added hardware (#11567)
Audit of the custom_meshtastic_* manifest on the variants backing the
newest boards, against the protobuf HardwareModel enum, the compiled
HW_VENDOR, the board flash size and the artwork actually published by
the web flasher. No support flag changes here - actively_supported is
left exactly as each variant already had it.

ThinkNode M9 had no HW_VENDOR arm, so every M9 has been reporting
PRIVATE_HW while its manifest advertised 131; add the mapping and
rename the slug to the enum name (THINKNODE_M9) it is meant to mirror.

Seeed SenseCAP Mesh-Tracker X1 moves from the PR matrix to release, and
its images entry now points at seeed_mesh_tracker_x1.svg, which is what
the flasher actually ships - the hyphenated name resolved to nothing.

T-Beam BPF, T-Beam 1W and Heltec Wireless Tracker V2 declared the
architecture as "esp32s3"; the value is copied verbatim into the
manifest, and the flash flow matches on the normalized "esp32-s3".

T-Beam BPF and M5Stack Unit C6L both build default_16MB.csv on 16 MB
flash but declared no partition scheme, which leaves the flasher on the
4 MB fallback offsets for a legacy clean install.

Meshnology W10 and W12 gain the artwork and vendor tag that already
exist for them.
2026-08-22 14:34:49 +00:00
vidplace7 f22ce82f5a fix t-deck-pro: disable BHI260AP support until SensorLib replacement is available
Missed in the previous commit
2026-08-21 12:42:16 -04:00
Austin 5f7077c44e fix t-deck-pro-v1.1: disable BHI260AP support until SensorLib replacement is available (#11562) 2026-08-21 11:25:39 -05:00
Austin 4d9d0f8a16 chore(deps): Correct library dependencies for T-Deck Pro and T-Watch Ultra (#11561) 2026-08-21 10:59:26 -05:00
renovate[bot] 4c640270f2 chore(deps): update lovyangfx to v1.2.27 (#11533)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-21 13:46:14 +00:00
renovate[bot] 1afcdabbe9 chore(deps): update esp8266audio digest to 3430246 (#11557)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-21 10:47:19 +00:00