touch: beta_47 — Discover app (find/list/map nearby nodes + wardriving), Attaky release wiring, P4 LCD

- New Discover app: active NODE_DISCOVER sweep -> signal-ranked nearby list, tap-to-add-contact, GPS wardriving log (SD CSV) + signal-coloured coverage overlay on the map. Board-agnostic.
- T-Display P4 TFT-LCD (HI8561) variant support (WADA_P4_LCD build; AMOLED bin untouched).
- Wire the Attaky Core board (#158, @attakygit) into the release matrix + web flasher.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kaj Schittecat
2026-07-19 17:45:05 +02:00
co-authored by Claude Opus 4.8
parent cab0c81208
commit 99423e5bf8
16 changed files with 1442 additions and 30 deletions
+48
View File
@@ -2401,6 +2401,22 @@ void MyMesh::onControlDataRecv(mesh::Packet *packet) {
_ui_sig_ms = millis();
}
}
// Discover scan (Discover app): collect EVERY NODE_DISCOVER_RESP matching the active discover
// sweep tag into _discover[], keyed by responder pubkey. Additive to the single-scalar probe
// capture above (they use different tags). RESP payload (MeshCore docs/payloads.md):
// [0]=0x9<<4|node_type [1]=their SNR*4 (reverse link) [2..5]=tag [6..]=pubkey (8 or 32 bytes).
if (packet->payload_len >= 6 + 8 && (packet->payload[0] & 0xF0) == CTL_TYPE_NODE_DISCOVER_RESP
&& _discover_tag != 0) {
uint32_t rtag; memcpy(&rtag, &packet->payload[2], 4);
if (rtag == _discover_tag) {
uint8_t node_type = packet->payload[0] & 0x0F;
int8_t their_snr_q4 = (int8_t)packet->payload[1];
uint8_t pklen = (packet->payload_len >= 6 + 32) ? 32 : 8;
int8_t our_snr_q4 = (int8_t)(packet->getSNR() * 4.0f);
int8_t our_rssi = (int8_t)_radio->getLastRSSI();
discoverUpsert(&packet->payload[6], pklen, node_type, our_snr_q4, our_rssi, their_snr_q4, packet->path_len);
}
}
if (packet->payload_len + 4 > sizeof(out_frame)) {
MESH_DEBUG_PRINTLN("onControlDataRecv(), payload_len too long: %d", packet->payload_len);
return;
@@ -2420,6 +2436,38 @@ void MyMesh::onControlDataRecv(mesh::Packet *packet) {
}
}
// Upsert a discover responder into _discover[] (keyed by the 8-byte pubkey prefix). New nodes
// append; a full table evicts the least-recently-heard. Both link directions + hop count are
// refreshed on every reply. See DiscoverHit / uiStartDiscoverScan in MyMesh.h.
void MyMesh::discoverUpsert(const uint8_t* pk, uint8_t pklen, uint8_t node_type,
int8_t our_snr_q4, int8_t our_rssi, int8_t their_snr_q4, uint8_t path_len) {
uint32_t now = millis();
int slot = -1;
for (uint8_t i = 0; i < _discover_cnt; i++) {
if (memcmp(_discover[i].pubkey, pk, 8) == 0) { slot = i; break; }
}
if (slot < 0) {
if (_discover_cnt < DISCOVER_MAX) {
slot = _discover_cnt++;
} else { // table full -> evict the least-recently-heard
uint32_t oldest = 0xFFFFFFFFu; slot = 0;
for (uint8_t i = 0; i < _discover_cnt; i++)
if (_discover[i].last_ms < oldest) { oldest = _discover[i].last_ms; slot = i; }
}
memset(&_discover[slot], 0, sizeof(DiscoverHit));
memcpy(_discover[slot].pubkey, pk, pklen > 32 ? 32 : pklen);
_discover[slot].first_ms = now;
}
DiscoverHit& h = _discover[slot];
h.node_type = node_type;
h.our_snr_q4 = our_snr_q4;
h.our_rssi = our_rssi;
h.their_snr_q4 = their_snr_q4;
h.path_len = path_len;
h.last_ms = now;
if (h.heard < 0xFFFF) h.heard++;
}
void MyMesh::onRawDataRecv(mesh::Packet *packet) {
if (packet->payload_len + 4 > sizeof(out_frame)) {
MESH_DEBUG_PRINTLN("onRawDataRecv(), payload_len too long: %d", packet->payload_len);