80
80
#define FLOOD_SEND_TIMEOUT_FACTOR 16 .0f
81
81
#define DIRECT_SEND_PERHOP_FACTOR 6 .0f
82
82
#define DIRECT_SEND_PERHOP_EXTRA_MILLIS 250
83
- #define LAZY_CONTACTS_WRITE_DELAY 5000
83
+ #define CONTACTS_SAVE_QUIET_WINDOW_MS 60000 // save after this period (1 minute) of no structural changes
84
+ #define CONTACTS_SAVE_MAX_WINDOW_MS 300000 // but never delay beyond this total coalesce window (5 minutes)
84
85
85
86
#define PUBLIC_GROUP_PSK " izOH6cXN6mrJ5e26oRXNcg=="
86
87
@@ -127,6 +128,15 @@ void MyMesh::writeDisabledFrame() {
127
128
_serial->writeFrame (buf, 1 );
128
129
}
129
130
131
+ void MyMesh::markContactsChanged () {
132
+ unsigned long now = _ms->getMillis ();
133
+ contacts_last_change = now;
134
+ if (!contacts_dirty) {
135
+ contacts_dirty = true ;
136
+ contacts_first_change = now;
137
+ }
138
+ }
139
+
130
140
void MyMesh::writeContactRespFrame (uint8_t code, const ContactInfo &contact) {
131
141
int i = 0 ;
132
142
out_frame[i++] = code;
@@ -268,8 +278,6 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
268
278
p->path_len = path_len;
269
279
memcpy (p->path , path, p->path_len );
270
280
}
271
-
272
- dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY);
273
281
}
274
282
275
283
static int sort_by_recent (const void *a, const void *b) {
@@ -290,8 +298,6 @@ void MyMesh::onContactPathUpdated(const ContactInfo &contact) {
290
298
out_frame[0 ] = PUSH_CODE_PATH_UPDATED;
291
299
memcpy (&out_frame[1 ], contact.id .pub_key , PUB_KEY_SIZE);
292
300
_serial->writeFrame (out_frame, 1 + PUB_KEY_SIZE); // NOTE: app may not be connected
293
-
294
- dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY);
295
301
}
296
302
297
303
bool MyMesh::processAck (const uint8_t *data) {
@@ -374,8 +380,6 @@ void MyMesh::onCommandDataRecv(const ContactInfo &from, mesh::Packet *pkt, uint3
374
380
void MyMesh::onSignedMessageRecv (const ContactInfo &from, mesh::Packet *pkt, uint32_t sender_timestamp,
375
381
const uint8_t *sender_prefix, const char *text) {
376
382
markConnectionActive (from);
377
- // from.sync_since change needs to be persisted
378
- dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY);
379
383
queueMessage (from, TXT_TYPE_SIGNED_PLAIN, pkt, sender_timestamp, sender_prefix, 4 , text);
380
384
}
381
385
@@ -641,7 +645,8 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
641
645
clearPendingReqs ();
642
646
next_ack_idx = 0 ;
643
647
sign_data = NULL ;
644
- dirty_contacts_expiry = 0 ;
648
+ contacts_dirty = false ;
649
+ contacts_last_change = contacts_first_change = 0 ;
645
650
memset (advert_paths, 0 , sizeof (advert_paths));
646
651
647
652
// defaults
@@ -940,8 +945,6 @@ void MyMesh::handleCmdFrame(size_t len) {
940
945
ContactInfo *recipient = lookupContactByPubKey (pub_key, PUB_KEY_SIZE);
941
946
if (recipient) {
942
947
recipient->out_path_len = -1 ;
943
- // recipient->lastmod = ?? shouldn't be needed, app already has this version of contact
944
- dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY);
945
948
writeOKFrame ();
946
949
} else {
947
950
writeErrFrame (ERR_CODE_NOT_FOUND); // unknown contact
@@ -953,15 +956,15 @@ void MyMesh::handleCmdFrame(size_t len) {
953
956
if (recipient) {
954
957
updateContactFromFrame (*recipient, last_mod, cmd_frame, len);
955
958
recipient->lastmod = last_mod;
956
- dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY );
959
+ markContactsChanged ( );
957
960
writeOKFrame ();
958
961
} else {
959
962
ContactInfo contact;
960
963
updateContactFromFrame (contact, last_mod, cmd_frame, len);
961
964
contact.lastmod = last_mod;
962
965
contact.sync_since = 0 ;
963
966
if (addContact (contact)) {
964
- dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY );
967
+ markContactsChanged ( );
965
968
writeOKFrame ();
966
969
} else {
967
970
writeErrFrame (ERR_CODE_TABLE_FULL);
@@ -971,7 +974,7 @@ void MyMesh::handleCmdFrame(size_t len) {
971
974
uint8_t *pub_key = &cmd_frame[1 ];
972
975
ContactInfo *recipient = lookupContactByPubKey (pub_key, PUB_KEY_SIZE);
973
976
if (recipient && removeContact (*recipient)) {
974
- dirty_contacts_expiry = futureMillis (LAZY_CONTACTS_WRITE_DELAY );
977
+ markContactsChanged ( );
975
978
writeOKFrame ();
976
979
} else {
977
980
writeErrFrame (ERR_CODE_NOT_FOUND); // not found, or unable to remove
@@ -1116,9 +1119,7 @@ void MyMesh::handleCmdFrame(size_t len) {
1116
1119
savePrefs ();
1117
1120
writeOKFrame ();
1118
1121
} else if (cmd_frame[0 ] == CMD_REBOOT && memcmp (&cmd_frame[1 ], " reboot" , 6 ) == 0 ) {
1119
- if (dirty_contacts_expiry) { // is there are pending dirty contacts write needed?
1120
- saveContacts ();
1121
- }
1122
+ saveContactsIfDirty ();
1122
1123
board.reboot ();
1123
1124
} else if (cmd_frame[0 ] == CMD_GET_BATT_AND_STORAGE) {
1124
1125
uint8_t reply[11 ];
@@ -1587,6 +1588,7 @@ void MyMesh::checkCLIRescueCmd() {
1587
1588
}
1588
1589
1589
1590
} else if (strcmp (cli_command, " reboot" ) == 0 ) {
1591
+ saveContactsIfDirty ();
1590
1592
board.reboot (); // doesn't return
1591
1593
} else {
1592
1594
Serial.println (" Error: unknown command" );
@@ -1632,10 +1634,16 @@ void MyMesh::loop() {
1632
1634
checkSerialInterface ();
1633
1635
}
1634
1636
1635
- // is there are pending dirty contacts write needed?
1636
- if (dirty_contacts_expiry && millisHasNowPassed (dirty_contacts_expiry)) {
1637
- saveContacts ();
1638
- dirty_contacts_expiry = 0 ;
1637
+ // Debounced save
1638
+ if (contacts_dirty) {
1639
+ unsigned long now = _ms->getMillis ();
1640
+ bool quiet_met = (now - contacts_last_change) >= CONTACTS_SAVE_QUIET_WINDOW_MS;
1641
+ bool max_reached = (now - contacts_first_change) >= CONTACTS_SAVE_MAX_WINDOW_MS;
1642
+ if (quiet_met || max_reached) {
1643
+ saveContacts ();
1644
+ contacts_dirty = false ;
1645
+ contacts_last_change = contacts_first_change = 0 ;
1646
+ }
1639
1647
}
1640
1648
1641
1649
#ifdef DISPLAY_CLASS
0 commit comments