feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
#pragma once
|
|
|
|
|
#include <math.h>
|
2026-06-02 23:51:08 +02:00
|
|
|
#include "../GeoUtils.h"
|
2026-06-03 08:32:32 +02:00
|
|
|
#include "NavView.h"
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
|
|
|
|
#ifndef M_PI
|
|
|
|
|
#define M_PI 3.14159265358979323846
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// ── Nearby Nodes ──────────────────────────────────────────────────────────────
|
|
|
|
|
// One list / detail / action-menu interaction path over two sources:
|
|
|
|
|
// SRC_STORED — contacts known to the mesh (distance / bearing / last-heard)
|
|
|
|
|
// SRC_SCAN — live NODE_DISCOVER_REQ results (RSSI / SNR / status)
|
|
|
|
|
// Filter (type) and sort are independent axes and combine freely. The action
|
|
|
|
|
// menu (Hold Enter) is identical everywhere; only the per-row column and the
|
|
|
|
|
// detail fields differ between sources.
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
class NearbyScreen : public UIScreen {
|
|
|
|
|
UITask* _task;
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// ── filter (type axis) ──────────────────────────────────────────────────────
|
|
|
|
|
enum Filter : uint8_t { F_ALL, F_FAV, F_COMP, F_RPT, F_ROOM, F_SNSR, F_COUNT };
|
|
|
|
|
static const char* FILTER_LABELS[F_COUNT];
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// ── sort axis ───────────────────────────────────────────────────────────────
|
|
|
|
|
enum Sort : uint8_t { SORT_DIST, SORT_TIME };
|
|
|
|
|
|
|
|
|
|
// ── source ──────────────────────────────────────────────────────────────────
|
|
|
|
|
enum Source : uint8_t { SRC_STORED, SRC_SCAN };
|
|
|
|
|
|
|
|
|
|
// ── action-menu actions (matched by id, not by row index) ────────────────────
|
|
|
|
|
enum Action : uint8_t { ACT_NAV, ACT_PING, ACT_WAYPOINT, ACT_SORT, ACT_SCAN };
|
|
|
|
|
|
|
|
|
|
// ── unified list entry ───────────────────────────────────────────────────────
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
struct Entry {
|
2026-05-14 16:29:04 +02:00
|
|
|
char name[32];
|
2026-06-15 09:22:26 +02:00
|
|
|
uint8_t type;
|
|
|
|
|
uint8_t pub_key[PUB_KEY_SIZE];
|
|
|
|
|
bool has_key;
|
|
|
|
|
// stored-source fields
|
2026-05-14 16:29:04 +02:00
|
|
|
int32_t lat_e6, lon_e6;
|
|
|
|
|
float dist_km;
|
2026-05-21 00:13:36 +02:00
|
|
|
uint32_t lastmod;
|
2026-06-15 09:22:26 +02:00
|
|
|
int contact_idx;
|
|
|
|
|
// scan-source fields
|
|
|
|
|
int8_t rssi, snr_x4, remote_snr_x4;
|
|
|
|
|
bool is_known;
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const int MAX_NEARBY = 32;
|
|
|
|
|
Entry _entries[MAX_NEARBY];
|
|
|
|
|
int _count;
|
|
|
|
|
int _sel;
|
|
|
|
|
int _scroll;
|
|
|
|
|
bool _detail;
|
2026-06-03 08:32:32 +02:00
|
|
|
bool _nav = false; // full-screen navigate-to-node view (over detail)
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
int32_t _own_lat, _own_lon;
|
|
|
|
|
bool _own_gps;
|
2026-06-15 09:22:26 +02:00
|
|
|
|
|
|
|
|
Source _source;
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
uint8_t _filter;
|
2026-06-15 09:22:26 +02:00
|
|
|
uint8_t _sort;
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
2026-05-14 16:29:04 +02:00
|
|
|
unsigned long _detail_refresh_ms;
|
2026-06-09 17:15:07 +02:00
|
|
|
unsigned long _list_refresh_ms = 0;
|
2026-06-15 09:22:26 +02:00
|
|
|
static const unsigned long DETAIL_REFRESH_MS = 10000UL;
|
2026-06-09 17:15:07 +02:00
|
|
|
static const unsigned long TIME_LIST_REFRESH_MS = 3000UL;
|
2026-05-14 16:29:04 +02:00
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// ── live-scan state ──────────────────────────────────────────────────────────
|
|
|
|
|
bool _scanning;
|
|
|
|
|
unsigned long _scan_started_ms;
|
|
|
|
|
static const unsigned long SCAN_DURATION_MS = 8000UL;
|
|
|
|
|
|
|
|
|
|
// ── popups ────────────────────────────────────────────────────────────────────
|
|
|
|
|
PopupMenu _menu; // unified action menu (Hold Enter), list + detail
|
|
|
|
|
PopupMenu _ping_menu; // ping (special: read-only result rows)
|
2026-05-29 21:49:48 +02:00
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
Action _menu_actions[8]; // parallel to _menu rows — stable action ids
|
|
|
|
|
int _menu_action_count;
|
|
|
|
|
char _sort_label[16]; // dynamic label for the Sort row
|
|
|
|
|
|
|
|
|
|
// ── ping state ───────────────────────────────────────────────────────────────
|
|
|
|
|
char _ping_time_str[24];
|
|
|
|
|
char _ping_snr_out_str[24];
|
|
|
|
|
char _ping_snr_back_str[24];
|
2026-05-29 21:49:48 +02:00
|
|
|
bool _pinging;
|
|
|
|
|
unsigned long _ping_started_ms;
|
|
|
|
|
static const unsigned long PING_TIMEOUT_MS = 3000UL;
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
2026-05-21 07:57:21 +02:00
|
|
|
// ── helpers ──────────────────────────────────────────────────────────────────
|
2026-05-21 09:14:16 +02:00
|
|
|
static void pubKeyToBase64(const uint8_t* key, char* out, int out_len) {
|
|
|
|
|
static const char T[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
|
int j = 0;
|
|
|
|
|
for (int i = 0; i < PUB_KEY_SIZE && j + 5 < out_len; i += 3) {
|
|
|
|
|
uint32_t b = ((uint32_t)key[i] << 16)
|
|
|
|
|
| (i+1 < PUB_KEY_SIZE ? (uint32_t)key[i+1] << 8 : 0)
|
|
|
|
|
| (i+2 < PUB_KEY_SIZE ? (uint32_t)key[i+2] : 0);
|
|
|
|
|
out[j++] = T[(b >> 18) & 63];
|
|
|
|
|
out[j++] = T[(b >> 12) & 63];
|
|
|
|
|
if (i+1 < PUB_KEY_SIZE) out[j++] = T[(b >> 6) & 63];
|
|
|
|
|
if (i+2 < PUB_KEY_SIZE) out[j++] = T[b & 63];
|
|
|
|
|
}
|
|
|
|
|
out[j] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-14 13:21:59 +02:00
|
|
|
bool useImperial() const { return _task && _task->useImperial(); }
|
|
|
|
|
|
2026-05-14 16:29:04 +02:00
|
|
|
static void fmtAge(char* buf, int n, uint32_t lastmod) {
|
|
|
|
|
uint32_t now = rtc_clock.getCurrentTime();
|
|
|
|
|
if (now < lastmod || lastmod == 0) { snprintf(buf, n, "unknown"); return; }
|
|
|
|
|
uint32_t age = now - lastmod;
|
|
|
|
|
if (age < 60) snprintf(buf, n, "%us ago", age);
|
|
|
|
|
else if (age < 3600) snprintf(buf, n, "%um ago", age / 60);
|
|
|
|
|
else if (age < 86400) snprintf(buf, n, "%uh ago", age / 3600);
|
|
|
|
|
else snprintf(buf, n, ">1d ago");
|
|
|
|
|
}
|
|
|
|
|
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
static const char* typeName(uint8_t t) {
|
|
|
|
|
switch (t) {
|
2026-05-14 15:49:23 +02:00
|
|
|
case ADV_TYPE_CHAT: return "Companion";
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
case ADV_TYPE_REPEATER: return "Repeater";
|
|
|
|
|
case ADV_TYPE_ROOM: return "Room";
|
|
|
|
|
case ADV_TYPE_SENSOR: return "Sensor";
|
|
|
|
|
default: return "Unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
static const char* typeShort(uint8_t t) {
|
|
|
|
|
switch (t) {
|
|
|
|
|
case ADV_TYPE_REPEATER: return "Rpt";
|
|
|
|
|
case ADV_TYPE_SENSOR: return "Snsr";
|
|
|
|
|
case ADV_TYPE_ROOM: return "Room";
|
|
|
|
|
case ADV_TYPE_CHAT: return "Comp";
|
|
|
|
|
default: return "?";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The selected entry, or nullptr when the list is empty.
|
|
|
|
|
const Entry* selected() const {
|
|
|
|
|
return (_count > 0 && _sel < _count) ? &_entries[_sel] : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool typeMatchesFilter(uint8_t type, uint8_t flags, bool have_flags) const {
|
|
|
|
|
switch (_filter) {
|
|
|
|
|
case F_FAV: return have_flags && (flags & 0x01);
|
|
|
|
|
case F_COMP: return type == ADV_TYPE_CHAT;
|
|
|
|
|
case F_RPT: return type == ADV_TYPE_REPEATER;
|
|
|
|
|
case F_ROOM: return type == ADV_TYPE_ROOM;
|
|
|
|
|
case F_SNSR: return type == ADV_TYPE_SENSOR;
|
|
|
|
|
case F_ALL:
|
|
|
|
|
default: return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── data refresh ──────────────────────────────────────────────────────────────
|
|
|
|
|
void refreshStored() {
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
_count = 0;
|
|
|
|
|
_own_lat = _own_lon = 0;
|
2026-06-04 00:12:15 +02:00
|
|
|
_own_gps = _task->currentLocation(_own_lat, _own_lon);
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
|
|
|
|
int nc = the_mesh.getNumContacts();
|
|
|
|
|
for (int i = 0; i < nc && _count < MAX_NEARBY; i++) {
|
|
|
|
|
ContactInfo ci;
|
|
|
|
|
if (!the_mesh.getContactByIdx(i, ci)) continue;
|
2026-06-15 09:22:26 +02:00
|
|
|
if (!typeMatchesFilter(ci.type, ci.flags, true)) continue;
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
|
|
|
|
Entry& e = _entries[_count++];
|
|
|
|
|
strncpy(e.name, ci.name, sizeof(e.name) - 1);
|
|
|
|
|
e.name[sizeof(e.name) - 1] = '\0';
|
2026-06-15 09:22:26 +02:00
|
|
|
memcpy(e.pub_key, ci.id.pub_key, PUB_KEY_SIZE);
|
|
|
|
|
e.has_key = true;
|
2026-05-14 15:49:23 +02:00
|
|
|
e.lat_e6 = ci.gps_lat;
|
|
|
|
|
e.lon_e6 = ci.gps_lon;
|
|
|
|
|
bool remote_gps = (ci.gps_lat != 0 || ci.gps_lon != 0);
|
|
|
|
|
e.dist_km = (_own_gps && remote_gps)
|
2026-06-04 00:15:36 +02:00
|
|
|
? geo::haversineKm(_own_lat, _own_lon, ci.gps_lat, ci.gps_lon)
|
2026-05-14 15:49:23 +02:00
|
|
|
: -1.0f;
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
e.type = ci.type;
|
|
|
|
|
e.contact_idx = i;
|
2026-05-14 16:29:04 +02:00
|
|
|
e.lastmod = ci.lastmod;
|
2026-06-15 09:22:26 +02:00
|
|
|
e.is_known = true;
|
2026-05-21 00:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
sortStored();
|
|
|
|
|
clampSelection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void sortStored() {
|
|
|
|
|
uint32_t now_ts = rtc_clock.getCurrentTime();
|
2026-05-14 15:49:23 +02:00
|
|
|
for (int i = 0; i < _count - 1; i++) {
|
|
|
|
|
int best = i;
|
|
|
|
|
for (int j = i + 1; j < _count; j++) {
|
2026-06-15 09:22:26 +02:00
|
|
|
if (_sort == SORT_TIME) {
|
|
|
|
|
// lastmod=0 or lastmod>now (RTC not synced) → "unknown" → sort to bottom.
|
|
|
|
|
uint32_t tj = (_entries[j].lastmod > 0 && now_ts >= _entries[j].lastmod) ? _entries[j].lastmod : 0;
|
|
|
|
|
uint32_t tb = (_entries[best].lastmod > 0 && now_ts >= _entries[best].lastmod) ? _entries[best].lastmod : 0;
|
2026-06-09 15:09:57 +02:00
|
|
|
if (tj > 0 && (tb == 0 || tj > tb)) best = j; // descending — most recent first
|
|
|
|
|
} else {
|
|
|
|
|
float dj = _entries[j].dist_km, db = _entries[best].dist_km;
|
|
|
|
|
if (dj >= 0.0f && (db < 0.0f || dj < db)) best = j; // ascending — closest first
|
|
|
|
|
}
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
}
|
2026-05-14 15:49:23 +02:00
|
|
|
if (best != i) { Entry tmp = _entries[i]; _entries[i] = _entries[best]; _entries[best] = tmp; }
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
}
|
2026-06-15 09:22:26 +02:00
|
|
|
}
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
void refreshScan() {
|
|
|
|
|
static DiscoverResult dr[DISCOVER_RESULTS_MAX]; // scratch — refresh isn't reentrant
|
|
|
|
|
int n = the_mesh.getDiscoverResults(dr, DISCOVER_RESULTS_MAX);
|
|
|
|
|
_count = 0;
|
|
|
|
|
for (int i = 0; i < n && _count < MAX_NEARBY; i++) {
|
|
|
|
|
if (!typeMatchesFilter(dr[i].type, 0, false)) continue;
|
|
|
|
|
Entry& e = _entries[_count++];
|
|
|
|
|
strncpy(e.name, dr[i].name, sizeof(e.name) - 1);
|
|
|
|
|
e.name[sizeof(e.name) - 1] = '\0';
|
|
|
|
|
e.type = dr[i].type;
|
|
|
|
|
memcpy(e.pub_key, dr[i].pub_key, PUB_KEY_SIZE);
|
|
|
|
|
e.has_key = true;
|
|
|
|
|
e.rssi = dr[i].rssi;
|
|
|
|
|
e.snr_x4 = dr[i].snr_x4;
|
|
|
|
|
e.remote_snr_x4 = dr[i].remote_snr_x4;
|
|
|
|
|
e.is_known = dr[i].is_known;
|
|
|
|
|
e.lat_e6 = e.lon_e6 = 0;
|
|
|
|
|
e.dist_km = -1.0f;
|
|
|
|
|
e.lastmod = 0;
|
|
|
|
|
e.contact_idx = -1;
|
|
|
|
|
}
|
|
|
|
|
// strongest first
|
|
|
|
|
for (int i = 0; i < _count - 1; i++) {
|
|
|
|
|
int best = i;
|
|
|
|
|
for (int j = i + 1; j < _count; j++)
|
|
|
|
|
if (_entries[j].rssi > _entries[best].rssi) best = j;
|
|
|
|
|
if (best != i) { Entry tmp = _entries[i]; _entries[i] = _entries[best]; _entries[best] = tmp; }
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
}
|
2026-06-15 09:22:26 +02:00
|
|
|
clampSelection();
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
void refresh() { if (_source == SRC_SCAN) refreshScan(); else refreshStored(); }
|
|
|
|
|
|
|
|
|
|
void clampSelection() {
|
|
|
|
|
if (_count == 0) { _sel = _scroll = 0; }
|
|
|
|
|
else if (_sel >= _count) { _sel = _count - 1; if (_scroll > _sel) _scroll = _sel; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── live scan ────────────────────────────────────────────────────────────────
|
|
|
|
|
void enterScan() {
|
|
|
|
|
_source = SRC_SCAN;
|
|
|
|
|
_detail = false;
|
|
|
|
|
_nav = false;
|
|
|
|
|
_scanning = true;
|
|
|
|
|
_scan_started_ms = millis();
|
|
|
|
|
_sel = _scroll = 0;
|
2026-05-21 07:57:21 +02:00
|
|
|
the_mesh.sendNodeDiscoverReq();
|
2026-06-15 09:22:26 +02:00
|
|
|
refreshScan();
|
2026-05-21 07:57:21 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
void leaveScan() {
|
|
|
|
|
_source = SRC_STORED;
|
|
|
|
|
_detail = false;
|
|
|
|
|
_nav = false;
|
|
|
|
|
_sel = _scroll = 0;
|
|
|
|
|
refreshStored();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save the currently-selected entry as a waypoint (its name as label).
|
|
|
|
|
void saveSelectedWaypoint() {
|
|
|
|
|
const Entry* e = selected();
|
|
|
|
|
if (!e) return;
|
|
|
|
|
if (e->lat_e6 == 0 && e->lon_e6 == 0) { _task->showAlert("No node GPS", 1000); return; }
|
|
|
|
|
_task->addWaypoint(e->lat_e6, e->lon_e6, e->name); // WaypointStore truncates the label
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── ping ──────────────────────────────────────────────────────────────────────
|
2026-05-29 21:49:48 +02:00
|
|
|
void resetPingLines() {
|
|
|
|
|
_ping_time_str[0] = '\0';
|
|
|
|
|
_ping_snr_out_str[0] = '\0';
|
|
|
|
|
_ping_snr_back_str[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 14:00:16 +02:00
|
|
|
int pingRowCount() const {
|
|
|
|
|
return 1 + (_ping_time_str[0] ? 1 : 0)
|
|
|
|
|
+ (_ping_snr_out_str[0] ? 1 : 0)
|
|
|
|
|
+ (_ping_snr_back_str[0] ? 1 : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void rebuildPingMenu() {
|
2026-06-14 13:37:03 +02:00
|
|
|
int keep = _ping_menu.selectedIndex(); // preserve selection across a rebuild
|
2026-05-29 21:49:48 +02:00
|
|
|
_ping_menu.begin("Ping", 4);
|
2026-05-30 23:35:07 +02:00
|
|
|
_ping_menu.addItem("Send");
|
2026-06-15 09:22:26 +02:00
|
|
|
if (_ping_time_str[0]) _ping_menu.addItem(_ping_time_str);
|
|
|
|
|
if (_ping_snr_out_str[0]) _ping_menu.addItem(_ping_snr_out_str);
|
2026-06-03 14:00:16 +02:00
|
|
|
if (_ping_snr_back_str[0]) _ping_menu.addItem(_ping_snr_back_str);
|
2026-06-14 13:37:03 +02:00
|
|
|
_ping_menu.setSelected(keep);
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void closePingMenu(bool clear_task = true) {
|
|
|
|
|
_ping_menu.active = false;
|
|
|
|
|
_pinging = false;
|
|
|
|
|
if (clear_task && _task) _task->clearPing();
|
|
|
|
|
resetPingLines();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void startPingForKey(const uint8_t* pub_key) {
|
|
|
|
|
resetPingLines();
|
2026-05-30 23:35:07 +02:00
|
|
|
snprintf(_ping_time_str, sizeof(_ping_time_str), "RTT: ...");
|
2026-05-29 21:49:48 +02:00
|
|
|
if (_task && _task->startPing(pub_key)) {
|
|
|
|
|
_pinging = true;
|
|
|
|
|
_ping_started_ms = millis();
|
|
|
|
|
} else {
|
2026-06-03 14:11:03 +02:00
|
|
|
snprintf(_ping_time_str, sizeof(_ping_time_str), "RTT: send fail");
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
2026-06-03 14:11:03 +02:00
|
|
|
if (_ping_menu.active) rebuildPingMenu(); // surface "RTT: ..." immediately
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updatePingMenuState() {
|
|
|
|
|
if (!_ping_menu.active || !_task || (!_pinging && !_task->isPingActive())) return;
|
|
|
|
|
|
|
|
|
|
int16_t snr_out = 0, snr_back = 0;
|
|
|
|
|
uint32_t rtt = 0;
|
|
|
|
|
_task->getPingResult(snr_out, snr_back, rtt);
|
|
|
|
|
|
|
|
|
|
if (_pinging && (snr_out != 0 || snr_back != 0 || rtt != 0)) {
|
|
|
|
|
if (rtt > 0 && rtt < 10000) {
|
2026-05-30 23:35:07 +02:00
|
|
|
snprintf(_ping_time_str, sizeof(_ping_time_str), "RTT: %lums", rtt);
|
2026-05-29 21:49:48 +02:00
|
|
|
} else {
|
2026-05-30 23:35:07 +02:00
|
|
|
snprintf(_ping_time_str, sizeof(_ping_time_str), "RTT: timeout");
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
2026-06-15 09:22:26 +02:00
|
|
|
if (snr_out != 0)
|
2026-05-29 21:49:48 +02:00
|
|
|
snprintf(_ping_snr_out_str, sizeof(_ping_snr_out_str), "SNR out: %.1f", snr_out / 4.0f);
|
2026-06-15 09:22:26 +02:00
|
|
|
if (snr_back != 0)
|
2026-05-29 21:49:48 +02:00
|
|
|
snprintf(_ping_snr_back_str, sizeof(_ping_snr_back_str), "SNR back: %.1f", snr_back / 4.0f);
|
|
|
|
|
_pinging = false;
|
|
|
|
|
} else if (_pinging && millis() - _ping_started_ms >= PING_TIMEOUT_MS) {
|
2026-05-30 23:35:07 +02:00
|
|
|
snprintf(_ping_time_str, sizeof(_ping_time_str), "RTT: timeout");
|
2026-05-29 21:49:48 +02:00
|
|
|
_ping_snr_out_str[0] = '\0';
|
|
|
|
|
_ping_snr_back_str[0] = '\0';
|
|
|
|
|
_pinging = false;
|
|
|
|
|
if (_task) _task->clearPing();
|
|
|
|
|
}
|
2026-06-14 13:37:03 +02:00
|
|
|
if (pingRowCount() != _ping_menu.count()) rebuildPingMenu();
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// Ping popup input. Result rows are read-only, so UP/DOWN are swallowed to keep
|
|
|
|
|
// the highlight on "Send".
|
|
|
|
|
void handlePingMenuInput(char c) {
|
|
|
|
|
if (c == KEY_UP || c == KEY_DOWN) return;
|
2026-05-29 21:49:48 +02:00
|
|
|
auto res = _ping_menu.handleInput(c);
|
|
|
|
|
if (res == PopupMenu::SELECTED) {
|
2026-06-15 09:22:26 +02:00
|
|
|
const Entry* e = selected();
|
|
|
|
|
if (!_pinging && e && e->has_key) startPingForKey(e->pub_key);
|
|
|
|
|
_ping_menu.active = true; // stay open so Ping can be repeated
|
2026-05-29 21:49:48 +02:00
|
|
|
} else if (res == PopupMenu::CANCELLED) {
|
|
|
|
|
closePingMenu();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// ── action menu (Hold Enter) — same everywhere ──────────────────────────────
|
|
|
|
|
void buildSortLabel() {
|
|
|
|
|
snprintf(_sort_label, sizeof(_sort_label),
|
|
|
|
|
"Sort: %s", _sort == SORT_TIME ? "Recent" : "Dist");
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
void openActionMenu() {
|
|
|
|
|
const Entry* e = selected();
|
|
|
|
|
bool stored = (_source == SRC_STORED);
|
|
|
|
|
bool has_gps = e && stored && (e->lat_e6 != 0 || e->lon_e6 != 0);
|
|
|
|
|
bool has_key = e && e->has_key;
|
|
|
|
|
|
|
|
|
|
buildSortLabel();
|
|
|
|
|
_menu_action_count = 0;
|
|
|
|
|
_menu.begin("Options", 5);
|
|
|
|
|
auto add = [&](const char* label, Action a) {
|
|
|
|
|
_menu.addItem(label);
|
|
|
|
|
_menu_actions[_menu_action_count++] = a;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (has_gps) add("Navigate", ACT_NAV);
|
|
|
|
|
if (has_key) add("Ping", ACT_PING);
|
|
|
|
|
if (has_gps) add("Save waypoint", ACT_WAYPOINT);
|
|
|
|
|
if (stored) add(_sort_label, ACT_SORT); // sort is meaningless for live-scan rows
|
|
|
|
|
add(stored ? "Discover scan" : "Rescan", ACT_SCAN);
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
void runAction(Action a) {
|
|
|
|
|
switch (a) {
|
|
|
|
|
case ACT_NAV: {
|
|
|
|
|
const Entry* e = selected();
|
|
|
|
|
if (e && (e->lat_e6 != 0 || e->lon_e6 != 0)) _nav = true;
|
|
|
|
|
else _task->showAlert("No node GPS", 1000);
|
|
|
|
|
break;
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
2026-06-15 09:22:26 +02:00
|
|
|
case ACT_PING: {
|
|
|
|
|
const Entry* e = selected();
|
|
|
|
|
rebuildPingMenu();
|
|
|
|
|
_ping_menu.active = true;
|
|
|
|
|
if (e && e->has_key) startPingForKey(e->pub_key);
|
|
|
|
|
break;
|
2026-05-31 00:11:43 +02:00
|
|
|
}
|
2026-06-15 09:22:26 +02:00
|
|
|
case ACT_WAYPOINT: saveSelectedWaypoint(); break;
|
|
|
|
|
case ACT_SORT: break; // adjusted in-place via LEFT/RIGHT, not ENTER
|
|
|
|
|
case ACT_SCAN: enterScan(); break;
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// ── detail rendering ──────────────────────────────────────────────────────────
|
|
|
|
|
void renderStoredDetail(DisplayDriver& display) {
|
2026-05-29 21:49:48 +02:00
|
|
|
const Entry& e = _entries[_sel];
|
2026-06-17 21:35:51 +02:00
|
|
|
const int hdr = display.listStart(); // content top (gap below the header separator)
|
2026-06-14 13:21:59 +02:00
|
|
|
display.drawInvertedHeader(e.name);
|
2026-05-29 21:49:48 +02:00
|
|
|
|
|
|
|
|
int step = display.lineStep();
|
|
|
|
|
if (step * 5 > display.height() - hdr) step = (display.height() - hdr) / 5;
|
|
|
|
|
char buf[32];
|
|
|
|
|
snprintf(buf, sizeof(buf), "Lat: %.5f", e.lat_e6 / 1e6);
|
|
|
|
|
display.setCursor(2, hdr); display.print(buf);
|
|
|
|
|
snprintf(buf, sizeof(buf), "Lon: %.5f", e.lon_e6 / 1e6);
|
|
|
|
|
display.setCursor(2, hdr + step); display.print(buf);
|
|
|
|
|
|
|
|
|
|
if (e.dist_km >= 0.0f) {
|
|
|
|
|
char dist[12];
|
2026-06-04 00:44:56 +02:00
|
|
|
geo::fmtDist(dist, sizeof(dist), e.dist_km, useImperial());
|
2026-06-04 00:15:36 +02:00
|
|
|
int az = geo::bearingDeg(_own_lat, _own_lon, e.lat_e6, e.lon_e6);
|
|
|
|
|
snprintf(buf, sizeof(buf), "Dist: %s %s", dist, geo::bearingCardinal(az));
|
2026-05-29 21:49:48 +02:00
|
|
|
} else {
|
|
|
|
|
snprintf(buf, sizeof(buf), "Dist: no GPS");
|
|
|
|
|
}
|
|
|
|
|
display.setCursor(2, hdr + step * 2); display.print(buf);
|
|
|
|
|
snprintf(buf, sizeof(buf), "Type: %s", typeName(e.type));
|
|
|
|
|
display.setCursor(2, hdr + step * 3); display.print(buf);
|
|
|
|
|
char age[16];
|
|
|
|
|
fmtAge(age, sizeof(age), e.lastmod);
|
|
|
|
|
snprintf(buf, sizeof(buf), "Seen: %s", age);
|
|
|
|
|
display.drawTextEllipsized(2, hdr + step * 4, display.width() - 4, buf);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
void renderScanDetail(DisplayDriver& display) {
|
|
|
|
|
const Entry& e = _entries[_sel];
|
2026-06-17 21:35:51 +02:00
|
|
|
const int hdr = display.listStart(); // content top (gap below the header separator)
|
2026-05-21 07:57:21 +02:00
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
char label[32];
|
|
|
|
|
if (e.name[0]) { strncpy(label, e.name, 31); label[31] = '\0'; }
|
|
|
|
|
else { snprintf(label, sizeof(label), "[%s]", typeName(e.type)); }
|
|
|
|
|
display.drawInvertedHeader(label);
|
2026-05-21 07:57:21 +02:00
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
char b64[48];
|
|
|
|
|
pubKeyToBase64(e.pub_key, b64, sizeof(b64));
|
|
|
|
|
int max_chars = (display.width() - 4) / display.getCharWidth();
|
|
|
|
|
char b64_line[48];
|
|
|
|
|
if (max_chars < 4) {
|
|
|
|
|
b64_line[0] = '\0';
|
|
|
|
|
} else if ((int)strlen(b64) > max_chars) {
|
|
|
|
|
strncpy(b64_line, b64, max_chars - 3);
|
|
|
|
|
b64_line[max_chars - 3] = '\0';
|
|
|
|
|
strcat(b64_line, "...");
|
2026-05-21 07:57:21 +02:00
|
|
|
} else {
|
2026-06-15 09:22:26 +02:00
|
|
|
strncpy(b64_line, b64, sizeof(b64_line) - 1);
|
|
|
|
|
b64_line[sizeof(b64_line) - 1] = '\0';
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
2026-06-15 09:22:26 +02:00
|
|
|
if (b64_line[0]) { display.setCursor(2, hdr); display.print(b64_line); }
|
2026-05-29 21:49:48 +02:00
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
int step = display.lineStep();
|
|
|
|
|
if (step * 5 > display.height() - hdr) step = (display.height() - hdr) / 5;
|
|
|
|
|
char buf[32];
|
|
|
|
|
snprintf(buf, sizeof(buf), "RSSI: %d dBm", (int)e.rssi);
|
|
|
|
|
display.setCursor(2, hdr + step); display.print(buf);
|
|
|
|
|
snprintf(buf, sizeof(buf), "SNR: %.1f dB", e.snr_x4 / 4.0f);
|
|
|
|
|
display.setCursor(2, hdr + step * 2); display.print(buf);
|
|
|
|
|
snprintf(buf, sizeof(buf), "Rem: %.1f dB", e.remote_snr_x4 / 4.0f);
|
|
|
|
|
display.setCursor(2, hdr + step * 3); display.print(buf);
|
|
|
|
|
display.setCursor(2, hdr + step * 4);
|
|
|
|
|
display.print(e.is_known ? "Status: known" : "Status: new");
|
2026-05-21 07:57:21 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// Draw whichever popup is active over the current view. Returns true if one was.
|
|
|
|
|
bool renderActivePopup(DisplayDriver& display) {
|
|
|
|
|
updatePingMenuState();
|
|
|
|
|
if (_ping_menu.active) { _ping_menu.render(display); return true; }
|
|
|
|
|
if (_menu.active) { _menu.render(display); return true; }
|
|
|
|
|
return false;
|
2026-05-21 07:57:21 +02:00
|
|
|
}
|
|
|
|
|
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
public:
|
|
|
|
|
NearbyScreen(UITask* task)
|
2026-05-21 22:08:28 +02:00
|
|
|
: _task(task), _count(0), _sel(0), _scroll(0), _detail(false),
|
2026-06-15 09:22:26 +02:00
|
|
|
_own_lat(0), _own_lon(0), _own_gps(false),
|
|
|
|
|
_source(SRC_STORED), _filter(F_ALL), _sort(SORT_DIST),
|
|
|
|
|
_detail_refresh_ms(0), _scanning(false), _scan_started_ms(0),
|
|
|
|
|
_menu_action_count(0), _pinging(false), _ping_started_ms(0) {
|
|
|
|
|
resetPingLines();
|
|
|
|
|
_sort_label[0] = '\0';
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
|
|
|
|
void enter() {
|
|
|
|
|
_sel = _scroll = 0;
|
|
|
|
|
_detail = false;
|
2026-06-03 08:32:32 +02:00
|
|
|
_nav = false;
|
2026-06-15 09:22:26 +02:00
|
|
|
_source = SRC_STORED;
|
|
|
|
|
// _filter / _sort persist across enter() — set once in the constructor
|
|
|
|
|
_scanning = false;
|
|
|
|
|
_menu.active = false;
|
2026-05-29 21:49:48 +02:00
|
|
|
_ping_menu.active = false;
|
|
|
|
|
_pinging = false;
|
2026-06-15 09:22:26 +02:00
|
|
|
resetPingLines();
|
2026-05-29 21:49:48 +02:00
|
|
|
_task->clearPing();
|
2026-06-15 09:22:26 +02:00
|
|
|
refreshStored();
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int render(DisplayDriver& display) override {
|
|
|
|
|
display.setTextSize(1);
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// periodic refresh in stored-detail — preserve selected contact by idx
|
|
|
|
|
if (_detail && _source == SRC_STORED &&
|
|
|
|
|
millis() - _detail_refresh_ms >= DETAIL_REFRESH_MS) {
|
|
|
|
|
int saved = (_sel < _count) ? _entries[_sel].contact_idx : -1;
|
|
|
|
|
refreshStored();
|
2026-05-14 16:29:04 +02:00
|
|
|
bool found = false;
|
2026-06-15 09:22:26 +02:00
|
|
|
if (saved >= 0)
|
|
|
|
|
for (int i = 0; i < _count; i++)
|
|
|
|
|
if (_entries[i].contact_idx == saved) { _sel = i; found = true; break; }
|
2026-06-03 08:36:41 +02:00
|
|
|
if (!found) { _detail = false; _nav = false; } // contact gone — drop both views
|
2026-05-14 16:29:04 +02:00
|
|
|
_detail_refresh_ms = millis();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 08:32:32 +02:00
|
|
|
// ── navigate-to-node view ────────────────────────────────────────────────
|
|
|
|
|
if (_nav && _sel < _count) {
|
|
|
|
|
const Entry& e = _entries[_sel];
|
|
|
|
|
int cog; bool cogv = _task->currentCourse(cog);
|
|
|
|
|
navview::draw(display, _own_gps, _own_lat, _own_lon,
|
2026-06-04 00:44:56 +02:00
|
|
|
e.lat_e6, e.lon_e6, e.name, cogv, cog, useImperial());
|
2026-06-03 08:32:32 +02:00
|
|
|
return 1000;
|
|
|
|
|
}
|
|
|
|
|
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
// ── detail view ──────────────────────────────────────────────────────────
|
|
|
|
|
if (_detail && _sel < _count) {
|
2026-06-15 09:22:26 +02:00
|
|
|
if (_source == SRC_SCAN) renderScanDetail(display);
|
|
|
|
|
else renderStoredDetail(display);
|
|
|
|
|
renderActivePopup(display);
|
2026-05-29 21:49:48 +02:00
|
|
|
return _ping_menu.active ? 50 : 2000;
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── list view ────────────────────────────────────────────────────────────
|
2026-06-15 09:22:26 +02:00
|
|
|
if (_source == SRC_SCAN) {
|
|
|
|
|
refreshScan();
|
|
|
|
|
if (_scanning && millis() - _scan_started_ms >= SCAN_DURATION_MS) _scanning = false;
|
|
|
|
|
} else if (_sort == SORT_TIME && millis() - _list_refresh_ms >= TIME_LIST_REFRESH_MS) {
|
|
|
|
|
// re-sort periodically so newly-heard contacts bubble up
|
|
|
|
|
refreshStored();
|
2026-06-09 17:15:07 +02:00
|
|
|
_list_refresh_ms = millis();
|
|
|
|
|
}
|
|
|
|
|
|
refactor: replace all hardcoded pixel positions with layout helpers
Add lineStep(), headerH(), listStart(), listVisible(), valCol() to
DisplayDriver so layout derives from getLineHeight() instead of fixed
OLED constants. Refactor all screens (SettingsScreen, NearbyScreen,
FullscreenMsgView, QuickMsgScreen, BotScreen, DashboardConfigScreen,
AutoAdvertScreen, RingtoneEditorScreen) and all HomeScreen pages plus
the lock screen and splash screen in UITask.cpp.
On OLED (lh=8) positions are unchanged; on landscape e-ink (lh=16) all
text and widgets now scale correctly to the 250×122 display.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 18:05:09 +02:00
|
|
|
int item_h = display.lineStep();
|
|
|
|
|
int dist_col = display.width() - display.getCharWidth() * 7;
|
|
|
|
|
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
display.setColor(DisplayDriver::LIGHT);
|
2026-06-15 09:22:26 +02:00
|
|
|
char title[28];
|
|
|
|
|
const char* flt = (_filter != F_ALL) ? FILTER_LABELS[_filter] : nullptr;
|
|
|
|
|
if (_source == SRC_SCAN) {
|
|
|
|
|
const char* base = _scanning ? "SCANNING" : "SCAN";
|
|
|
|
|
if (flt) {
|
|
|
|
|
if (!_scanning && _count == 0) snprintf(title, sizeof(title), "SCAN %s: none", flt);
|
|
|
|
|
else snprintf(title, sizeof(title), "%s %s (%d)", base, flt, _count);
|
|
|
|
|
} else if (_scanning) snprintf(title, sizeof(title), "SCANNING (%d)", _count);
|
|
|
|
|
else if (_count == 0) snprintf(title, sizeof(title), "SCAN: none");
|
|
|
|
|
else snprintf(title, sizeof(title), "SCAN (%d)", _count);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(title, sizeof(title), "NEARBY %s %s",
|
|
|
|
|
FILTER_LABELS[_filter], _sort == SORT_TIME ? "recent" : "dist");
|
|
|
|
|
}
|
refactor(ui): shared drawList + header + key-decode helpers
- drawList(): scrollable list skeleton (visible window, keep-sel-in-view,
scroll-column reserve, per-row callback, indicator). Migrate Tools, Bot,
Nearby, Waypoints list and the three QuickMsg lists onto it; centralises the
scroll-clamp/reserve/indicator that each had open-coded (and inconsistently).
- drawCenteredHeader(): centred title + separator, replacing the duplicated
two-liner across ~10 screens (incl. QuickMsg, which used width()/2 spacing and
was missed before); unifies the separator at headerH()-sepH().
- keyIsPrev()/keyIsNext() in UIScreen.h for the LEFT||PREV / RIGHT||NEXT idiom;
applied to 6 screens, behaviour-preserving. Cancel/context-menu stay
per-screen (they mean different things on different screens).
- rotLabel() dedups the 0/90/180/270 array in Settings.
- Unify selection-row height to lineStep()-1 (Tools/Bot matched the rest).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:23:00 +02:00
|
|
|
display.drawCenteredHeader(title);
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
|
|
|
|
if (_count == 0) {
|
2026-06-15 09:22:26 +02:00
|
|
|
char empty[24];
|
|
|
|
|
if (_source == SRC_SCAN) {
|
|
|
|
|
const char* msg;
|
|
|
|
|
if (_scanning) msg = "Waiting for replies...";
|
|
|
|
|
else if (flt) { snprintf(empty, sizeof(empty), "No %s nodes", flt); msg = empty; }
|
|
|
|
|
else msg = "No nodes found";
|
|
|
|
|
display.drawTextCentered(display.width() / 2, display.height() / 2, msg);
|
|
|
|
|
} else {
|
|
|
|
|
const char* hint;
|
|
|
|
|
if (flt) { snprintf(empty, sizeof(empty), "No %s contacts", flt); hint = "[<>] change filter"; }
|
|
|
|
|
else { snprintf(empty, sizeof(empty), "No contacts found"); hint = "[Enter]=Discover"; }
|
|
|
|
|
display.drawTextCentered(display.width() / 2, display.height() / 2 - display.lineStep() / 2, empty);
|
|
|
|
|
display.drawTextCentered(display.width() / 2, display.height() / 2 + display.lineStep() / 2, hint);
|
|
|
|
|
}
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
} else {
|
2026-06-16 08:54:11 +02:00
|
|
|
drawList(display, _count, _sel, _scroll, [&](int idx, int y, bool sel, int reserve) {
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
const Entry& e = _entries[idx];
|
|
|
|
|
|
feat(ui): proportional scrollbar with mini-icon caps; reclaim row space
Replace the text ^/v scroll arrows with a font-scaling indicator drawn in
icons.h: a proportional thumb between fixed up/down triangle mini-icon caps.
drawScrollIndicator() lives in a ~5px right-edge column and scales via
miniIconScale (1x OLED, 2x landscape e-ink).
- Mini-icons: ICON_SCROLL_UP/DOWN; miniIconDrawTop (exact placement) and
miniIconDrawHalo (shape-hugging 1px DARK halo, clip_top to spare the header
separator) so caps stay visible on the LIGHT selection bar without a box.
- Thumb is 3*s wide so it centres on the triangle column; caps are static end
markers (always drawn) so neither vanishes at the extremes.
- scrollIndicatorReserve(): right-edge gutter (0 when the list fits). Content,
the selection bar and the message-history bubbles are all narrowed by it so
nothing renders under the scrollbar. Portrait e-ink wrap width subtracts the
reserve in both the box-height pass and render so wrapped text can't spill.
- Drop the redundant ">" selection marker (the highlight bar already shows
selection) and shift rows to x=2, reclaiming left-edge space.
- Wire the indicator into every scrollable list (Bot, Settings, Nearby, Tools,
Trail, QuickMsg lists + DM/channel history) and add one to WaypointsView,
which scrolled with no indicator before.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:41:46 +02:00
|
|
|
display.drawSelectionRow(0, y - 1, display.width() - reserve, item_h - 1, sel);
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
|
|
|
|
char filt[32];
|
|
|
|
|
display.translateUTF8ToBlocks(filt, e.name, sizeof(filt));
|
2026-06-15 09:22:26 +02:00
|
|
|
if (_source == SRC_SCAN && !e.name[0]) { // unknown node → "[Type]"
|
|
|
|
|
snprintf(filt, sizeof(filt), "[%s]", typeName(e.type));
|
|
|
|
|
}
|
refactor: replace all hardcoded pixel positions with layout helpers
Add lineStep(), headerH(), listStart(), listVisible(), valCol() to
DisplayDriver so layout derives from getLineHeight() instead of fixed
OLED constants. Refactor all screens (SettingsScreen, NearbyScreen,
FullscreenMsgView, QuickMsgScreen, BotScreen, DashboardConfigScreen,
AutoAdvertScreen, RingtoneEditorScreen) and all HomeScreen pages plus
the lock screen and splash screen in UITask.cpp.
On OLED (lh=8) positions are unchanged; on landscape e-ink (lh=16) all
text and widgets now scale correctly to the 250×122 display.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 18:05:09 +02:00
|
|
|
display.drawTextEllipsized(2, y, dist_col - 4, filt);
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
|
|
|
|
|
display.setColor(sel ? DisplayDriver::DARK : DisplayDriver::LIGHT);
|
2026-06-09 15:09:57 +02:00
|
|
|
char right[10];
|
2026-06-15 09:22:26 +02:00
|
|
|
if (_source == SRC_SCAN) {
|
|
|
|
|
snprintf(right, sizeof(right), "%d", (int)e.rssi);
|
|
|
|
|
} else if (_sort == SORT_TIME) {
|
2026-06-09 15:09:57 +02:00
|
|
|
uint32_t now = rtc_clock.getCurrentTime();
|
2026-06-15 09:22:26 +02:00
|
|
|
if (e.lastmod == 0 || now < e.lastmod) snprintf(right, sizeof(right), "?");
|
|
|
|
|
else {
|
2026-06-09 17:19:06 +02:00
|
|
|
uint32_t age = now - e.lastmod;
|
|
|
|
|
if (age < 60) snprintf(right, sizeof(right), "%us", age);
|
|
|
|
|
else if (age < 3600) snprintf(right, sizeof(right), "%um", age / 60);
|
|
|
|
|
else snprintf(right, sizeof(right), "%uh", age / 3600);
|
|
|
|
|
}
|
2026-06-09 15:09:57 +02:00
|
|
|
} else {
|
|
|
|
|
if (e.dist_km >= 0.0f) geo::fmtDist(right, sizeof(right), e.dist_km, useImperial());
|
|
|
|
|
else strncpy(right, "?GPS", sizeof(right));
|
|
|
|
|
}
|
feat(ui): proportional scrollbar with mini-icon caps; reclaim row space
Replace the text ^/v scroll arrows with a font-scaling indicator drawn in
icons.h: a proportional thumb between fixed up/down triangle mini-icon caps.
drawScrollIndicator() lives in a ~5px right-edge column and scales via
miniIconScale (1x OLED, 2x landscape e-ink).
- Mini-icons: ICON_SCROLL_UP/DOWN; miniIconDrawTop (exact placement) and
miniIconDrawHalo (shape-hugging 1px DARK halo, clip_top to spare the header
separator) so caps stay visible on the LIGHT selection bar without a box.
- Thumb is 3*s wide so it centres on the triangle column; caps are static end
markers (always drawn) so neither vanishes at the extremes.
- scrollIndicatorReserve(): right-edge gutter (0 when the list fits). Content,
the selection bar and the message-history bubbles are all narrowed by it so
nothing renders under the scrollbar. Portrait e-ink wrap width subtracts the
reserve in both the box-height pass and render so wrapped text can't spill.
- Drop the redundant ">" selection marker (the highlight bar already shows
selection) and shift rows to x=2, reclaiming left-edge space.
- Wire the indicator into every scrollable list (Bot, Settings, Nearby, Tools,
Trail, QuickMsg lists + DM/channel history) and add one to WaypointsView,
which scrolled with no indicator before.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 14:41:46 +02:00
|
|
|
display.setCursor(display.width() - display.getTextWidth(right) - 2 - reserve, y);
|
2026-06-09 15:09:57 +02:00
|
|
|
display.print(right);
|
refactor(ui): shared drawList + header + key-decode helpers
- drawList(): scrollable list skeleton (visible window, keep-sel-in-view,
scroll-column reserve, per-row callback, indicator). Migrate Tools, Bot,
Nearby, Waypoints list and the three QuickMsg lists onto it; centralises the
scroll-clamp/reserve/indicator that each had open-coded (and inconsistently).
- drawCenteredHeader(): centred title + separator, replacing the duplicated
two-liner across ~10 screens (incl. QuickMsg, which used width()/2 spacing and
was missed before); unifies the separator at headerH()-sepH().
- keyIsPrev()/keyIsNext() in UIScreen.h for the LEFT||PREV / RIGHT||NEXT idiom;
applied to 6 screens, behaviour-preserving. Cancel/context-menu stay
per-screen (they mean different things on different screens).
- rotLabel() dedups the 0/90/180/270 array in Settings.
- Unify selection-row height to lineStep()-1 (Tools/Bot matched the rest).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:23:00 +02:00
|
|
|
});
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
if (renderActivePopup(display)) return 50;
|
|
|
|
|
if (_source == SRC_SCAN) return _scanning ? 200 : 2000;
|
2026-05-21 07:57:21 +02:00
|
|
|
return _count == 0 ? 3000 : 2000;
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool handleInput(char c) override {
|
2026-06-03 08:32:32 +02:00
|
|
|
// ── navigate-to-node view — any nav key returns to detail ─────────────────
|
|
|
|
|
if (_nav) {
|
refactor(ui): shared drawList + header + key-decode helpers
- drawList(): scrollable list skeleton (visible window, keep-sel-in-view,
scroll-column reserve, per-row callback, indicator). Migrate Tools, Bot,
Nearby, Waypoints list and the three QuickMsg lists onto it; centralises the
scroll-clamp/reserve/indicator that each had open-coded (and inconsistently).
- drawCenteredHeader(): centred title + separator, replacing the duplicated
two-liner across ~10 screens (incl. QuickMsg, which used width()/2 spacing and
was missed before); unifies the separator at headerH()-sepH().
- keyIsPrev()/keyIsNext() in UIScreen.h for the LEFT||PREV / RIGHT||NEXT idiom;
applied to 6 screens, behaviour-preserving. Cancel/context-menu stay
per-screen (they mean different things on different screens).
- rotLabel() dedups the 0/90/180/270 array in Settings.
- Unify selection-row height to lineStep()-1 (Tools/Bot matched the rest).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:23:00 +02:00
|
|
|
if (c == KEY_CANCEL || keyIsPrev(c) || keyIsNext(c)) _nav = false;
|
2026-06-03 08:32:32 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// ── popups (same handling in list and detail) ─────────────────────────────
|
|
|
|
|
if (_ping_menu.active) { handlePingMenuInput(c); return true; }
|
|
|
|
|
if (_menu.active) {
|
|
|
|
|
// LEFT/RIGHT on the Sort row toggles the value in-place and rebuilds the
|
|
|
|
|
// label; the popup stays open so the user can keep tapping. Other rows
|
|
|
|
|
// swallow L/R. ENTER on Sort just closes (value changes via L/R only).
|
refactor(ui): shared drawList + header + key-decode helpers
- drawList(): scrollable list skeleton (visible window, keep-sel-in-view,
scroll-column reserve, per-row callback, indicator). Migrate Tools, Bot,
Nearby, Waypoints list and the three QuickMsg lists onto it; centralises the
scroll-clamp/reserve/indicator that each had open-coded (and inconsistently).
- drawCenteredHeader(): centred title + separator, replacing the duplicated
two-liner across ~10 screens (incl. QuickMsg, which used width()/2 spacing and
was missed before); unifies the separator at headerH()-sepH().
- keyIsPrev()/keyIsNext() in UIScreen.h for the LEFT||PREV / RIGHT||NEXT idiom;
applied to 6 screens, behaviour-preserving. Cancel/context-menu stay
per-screen (they mean different things on different screens).
- rotLabel() dedups the 0/90/180/270 array in Settings.
- Unify selection-row height to lineStep()-1 (Tools/Bot matched the rest).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:23:00 +02:00
|
|
|
if (keyIsPrev(c) || keyIsNext(c)) {
|
2026-06-15 09:22:26 +02:00
|
|
|
int i = _menu.selectedIndex();
|
|
|
|
|
if (i >= 0 && i < _menu_action_count && _menu_actions[i] == ACT_SORT) {
|
|
|
|
|
_sort = (_sort == SORT_DIST) ? SORT_TIME : SORT_DIST;
|
|
|
|
|
buildSortLabel();
|
|
|
|
|
refresh();
|
2026-06-03 14:00:16 +02:00
|
|
|
}
|
2026-06-03 08:32:32 +02:00
|
|
|
return true;
|
2026-05-29 21:49:48 +02:00
|
|
|
}
|
2026-06-15 09:22:26 +02:00
|
|
|
auto res = _menu.handleInput(c);
|
|
|
|
|
if (res == PopupMenu::SELECTED) {
|
|
|
|
|
int i = _menu.selectedIndex();
|
|
|
|
|
if (i >= 0 && i < _menu_action_count && _menu_actions[i] != ACT_SORT)
|
|
|
|
|
runAction(_menu_actions[i]);
|
2026-06-03 08:32:32 +02:00
|
|
|
}
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// ── detail view ───────────────────────────────────────────────────────────
|
|
|
|
|
if (_detail) {
|
|
|
|
|
if (c == KEY_CANCEL) { _detail = false; closePingMenu(); return true; }
|
|
|
|
|
if (c == KEY_CONTEXT_MENU) { openActionMenu(); return true; }
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
// ── list view ───────────────────────────────────────────────────────────
|
|
|
|
|
if (c == KEY_CANCEL) {
|
|
|
|
|
if (_source == SRC_SCAN) leaveScan();
|
|
|
|
|
else _task->gotoToolsScreen();
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2026-06-15 09:22:26 +02:00
|
|
|
if (c == KEY_CONTEXT_MENU) { openActionMenu(); return true; }
|
2026-06-16 08:54:11 +02:00
|
|
|
// drawList() reclamps _scroll from _sel every render.
|
2026-06-18 09:16:57 +02:00
|
|
|
if (c == KEY_UP && _count > 0) { _sel = (_sel > 0) ? _sel - 1 : _count - 1; return true; }
|
|
|
|
|
if (c == KEY_DOWN && _count > 0) { _sel = (_sel < _count - 1) ? _sel + 1 : 0; return true; }
|
2026-06-15 09:22:26 +02:00
|
|
|
if (c == KEY_ENTER) {
|
|
|
|
|
if (_count == 0) { if (_source == SRC_STORED) enterScan(); return true; }
|
2026-05-14 16:17:52 +02:00
|
|
|
_detail = true;
|
2026-05-14 16:29:04 +02:00
|
|
|
_detail_refresh_ms = millis();
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2026-06-15 09:22:26 +02:00
|
|
|
if (c == KEY_LEFT) { _filter = (_filter + F_COUNT - 1) % F_COUNT; refresh(); return true; }
|
|
|
|
|
if (c == KEY_RIGHT) { _filter = (_filter + 1) % F_COUNT; refresh(); return true; }
|
feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
y=19, separator at y=28, three data field rows at y=31/41/51;
hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
eliminating double libm and saving ~5-10 KB flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-15 09:22:26 +02:00
|
|
|
const char* NearbyScreen::FILTER_LABELS[F_COUNT] = { "All", "Fav", "Comp", "Rpt", "Room", "Snsr" };
|