UITask shutdown (already occurs/unchanged):
1. BLE disabled (_serial->disable()).
2. WiFi disconnected + WIFI_OFF.
3. 4G modem shutdown().
4. GPS power cut (PIN_GPS_EN LOW).
5. LoRa radio powerOff() (standby mode).
6. Display turnOff().
TDeckBoard::powerOff() (new):
7. btStop() -- BLE controller stop.
8. Peripheral power OFF (PIN_PERF_POWERON LOW) -- keyboard, BQ27220, sensors.
9. LoRa module power OFF (P_LORA_EN LOW) -- cuts power entirely.
10. Hold LoRa NSS high (prevents SX1262 drawing current from floating CS).
11. esp_deep_sleep_start() -- CPU halts, ~10-40uA.
Wake (reset button or USB power-on):
12. ESP32-S3 cold boots.
13. TDeckBoard::begin() runs: peripheral power ON, I2C init, LoRa power ON, NSS hold released.
14. App starts fresh -- prefs/contacts/messages load from flash.
No LoRa wake during hibernate -- the device is truly off. Only a hardware
reset (reset button) or USB power-on wakes the device.
Incorporates hardware-specific learnings from caveman99's Meshtastic
T-Echo Card support PR (meshtastic/firmware#10267), cross-referenced
against LilyGo's official t_echo_card_config.h pinmap.
Battery (critical):
- Add BATTERY_MEASUREMENT_CONTROL pin P0.31 — gates the resistive
voltage divider feeding AIN0. Without toggling this pin, battery
ADC reads are invalid (floating divider input).
- TechoCardBoard::getBatteryVoltage() now drives P0.31 HIGH before
SAADC read and LOW after to avoid parasitic drain.
Display (critical):
- Add OLED_DISPLAY_OFFSET = 24 for the SSD1315 72×40 panel. The
physical display is mapped at GDDRAM pages 3–7 (rows 24–63);
SETDISPLAYOFFSET and SETMULTIPLEX commands are sent after
display.begin() in target.cpp to shift the visible window.
Power rail (high priority):
- RT9080 3V3 LDO now gets a clean HIGH→LOW→HIGH reset cycle with
100ms dwell in board.begin(), preventing brown-out when LoRa TX
fires at +22 dBm after a soft reset.
TDPro - Update firmware build date
Contactsscreen.h — five changes:
- EPOCH_2026 = 1735689600UL constant added (Jan 1 2026 UTC), used in sort
and formatAge.
- typeChar replaced by typeStr returning const char*, with "RS" for room
servers (previously "S", easily confused with sensors). prefix buffer
bumped to [5], all three snprintf calls updated to %s.
- Hop display: out_path_len == 0xFF branch now performs a live lookup
against the 12 most recently heard advert paths (via
getRecentlyHeard). Matches on first 7 bytes of pub_key, extracts hop
count with a bph-aware sanity cap (64/bph max) to reject impossible
values. Shows "~D" for direct flood neighbours, "~N" for N-hop flood
path, "?" if not in the recent-heard cache. Resets to "?" on reboot
until each contact re-advertises — intentional, ensures hop count is
always fresh.
- Sort: _filteredTs now stores contact.lastmod (our local receive time)
instead of contact.last_advert_timestamp (sender's claimed time).
lastmod values below EPOCH_2026 are stored as 0 so stale repeaters
with unsynced clocks and contacts received before our own timesync
sink to the bottom of the list.
- formatAge rewritten: rejects timestamp == 0, timestamp < EPOCH_2026,
and now < timestamp (all show "--" instead of wrapping or displaying
garbage). Arithmetic changed from int to uint32_t, eliminating the
signed overflow path that produced negative hour values. Age display
call site switched from last_advert_timestamp to lastmod, so display
self-corrects after a GPS or 4G timesync.
SerialBLEInterface.cpp — added esp_bt.h include and three esp_ble_tx_power_set calls at +9 dBm after BLEDevice::init(), covering default, advertising, and scan power types.
MyMesh.h — FIRMWARE_VER_CODE bumped from 10 → 11.
MyMesh.cpp — The RESP_CODE_DEVICE_INFO frame construction now:
Byte 2: sends 0xFF (sentinel) when MAX_CONTACTS > 510, otherwise the normal MAX_CONTACTS / 2. Older apps interpret 0xFF as 510 contacts — completely harmless.
Bytes 80-81 (new, appended after the version string): uint16_t little-endian with the true MAX_CONTACTS value. Apps that understand v11+ read it here. Apps < v11 ignore trailing bytes — the BLE/serial frame protocol is length-delimited, so extra bytes at the tail are safe.
platformio.ini — Both BLE builds (meck_audio_ble, meck_4g_ble) bumped from 510 → 2000.
mymesh.cpp: writeContactRespFrame return type change (return _serial->writeFrame() result)
checkSerialInterface() batch-fill loop.