updated chunked save method so it doesn't occur when device is actively being used. The flow: user navigates (keypresses every ~200ms) → _lastUserInput stays fresh → userActive is true → save deferred. User stops navigating → 3 seconds pass → userActive goes false → chunked save starts/resumes, 20 contacts per loop iteration until done.

This commit is contained in:
pelgraine
2026-05-02 10:44:17 +10:00
parent 95c992d966
commit 97498e131d
3 changed files with 17 additions and 6 deletions

View File

@@ -114,6 +114,7 @@
#define DIRECT_SEND_PERHOP_FACTOR 6.0f
#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250
#define LAZY_CONTACTS_WRITE_DELAY 5000
#define USER_IDLE_SAVE_THRESHOLD 15000 // Defer saves until 15s after last keypress
#define PUBLIC_GROUP_PSK "izOH6cXN6mrJ5e26oRXNcg=="
@@ -3462,18 +3463,19 @@ void MyMesh::loop() {
}
// is there are pending dirty contacts write needed?
bool userActive = _lastUserInput && (millis() - _lastUserInput) < USER_IDLE_SAVE_THRESHOLD;
if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) {
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
// nRF52/STM32: blocking save (fast on internal flash, no chunking needed)
if (!_deferSaves) {
if (!_deferSaves && !userActive) {
_store->saveContacts(this);
dirty_contacts_expiry = 0;
} else {
dirty_contacts_expiry = futureMillis(2000);
}
#else
if (_deferSaves) {
// Voice session receiving — push save forward to avoid SPI contention
if (_deferSaves || userActive) {
// Voice session or active keyboard use -- push save forward
dirty_contacts_expiry = futureMillis(2000);
} else if (!_store->isSaveInProgress()) {
_store->beginSaveContacts(this);
@@ -3483,10 +3485,11 @@ void MyMesh::loop() {
}
#if !defined(NRF52_PLATFORM) && !defined(STM32_PLATFORM)
// Drive chunked contact save write a batch each loop iteration
if (_store->isSaveInProgress() && !_deferSaves) {
// Drive chunked contact save -- write a batch each loop iteration
// Paused while user is actively pressing keys or voice session is receiving
if (_store->isSaveInProgress() && !_deferSaves && !userActive) {
if (!_store->saveContactsChunk(20)) { // 20 contacts per chunk (~3KB, ~30ms)
_store->finishSaveContacts(); // Done or error verify and commit
_store->finishSaveContacts(); // Done or error -- verify and commit
}
}
#endif

View File

@@ -160,6 +160,10 @@ public:
void setDeferSaves(bool defer) { _deferSaves = defer; }
bool isDeferSaves() const { return _deferSaves; }
// Notify that the user pressed a key — defers contact saves until idle.
// Call from main.cpp keyboard handler on every keypress.
void notifyUserInput() { _lastUserInput = millis(); }
// Repeater admin - UI-initiated operations
bool uiLoginToRepeater(uint32_t contact_idx, const char* password, uint32_t& est_timeout_ms);
bool uiSendCliCommand(uint32_t contact_idx, const char* command);
@@ -274,6 +278,7 @@ private:
VoiceEnvelopeHandler _voiceEnvHandler = nullptr;
mutable bool _forceNextImport = false;
bool _deferSaves = false;
unsigned long _lastUserInput = 0; // millis() of last keypress -- defer saves until idle
uint32_t pending_login;
uint32_t pending_status;
uint32_t pending_telemetry, pending_discovery; // pending _TELEMETRY_REQ

View File

@@ -3619,6 +3619,9 @@ void handleKeyboardInput() {
Serial.printf("handleKeyboardInput: key='%c' (0x%02X) composeMode=%d\n",
key >= 32 ? key : '?', key, composeMode);
// Defer contact saves while user is actively pressing keys
the_mesh.notifyUserInput();
// Alarm ringing: ANY key dismisses (highest priority after lock screen)
#ifdef MECK_AUDIO_VARIANT
{