proper discover node function working

This commit is contained in:
pelgraine
2026-03-04 18:55:13 +11:00
parent 7685de4be6
commit 33f2e0fc6e
3 changed files with 101 additions and 19 deletions

View File

@@ -58,6 +58,10 @@
#define CMD_SET_FLOOD_SCOPE 54 // v8+
#define CMD_SEND_CONTROL_DATA 55 // v8+
#define CMD_GET_STATS 56 // v8+, second byte is stats type
// Control data sub-types for active node discovery
#define CTL_TYPE_NODE_DISCOVER_REQ 0x80
#define CTL_TYPE_NODE_DISCOVER_RESP 0x90
#define CMD_SEND_ANON_REQ 57
#define CMD_SET_AUTOADD_CONFIG 58
#define CMD_GET_AUTOADD_CONFIG 59
@@ -371,6 +375,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
_discovered[i].contact = contact;
_discovered[i].path_len = path_len;
_discovered[i].already_in_contacts = !is_new;
// Preserve snr if already set by active discovery response
dup = true;
Serial.printf("[Discovery] Updated: %s (hops=%d)\n", contact.name, path_len);
break;
@@ -379,6 +384,7 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
if (!dup) {
_discovered[_discoveredCount].contact = contact;
_discovered[_discoveredCount].path_len = path_len;
_discovered[_discoveredCount].snr = 0; // no SNR from passive advert
_discovered[_discoveredCount].already_in_contacts = !is_new;
_discoveredCount++;
Serial.printf("[Discovery] Found: %s (hops=%d, is_new=%d, total=%d)\n",
@@ -931,6 +937,62 @@ bool MyMesh::onContactPathRecv(ContactInfo& contact, uint8_t* in_path, uint8_t i
}
void MyMesh::onControlDataRecv(mesh::Packet *packet) {
// --- Active discovery response interception ---
if (_discoveryActive && packet->payload_len >= 6) {
uint8_t resp_type = packet->payload[0] & 0xF0;
if (resp_type == CTL_TYPE_NODE_DISCOVER_RESP) {
uint8_t node_type = packet->payload[0] & 0x0F;
int8_t snr_scaled = (int8_t)packet->payload[1]; // SNR × 4 (how well repeater heard us)
uint32_t tag;
memcpy(&tag, &packet->payload[2], 4);
// Validate: tag must match ours AND payload must include full 32-byte pubkey
if (tag == _discoveryTag && packet->payload_len >= 6 + PUB_KEY_SIZE) {
const uint8_t* pubkey = &packet->payload[6];
// Dedup check against existing buffer entries (pre-seeded or earlier responses)
for (int i = 0; i < _discoveredCount; i++) {
if (_discovered[i].contact.id.matches(pubkey)) {
// Already in buffer — update SNR (active discovery data is fresher)
_discovered[i].snr = snr_scaled;
Serial.printf("[Discovery] Updated SNR for %s: %d\n",
_discovered[i].contact.name, snr_scaled);
return;
}
}
// New node — add if room
if (_discoveredCount < MAX_DISCOVERED_NODES) {
DiscoveredNode& node = _discovered[_discoveredCount];
memset(&node.contact, 0, sizeof(ContactInfo));
memcpy(node.contact.id.pub_key, pubkey, PUB_KEY_SIZE);
node.contact.type = node_type;
node.snr = snr_scaled;
node.path_len = packet->path_len;
// Try to resolve name from contacts table
ContactInfo* existing = lookupContactByPubKey(pubkey, PUB_KEY_SIZE);
if (existing) {
strncpy(node.contact.name, existing->name, sizeof(node.contact.name) - 1);
node.already_in_contacts = true;
} else {
// Show hex prefix as placeholder name
snprintf(node.contact.name, sizeof(node.contact.name),
"%02X%02X%02X%02X",
pubkey[0], pubkey[1], pubkey[2], pubkey[3]);
node.already_in_contacts = false;
}
_discoveredCount++;
Serial.printf("[Discovery] Active response: %s type=%d snr=%d hops=%d (total=%d)\n",
node.contact.name, node_type, snr_scaled, packet->path_len, _discoveredCount);
}
}
return; // consumed — don't forward discovery responses to BLE
}
}
// --- Original BLE forwarding for non-discovery control data ---
if (packet->payload_len + 4 > sizeof(out_frame)) {
MESH_DEBUG_PRINTLN("onControlDataRecv(), payload_len too long: %d", packet->payload_len);
return;
@@ -1030,6 +1092,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_discoveredCount = 0;
_discoveryActive = false;
_discoveryTimeout = 0;
_discoveryTag = 0;
// defaults
memset(&_prefs, 0, sizeof(_prefs));
@@ -2695,36 +2758,42 @@ void MyMesh::startDiscovery(uint32_t duration_ms) {
_discoveredCount = 0;
_discoveryActive = true;
_discoveryTimeout = futureMillis(duration_ms);
Serial.printf("[Discovery] Scan started (%lu ms)\n", duration_ms);
_discoveryTag = getRNG()->nextInt(1, 0xFFFFFFFF);
// Pre-seed from advert_paths cache (nodes heard recently, before scan started)
Serial.printf("[Discovery] Active scan started (%lu ms, tag=%08X)\n",
duration_ms, _discoveryTag);
// --- Pre-seed from advert_paths cache (shows known nodes immediately) ---
for (int i = 0; i < ADVERT_PATH_TABLE_SIZE && _discoveredCount < MAX_DISCOVERED_NODES; i++) {
if (advert_paths[i].recv_timestamp == 0) continue; // empty slot
// Look up full contact info by pubkey prefix
ContactInfo* c = lookupContactByPubKey(advert_paths[i].pubkey_prefix, sizeof(advert_paths[i].pubkey_prefix));
if (c) {
_discovered[_discoveredCount].contact = *c;
_discovered[_discoveredCount].path_len = advert_paths[i].path_len;
_discovered[_discoveredCount].snr = 0; // no SNR from cache
_discovered[_discoveredCount].already_in_contacts = true;
_discoveredCount++;
}
}
Serial.printf("[Discovery] Pre-seeded %d nodes from cache\n", _discoveredCount);
// Flood self-advert through mesh (not zero-hop) so repeaters
// multiple hops away hear it and respond with their own adverts
mesh::Packet* pkt;
if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) {
pkt = createSelfAdvert(_prefs.node_name);
} else {
pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon);
}
// --- Send active discovery request (CTL_TYPE_NODE_DISCOVER_REQ) ---
// Repeaters with firmware v1.11+ will respond with their pubkey + SNR
uint8_t ctl_payload[10];
ctl_payload[0] = CTL_TYPE_NODE_DISCOVER_REQ; // 0x80, prefix_only=0 (full 32-byte pubkeys)
ctl_payload[1] = (1 << ADV_TYPE_REPEATER) // repeaters
| (1 << ADV_TYPE_ROOM); // rooms (repeaters with chat)
memcpy(&ctl_payload[2], &_discoveryTag, 4); // random correlation tag
uint32_t since = 0; // accept all firmware versions
memcpy(&ctl_payload[6], &since, 4);
auto pkt = createControlData(ctl_payload, sizeof(ctl_payload));
if (pkt) {
sendFlood(pkt);
Serial.println("[Discovery] Self-advert flooded");
sendZeroHop(pkt);
Serial.println("[Discovery] Sent CTL_TYPE_NODE_DISCOVER_REQ (zero-hop)");
} else {
Serial.println("[Discovery] ERROR: createSelfAdvert returned NULL (packet pool full?)");
Serial.println("[Discovery] ERROR: createControlData returned NULL (packet pool full?)");
}
}

View File

@@ -90,6 +90,7 @@ struct AdvertPath {
struct DiscoveredNode {
ContactInfo contact;
uint8_t path_len;
int8_t snr; // SNR × 4 from active discovery response (0 if pre-seeded)
bool already_in_contacts; // true if contact was auto-added or already known
};
@@ -280,6 +281,7 @@ private:
int _discoveredCount;
bool _discoveryActive;
unsigned long _discoveryTimeout;
uint32_t _discoveryTag; // random correlation tag for active discovery
};
extern MyMesh the_mesh;

View File

@@ -113,12 +113,23 @@ public:
}
display.print(prefix);
// Build right-side info: hop count + status
char rightStr[12];
if (node.already_in_contacts) {
snprintf(rightStr, sizeof(rightStr), "%dh [+]", node.path_len);
// Build right-side info: SNR or hop count + status
char rightStr[16];
if (node.snr != 0) {
// Active discovery result — show SNR in dB (value is ×4 scaled)
int snr_db = node.snr / 4;
if (node.already_in_contacts) {
snprintf(rightStr, sizeof(rightStr), "%ddB [+]", snr_db);
} else {
snprintf(rightStr, sizeof(rightStr), "%ddB", snr_db);
}
} else {
snprintf(rightStr, sizeof(rightStr), "%dh", node.path_len);
// Pre-seeded from cache — show hop count
if (node.already_in_contacts) {
snprintf(rightStr, sizeof(rightStr), "%dh [+]", node.path_len);
} else {
snprintf(rightStr, sizeof(rightStr), "%dh", node.path_len);
}
}
int rightWidth = display.getTextWidth(rightStr) + 2;