diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 8cdf1406..90ca2c2d 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -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 diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index a823e398..b95cfb11 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -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 diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 9a073c15..d8bd365b 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -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 {