Changed max contacts handling to psram so Audio BLE is 400 → 500 contacts, 20 channels (Near BLE protocol max (510)). Audio Standalone350 → 1500 (40 channels → 20) PSRAM-backed. 4G BLE env is 400 → 500 with 20 channels (Near BLE protocol max (510)). 4G Standalone is 600 → 1500 contacts with 20 channels - PSRAM-backed

This commit is contained in:
pelgraine
2026-02-26 14:14:39 +11:00
parent 623f3eaec4
commit 7915e5ef0b
5 changed files with 52 additions and 24 deletions

View File

@@ -1043,6 +1043,7 @@ void MyMesh::begin(bool has_display) {
_active_ble_pin = 0;
#endif
initContacts(); // allocate contacts array from PSRAM (deferred from constructor)
resetContacts();
_store->loadContacts(this);
bootstrapRTCfromContacts();

View File

@@ -18,7 +18,6 @@ public:
FILTER_REPEATER,
FILTER_ROOM, // Room servers
FILTER_SENSOR,
FILTER_FAVOURITE, // Contacts marked as favourite (any type)
FILTER_COUNT // keep last
};
@@ -31,9 +30,9 @@ private:
// Cached filtered contact indices for efficient scrolling
// We rebuild this on filter change or when entering the screen
static const int MAX_VISIBLE = 400; // matches MAX_CONTACTS build flag
uint16_t _filteredIdx[MAX_VISIBLE]; // indices into contact table
uint32_t _filteredTs[MAX_VISIBLE]; // cached last_advert_timestamp for sorting
// Arrays allocated in PSRAM when available (supports 1000+ contacts)
uint16_t* _filteredIdx; // indices into contact table
uint32_t* _filteredTs; // cached last_advert_timestamp for sorting
int _filteredCount; // how many contacts match current filter
bool _cacheValid;
@@ -49,7 +48,6 @@ private:
case FILTER_REPEATER: return "Rptr";
case FILTER_ROOM: return "Room";
case FILTER_SENSOR: return "Sens";
case FILTER_FAVOURITE: return "Fav";
default: return "?";
}
}
@@ -63,7 +61,7 @@ private:
}
}
bool matchesFilter(uint8_t adv_type, uint8_t flags = 0) const {
bool matchesFilter(uint8_t adv_type) const {
switch (_filter) {
case FILTER_ALL: return true;
case FILTER_CHAT: return adv_type == ADV_TYPE_CHAT;
@@ -72,7 +70,6 @@ private:
case FILTER_SENSOR: return (adv_type != ADV_TYPE_CHAT &&
adv_type != ADV_TYPE_REPEATER &&
adv_type != ADV_TYPE_ROOM);
case FILTER_FAVOURITE: return (flags & 0x01) != 0;
default: return true;
}
}
@@ -81,9 +78,9 @@ private:
_filteredCount = 0;
uint32_t numContacts = the_mesh.getNumContacts();
ContactInfo contact;
for (uint32_t i = 0; i < numContacts && _filteredCount < MAX_VISIBLE; i++) {
for (uint32_t i = 0; i < numContacts && _filteredCount < MAX_CONTACTS; i++) {
if (the_mesh.getContactByIdx(i, contact)) {
if (matchesFilter(contact.type, contact.flags)) {
if (matchesFilter(contact.type)) {
_filteredIdx[_filteredCount] = (uint16_t)i;
_filteredTs[_filteredCount] = contact.last_advert_timestamp;
_filteredCount++;
@@ -91,7 +88,7 @@ private:
}
}
// Sort by last_advert_timestamp descending (most recently seen first)
// Simple insertion sort — fine for up to 400 entries on ESP32
// Insertion sort — fine for up to ~1000 entries on ESP32
for (int i = 1; i < _filteredCount; i++) {
uint16_t tmpIdx = _filteredIdx[i];
uint32_t tmpTs = _filteredTs[i];
@@ -133,7 +130,15 @@ private:
public:
ContactsScreen(UITask* task, mesh::RTCClock* rtc)
: _task(task), _rtc(rtc), _scrollPos(0), _filter(FILTER_ALL),
_filteredCount(0), _cacheValid(false), _rowsPerPage(5) {}
_filteredCount(0), _cacheValid(false), _rowsPerPage(5) {
#if defined(ESP32) && defined(BOARD_HAS_PSRAM)
_filteredIdx = (uint16_t*)ps_calloc(MAX_CONTACTS, sizeof(uint16_t));
_filteredTs = (uint32_t*)ps_calloc(MAX_CONTACTS, sizeof(uint32_t));
#else
_filteredIdx = new uint16_t[MAX_CONTACTS]();
_filteredTs = new uint32_t[MAX_CONTACTS]();
#endif
}
void invalidateCache() { _cacheValid = false; }
@@ -289,17 +294,17 @@ public:
display.drawRect(0, footerY - 2, display.width(), 1);
display.setColor(DisplayDriver::YELLOW);
// Left: Q:Back
// Left: Q:Bk X:Exp
display.setCursor(0, footerY);
display.print("Q:Back");
display.print("Q:Bk X:Exp");
// Center: A/D:Filter
const char* mid = "A/D:Filtr";
display.setCursor((display.width() - display.getTextWidth(mid)) / 2, footerY);
display.print(mid);
// Right: W/S:Scroll
const char* right = "W/S:Scrll";
// Right: R:Imp W/S
const char* right = "R:Imp W/S";
display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY);
display.print(right);

View File

@@ -1524,6 +1524,9 @@ void UITask::gotoWebReader() {
if (_display != NULL) {
wr->enter(*_display);
}
// Heap diagnostic — check state after web reader entry (WiFi connects later)
Serial.printf("[HEAP] WebReader enter - free: %u, largest: %u, PSRAM: %u\n",
ESP.getFreeHeap(), ESP.getMaxAllocHeap(), ESP.getFreePsram());
setCurrScreen(web_reader);
if (_display != NULL && !_display->isOn()) {
_display->turnOn();

View File

@@ -58,9 +58,9 @@ class BaseChatMesh : public mesh::Mesh {
friend class ContactsIterator;
ContactInfo contacts[MAX_CONTACTS];
ContactInfo* contacts;
int num_contacts;
int sort_array[MAX_CONTACTS];
int* sort_array;
int matching_peer_indexes[MAX_SEARCH_RESULTS];
unsigned long txt_send_timeout;
#ifdef MAX_GROUP_CHANNELS
@@ -78,6 +78,8 @@ protected:
BaseChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::PacketManager& mgr, mesh::MeshTables& tables)
: mesh::Mesh(radio, ms, rng, rtc, mgr, tables)
{
contacts = NULL;
sort_array = NULL;
num_contacts = 0;
#ifdef MAX_GROUP_CHANNELS
memset(channels, 0, sizeof(channels));
@@ -90,6 +92,19 @@ protected:
void bootstrapRTCfromContacts();
void resetContacts() { num_contacts = 0; }
// Must be called from begin() before loadContacts/bootstrapRTCfromContacts.
// Deferred from constructor because PSRAM is not available during global init.
void initContacts() {
if (contacts != NULL) return; // already initialized
#if defined(ESP32) && defined(BOARD_HAS_PSRAM)
contacts = (ContactInfo*)ps_calloc(MAX_CONTACTS, sizeof(ContactInfo));
sort_array = (int*)ps_calloc(MAX_CONTACTS, sizeof(int));
#else
contacts = new ContactInfo[MAX_CONTACTS]();
sort_array = new int[MAX_CONTACTS]();
#endif
}
void populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp);
ContactInfo* allocateContactSlot(); // helper to find slot for new contact
@@ -169,4 +184,4 @@ public:
int findChannelIdx(const mesh::GroupChannel& ch);
void loop();
};
};

View File

@@ -96,12 +96,13 @@ lib_deps =
; ---------------------------------------------------------------------------
; Audio + BLE companion (audio-player hardware with BLE phone bridging)
; MAX_CONTACTS=500 is near BLE protocol ceiling (MAX_CONTACTS/2 sent as uint8_t, max 510)
[env:meck_audio_ble]
extends = LilyGo_TDeck_Pro
build_flags =
${LilyGo_TDeck_Pro.build_flags}
-I examples/companion_radio/ui-new
-D MAX_CONTACTS=400
-D MAX_CONTACTS=500
-D MAX_GROUP_CHANNELS=20
-D BLE_PIN_CODE=123456
-D OFFLINE_QUEUE_SIZE=256
@@ -121,13 +122,14 @@ lib_deps =
; Audio standalone (audio-player hardware, no BLE/WiFi — maximum battery life)
; No MECK_WEB_READER: WiFi power draw conflicts with zero-radio-power design.
; Contacts and sort arrays allocated in PSRAM — 1500 contacts uses ~290KB of 8MB.
[env:meck_audio_standalone]
extends = LilyGo_TDeck_Pro
build_flags =
${LilyGo_TDeck_Pro.build_flags}
-I examples/companion_radio/ui-new
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D MAX_CONTACTS=1500
-D MAX_GROUP_CHANNELS=20
-D OFFLINE_QUEUE_SIZE=256
-D MECK_AUDIO_VARIANT
build_src_filter = ${LilyGo_TDeck_Pro.build_src_filter}
@@ -143,12 +145,13 @@ lib_deps =
bitbank2/JPEGDEC
; 4G + BLE companion (4G modem hardware, no audio — GPIO conflict with PCM5102A)
; MAX_CONTACTS=500 is near BLE protocol ceiling (MAX_CONTACTS/2 sent as uint8_t, max 510)
[env:meck_4g_ble]
extends = LilyGo_TDeck_Pro
build_flags =
${LilyGo_TDeck_Pro.build_flags}
-I examples/companion_radio/ui-new
-D MAX_CONTACTS=400
-D MAX_CONTACTS=500
-D MAX_GROUP_CHANNELS=20
-D BLE_PIN_CODE=123456
-D OFFLINE_QUEUE_SIZE=256
@@ -169,13 +172,14 @@ lib_deps =
; No BLE_PIN_CODE: BLE never initializes, saving ~30KB heap + radio power.
; MECK_WEB_READER enabled: works better without BLE (no teardown dance needed,
; more free heap from boot). WiFi-first with cellular PPP fallback (future).
; Contacts and sort arrays allocated in PSRAM — 1500 contacts uses ~290KB of 8MB.
[env:meck_4g_standalone]
extends = LilyGo_TDeck_Pro
build_flags =
${LilyGo_TDeck_Pro.build_flags}
-I examples/companion_radio/ui-new
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=40
-D MAX_CONTACTS=1500
-D MAX_GROUP_CHANNELS=20
-D OFFLINE_QUEUE_SIZE=256
-D HAS_4G_MODEM=1
-D MECK_WEB_READER=1