The markChannelReadFromBLE() calls in the CMD_SYNC_NEXT_MESSAGE handler were marking channels and DMs as read the moment the BLE companion app synced them from the offline queue. Since the app drains the entire queue automatically on connect, this had the effect of clearing all unread indicators on the device as soon as BLE connected — before the user had actually read anything in either the app or on the device.
The MeshCore BLE protocol has no "user opened this channel" command from the app side; CMD_SYNC_NEXT_MESSAGE is an automatic bulk pull, so "synced to app" ≠ "read by user." Removed the channel and DM mark-read calls so unread counts only clear when the user navigates to that channel on the device itself. The msgRead() progress counter (syncing X messages) is unaffected.
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.