Keyboard LED brightness (T-Deck Pro MAX):
Adds a "Keyboard LED" row to Settings (MAX only), below Backlight
Brightness and mirroring it: 5-100% in 5% steps, default 50%. The
both-shifts keyboard backlight toggle previously used a hardcoded PWM
level of 8 (~3%), too dim for some users; it now reads the stored
percentage and maps it to PWM, taking effect on the next toggle.
- NodePrefs.h: new kb_backlight_pct field (5..100, default 50)
- Settingsscreen.h: ROW_KB_BACKLIGHT row plus label/edit handling
- DataStore.cpp: load/save/clamp, appended so existing prefs files stay
compatible (fall back to 50%)
- main.cpp: toggle uses kb_backlight_pct (percent -> PWM) instead of 8
Standalone unread counter fix:
On standalone builds the home-page MSG count and channel-picker unread
badges never updated, although messages arrived and displayed normally.
ArduinoSerialInterface::isConnected() is hardcoded to return true (a plain
UART has no connection state), so the periodic
setHasConnection(_serial->isConnected()) in MyMesh left hasConnection()
permanently true -- making UITask mark every received message read on
arrival. The same stuck flag also suppressed the DM counter and the
new-message screen wake. Guard the call so only builds with a real
companion (BLE / WiFi / wired SERIAL_RX) derive connection state from the
interface; companion-less builds report not-connected. The core
ArduinoSerialInterface is unchanged, preserving wired-companion behaviour.
- MyMesh.cpp: guard setHasConnection() for companion-less builds
main.cpp — added a dispatch block in the Settings handler: when the network picker is showing and key=='\b' with shift consumed, it calls ui_task.injectKey('q'), which runs the existing 'q' exit path synchronously (tear-down of EDIT_WIFI, onboarding handling, wifiReconnectSaved()) and refreshes. So Shift+Del and Q now both exit the picker, with no duplicated exit logic.
RX packet counter: shows total received packets (flood + direct) on the
radio details page; RAM-only, resets on boot and on any SF/freq/BW change.
Rx Log: app-style packet sniffer opened from Settings > "Rx Log >>".
Captures the last 100 received packets pre-filter via Dispatcher::logRx
into a PSRAM ring, with decoded "sender: message" lines matched by packet
hash for decryptable channels. Each entry shows route/type, time, size,
hash, path, channel or From/To, SNR. W/S scroll, Q back to Settings;
T5S3 swipe-scroll rides the same path.
Files: MyMesh.h/.cpp, RxLogScreen.h (new), UITask.h/.cpp,
SettingsScreen.h, main.cpp.
GPS — fixed (the "off" toggle and boot path now actually cut the XL9555 rail). Kept in main.cpp + UITask.cpp.
BLE controller — the ~13 mA between the BLE build (BLE off) and standalone is the controller staying initialised; the boot-gate in SerialBLEInterface.cpp/.h defers BLEDevice::init() until first enable, reclaiming it. That's the meaningful idle win.
CPU, gyro rail, ES8311, frontlight — all measured and ruled out. The residual ~12 mA Max-vs-Pro is distributed always-on hardware with no software switch — hardware overhead, not a bug.
Defer ESP32 BLE controller bring-up to first user enable
MyMesh::startInterface() called serial.enable() unconditionally at boot. On the ESP32 BLE build this ran the deferred-init SerialBLEInterface's _realBegin()/BLEDevice::init() and powered the BT controller before main.cpp's boot-time disable(), which only stops advertising and cannot power the controller back down -- so the controller stayed up while "off", drawing ~13 mA at idle. Guard the enable() so ESP32 BLE builds skip it at boot; the controller now comes up lazily on the first enable() when the user turns Bluetooth on from the Bluetooth page. WiFi builds are unaffected and still enable at boot.
SPIFFS.begin(true) auto-formats on mount failure, but the auto-format
itself can fail if the partition contains residual data from a previous
firmware (e.g. stock LilyGo, Meshtastic, or MeshCore with a different
partition layout). When that happens the firmware previously printed
"SPIFFS format FAILED!" and continued in a broken state with no
persistence.
Now on auto-format failure:
1. Find the SPIFFS partition via esp_partition_find_first()
2. Erase it completely with esp_partition_erase_range()
3. Call SPIFFS.format() + SPIFFS.begin(false) with up to 3 retries
Added #include <esp_partition.h> under ESP32 guard.
The existing first-boot display feedback ("Formatting storage...
First boot - please wait") is unchanged -- it fires on the initial
mount failure. The new partition erase code only triggers if the
auto-format also fails.
Hibernate (deep sleep) leaves the BQ25896 charger IC powered, drawing
~30-60uA quiescent from BAT. This adds a second option on the shutdown
page -- "power off" -- that writes the BATFET_DIS bit in BQ25896 REG09
to fully disconnect the battery from VSYS. Leakage drops to ~12-23uA
(IC internal only). Wake requires USB-C plug-in.
Shutdown page now shows two options with a cursor (up/down to toggle):
> hibernate: long press/Enter (T-Deck Pro)
power off: long press/Enter
> hibernate: long press (T5S3 / other)
power off: long press
Selecting "hibernate" triggers immediately (unchanged behaviour).
Selecting "power off" shows a confirmation prompt:
power off device?
usb-c to wake
Enter:yes q:no
Power-off display suppresses the header (node name, clock, battery)
and shows only "powering off..." and "plug in USB-C to turn on".
The 's' key shortcut to settings is gated on the shutdown page so it
passes through to the hibernate/power-off selection toggle instead.
Both the TCA8418 handler (loop) and the broader handler
(handleKeyboardInput) are gated via isHomeOnShutdownPage().
PRESS_LABEL macro: removed dead UI_HAS_JOYSTICK branch (no Meck
device has a joystick), collapsed to a single #define "long press".
Joystick input polling block in loop() also removed (dead code behind
#if UI_HAS_JOYSTICK, never compiled for any Meck build).
BQ25896 I2C sequence follows TI recommendation (E2E forum, Jeff/TI):
1. Read REG09
2. Write BATFET_DLY=1 (bit 3) -- delays disconnect so I2C completes
3. Write BATFET_DIS=1 | BATFET_DLY=1 (bits 5+3) -- last I2C write
The write happens after display turnOff but before board powerOff, so
I2C pull-ups on VDD3V3 are still alive. Board enters deep sleep, then
BATFET opens after tSM_DLY (~10-15s). Skipping the delay risks leaving
the BQ25896 I2C engine in an undefined state that can prevent wake on
USB-C plug-in (device soft-brick requiring battery disconnect).
REG09 bit map (confirmed from Linux kernel bq25890_charger.c):
Bit 7: FORCE_ICO
Bit 6: TMR2X_EN
Bit 5: BATFET_DIS (0x20) -- disconnect battery
Bit 4: JEITA_VSET
Bit 3: BATFET_DLY (0x08) -- delay before disconnect
Bit 2: BATFET_RST_EN (0x04) -- QON wake (not wired on T-Deck Pro)
Bit 1: PUMPX_UP
Bit 0: PUMPX_DN
Schematic confirms QON (pin 12) has R4 10K pull-up to REGN with no
user-accessible button -- USB-C is the only wake path from ship mode.
Guarded by #ifdef I2C_ADDR_BQ25896 so it compiles on all platforms
but only activates on boards with the charger (T-Deck Pro, T5S3).
Files changed:
UITask.h -- _full_poweroff, setFullPowerOff(), isHomeOnShutdownPage()
UITask.cpp -- shutdown page UI, input handling, BATFET write,
PRESS_LABEL cleanup, joystick removal
main.cpp -- 's' key gated on shutdown page (both handlers)