Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion examples/companion_radio/DataStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,18 @@ File file = openRead(_getContactsChannelsFS(), "/contacts3");
}
}

void DataStore::saveContacts(DataStoreHost* host) {
void DataStore::saveContacts(DataStoreHost* host, bool (*filter)(const ContactInfo& c)) {
File file = openWrite(_getContactsChannelsFS(), "/contacts3");
if (file) {
uint32_t idx = 0;
ContactInfo c;
uint8_t unused = 0;

while (host->getContactForSave(idx, c)) {
if (filter && !filter(c)) {
idx++; // advance to next contact
continue;
}
bool success = (file.write(c.id.pub_key, 32) == 32);
success = success && (file.write((uint8_t *)&c.name, 32) == 32);
success = success && (file.write(&c.type, 1) == 1);
Expand Down
2 changes: 1 addition & 1 deletion examples/companion_radio/DataStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class DataStore {
void loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon);
void savePrefs(const NodePrefs& prefs, double node_lat, double node_lon);
void loadContacts(DataStoreHost* host);
void saveContacts(DataStoreHost* host);
void saveContacts(DataStoreHost* host, bool (*filter)(const ContactInfo& c) = NULL);
void loadChannels(DataStoreHost* host);
void saveChannels(DataStoreHost* host);
void migrateToSecondaryFS();
Expand Down
29 changes: 27 additions & 2 deletions examples/companion_radio/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,15 @@ void MyMesh::handleCmdFrame(size_t len) {
} else if (cmd_frame[0] == CMD_SEND_ANON_REQ && len > 1 + PUB_KEY_SIZE) {
uint8_t *pub_key = &cmd_frame[1];
ContactInfo *recipient = lookupContactByPubKey(pub_key, PUB_KEY_SIZE);
ContactInfo anon;
if (recipient == NULL) { // FIRMWARE_VER_CODE 13+, allow non-contact requests
memset(&anon, 0, sizeof(anon));
memcpy(anon.id.pub_key, pub_key, PUB_KEY_SIZE);
anon.out_path_len = 0; // default to zero-hop direct
anon.type = ADV_TYPE_NONE; // unknown

if (addContact(anon)) recipient = &anon;
}
uint8_t *data = &cmd_frame[1 + PUB_KEY_SIZE];
if (recipient) {
uint32_t tag, est_timeout;
Expand All @@ -1552,7 +1561,7 @@ void MyMesh::handleCmdFrame(size_t len) {
_serial->writeFrame(out_frame, 10);
}
} else {
writeErrFrame(ERR_CODE_NOT_FOUND); // contact not found
writeErrFrame(ERR_CODE_TABLE_FULL); // contacts full
}
} else if (cmd_frame[0] == CMD_SEND_STATUS_REQ && len >= 1 + PUB_KEY_SIZE) {
uint8_t *pub_key = &cmd_frame[1];
Expand Down Expand Up @@ -1983,6 +1992,14 @@ void MyMesh::handleCmdFrame(size_t len) {
}
}

static bool save_filter(const ContactInfo& c) {
return c.type != ADV_TYPE_NONE; // don't save the transient/anon entries
}

void MyMesh::saveContacts() {
_store->saveContacts(this, save_filter);
}

void MyMesh::enterCLIRescue() {
_cli_rescue = true;
cli_command[0] = 0;
Expand Down Expand Up @@ -2169,7 +2186,15 @@ void MyMesh::checkSerialInterface() {
&& !_serial->isWriteBusy() // don't spam the Serial Interface too quickly!
) {
ContactInfo contact;
if (_iter.hasNext(this, contact)) {
bool found = false;
while (_iter.hasNext(this, contact)) {
if (contact.type != ADV_TYPE_NONE) {
found = true;
break;
}
}

if (found) {
if (contact.lastmod > _iter_filter_since) { // apply the 'since' filter
writeContactRespFrame(RESP_CODE_CONTACT, contact);
if (contact.lastmod > _most_recent_lastmod) {
Expand Down
4 changes: 2 additions & 2 deletions examples/companion_radio/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "AbstractUITask.h"

/*------------ Frame Protocol --------------*/
#define FIRMWARE_VER_CODE 12
#define FIRMWARE_VER_CODE 13

#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "19 Apr 2026"
Expand Down Expand Up @@ -201,7 +201,7 @@ class MyMesh : public BaseChatMesh, public DataStoreHost {

// helpers, short-cuts
void saveChannels() { _store->saveChannels(this); }
void saveContacts() { _store->saveContacts(this); }
void saveContacts();

DataStore* _store;
NodePrefs _prefs;
Expand Down
40 changes: 24 additions & 16 deletions src/helpers/BaseChatMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,25 @@ void BaseChatMesh::bootstrapRTCfromContacts() {
}
}

ContactInfo* BaseChatMesh::allocateContactSlot() {
ContactInfo* BaseChatMesh::allocateContactSlot(bool transient_only) {
if (num_contacts < MAX_CONTACTS) {
return &contacts[num_contacts++];
} else if (shouldOverwriteWhenFull()) {
} else if (transient_only || shouldOverwriteWhenFull()) {
// Find oldest non-favourite contact by oldest lastmod timestamp
int oldest_idx = -1;
uint32_t oldest_lastmod = 0xFFFFFFFF;
for (int i = 0; i < num_contacts; i++) {
bool is_favourite = (contacts[i].flags & 0x01) != 0;
if (!is_favourite && contacts[i].lastmod < oldest_lastmod) {
oldest_lastmod = contacts[i].lastmod;
oldest_idx = i;
if (transient_only) {
if (contacts[i].type == ADV_TYPE_NONE && contacts[i].lastmod < oldest_lastmod) {
oldest_lastmod = contacts[i].lastmod;
oldest_idx = i;
}
} else {
bool is_favourite = (contacts[i].flags & 0x01) != 0;
if (!is_favourite && contacts[i].lastmod < oldest_lastmod && contacts[i].type != ADV_TYPE_NONE) {
oldest_lastmod = contacts[i].lastmod;
oldest_idx = i;
}
}
}
if (oldest_idx >= 0) {
Expand Down Expand Up @@ -164,16 +171,17 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id,
from->sync_since = 0;
from->shared_secret_valid = false;
}

// update
putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen);
StrHelper::strncpy(from->name, parser.getName(), sizeof(from->name));
from->type = parser.getType();
if (parser.hasLatLon()) {
from->gps_lat = parser.getIntLat();
from->gps_lon = parser.getIntLon();
}
from->last_advert_timestamp = timestamp;
from->lastmod = getRTCClock()->getCurrentTime();
putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen);
StrHelper::strncpy(from->name, parser.getName(), sizeof(from->name));
from->type = parser.getType();
if (parser.hasLatLon()) {
from->gps_lat = parser.getIntLat();
from->gps_lon = parser.getIntLon();
}
from->last_advert_timestamp = timestamp;
from->lastmod = getRTCClock()->getCurrentTime();

onDiscoveredContact(*from, is_new, packet->path_len, packet->path); // let UI know
}
Expand Down Expand Up @@ -829,7 +837,7 @@ ContactInfo* BaseChatMesh::lookupContactByPubKey(const uint8_t* pub_key, int pre
}

bool BaseChatMesh::addContact(const ContactInfo& contact) {
ContactInfo* dest = allocateContactSlot();
ContactInfo* dest = allocateContactSlot(contact.type == ADV_TYPE_NONE);
if (dest) {
*dest = contact;
dest->shared_secret_valid = false; // mark shared_secret as needing calculation
Expand Down
8 changes: 5 additions & 3 deletions src/helpers/BaseChatMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class ContactsIterator {
#define MAX_CONTACTS 32
#endif

#define MAX_ANON_CONTACTS 8

#ifndef MAX_CONNECTIONS
#define MAX_CONNECTIONS 16
#endif
Expand All @@ -58,9 +60,9 @@ class BaseChatMesh : public mesh::Mesh {

friend class ContactsIterator;

ContactInfo contacts[MAX_CONTACTS];
ContactInfo contacts[MAX_CONTACTS+MAX_ANON_CONTACTS];
int num_contacts;
int sort_array[MAX_CONTACTS];
int sort_array[MAX_CONTACTS+MAX_ANON_CONTACTS];
int matching_peer_indexes[MAX_SEARCH_RESULTS];
unsigned long txt_send_timeout;
#ifdef MAX_GROUP_CHANNELS
Expand Down Expand Up @@ -91,7 +93,7 @@ class BaseChatMesh : public mesh::Mesh {
void bootstrapRTCfromContacts();
void resetContacts() { num_contacts = 0; }
void populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp);
ContactInfo* allocateContactSlot(); // helper to find slot for new contact
ContactInfo* allocateContactSlot(bool transient_only=false); // helper to find slot for new contact

// 'UI' concepts, for sub-classes to implement
virtual bool isAutoAddEnabled() const { return true; }
Expand Down
Loading