mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-08-03 18:56:15 +00:00
feat(repeater): politeness controls, dedicated radio profile, and diagnostics
Settings > Radio gains a repeater toggle, 16 community-suggested radio presets plus manual Freq/SF/BW/CR tuning (digit-by-digit Freq editor), 4 persisted user preset slots, and the packet pool bumped 16->32 so queued retransmits stop starving incoming-packet allocation while relaying. Four opt-in politeness knobs (skip-advert, max-hops, yield, min-SNR), all flood-only and off by default, plus overhear suppression that cancels a queued retransmit if a peer relays the same packet first. Adaptive Power Control is suppressed while relaying (pins TX power to the ceiling) and duty-cycle RX is forced off, since a repeater needs to hear and relay at consistent power. A dedicated Tools > Repeater screen consolidates the toggle, the politeness knobs, and live forwarding stats, plus an optional "Custom" radio profile — a dedicated frequency/SF/BW/CR for relaying, separate from the companion's own network, band-matched to the companion frequency by default and used everywhere a radio change can happen (boot, on-device toggle, app-driven CMD_SET_RADIO_PARAMS, Settings radio edits) so the device never silently falls back to the wrong params mid-relay. Diagnostics gains the actually-forwarded packet count, real heap-free via mallinfo(), a reset-counters popup, and hardened loop-detect bounds. Also: a frequency-floor fix and a float-equality preset-match fix in the repeater profile logic, deduped BW-table/valCol/profile- seeding helpers shared between Settings and the Repeater screen, and a build.sh fix tolerating control characters in `pio project config`'s JSON dump. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
327e659d51
commit
32c50d1cfe
@@ -4,6 +4,7 @@
|
||||
#include <helpers/DeviceDiag.h>
|
||||
#include <Arduino.h>
|
||||
#include "icons.h"
|
||||
#include "PopupMenu.h"
|
||||
#include "../MyMesh.h"
|
||||
|
||||
extern MyMesh the_mesh;
|
||||
@@ -18,9 +19,10 @@ extern MyMesh the_mesh;
|
||||
class DiagnosticsScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
int _scroll = 0;
|
||||
PopupMenu _reset_menu; // Hold Enter → 1-item "Reset counters" action menu (Back dismisses)
|
||||
|
||||
struct Row { const char* label; char value[20]; };
|
||||
static const int MAX_ROWS = 12;
|
||||
static const int MAX_ROWS = 14;
|
||||
Row _rows[MAX_ROWS];
|
||||
int _row_count = 0;
|
||||
|
||||
@@ -76,6 +78,9 @@ class DiagnosticsScreen : public UIScreen {
|
||||
addRxTxRow("Ack/Path", rte_rx, rte_tx);
|
||||
addRxTxRow("Other", oth_rx, oth_tx);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%lu", (unsigned long)the_mesh.getNumForwarded());
|
||||
addRow("Forwarded", buf);
|
||||
|
||||
uint32_t heap_free, heap_total;
|
||||
DeviceDiag::getHeapStats(heap_free, heap_total);
|
||||
if (heap_total > 0) snprintf(buf, sizeof(buf), "%lu/%luKB", (unsigned long)(heap_free / 1024), (unsigned long)(heap_total / 1024));
|
||||
@@ -95,6 +100,20 @@ class DiagnosticsScreen : public UIScreen {
|
||||
addRow("Pool free", buf);
|
||||
snprintf(buf, sizeof(buf), "%d", the_mesh.getOutboundQueueLen());
|
||||
addRow("Queue", buf);
|
||||
|
||||
// Radio error flags since boot/reset, decoded to short tokens (F=queue full,
|
||||
// C=CAD timeout, R=RX-start timeout). "OK" when none have fired.
|
||||
uint16_t err = the_mesh.getErrFlags();
|
||||
if (err == 0) strcpy(buf, "OK");
|
||||
else {
|
||||
buf[0] = '\0';
|
||||
if (err & ERR_EVENT_FULL) strcat(buf, "F ");
|
||||
if (err & ERR_EVENT_CAD_TIMEOUT) strcat(buf, "C ");
|
||||
if (err & ERR_EVENT_STARTRX_TIMEOUT) strcat(buf, "R ");
|
||||
int len = strlen(buf); // drop the trailing space so right-alignment sits flush
|
||||
if (len > 0 && buf[len - 1] == ' ') buf[len - 1] = '\0';
|
||||
}
|
||||
addRow("Errors", buf);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -125,13 +144,27 @@ public:
|
||||
}
|
||||
drawScrollIndicator(display, start_y, visible * item_h, _row_count, visible, _scroll);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
return 1000; // stats refresh once a second — no need for a faster redraw
|
||||
if (_reset_menu.active) _reset_menu.render(display);
|
||||
return _reset_menu.active ? 50 : 1000; // popup wants snappier redraw; stats otherwise refresh once a second
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (_reset_menu.active) {
|
||||
auto res = _reset_menu.handleInput(c); // Back/Cancel dismisses; the only item is "Reset counters"
|
||||
if (res == PopupMenu::SELECTED) {
|
||||
the_mesh.resetStats(); // zeroes Dispatcher per-type counters + Mesh forward count + err flags
|
||||
_task->showAlert("Counters reset", 800);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_UP) { if (_scroll > 0) _scroll--; return true; }
|
||||
if (c == KEY_DOWN) { _scroll++; return true; } // clamped to max_scroll in render()
|
||||
if (c == KEY_CANCEL || c == KEY_CONTEXT_MENU || c == KEY_ENTER) { _task->gotoToolsScreen(); return true; }
|
||||
if (c == KEY_CONTEXT_MENU) { // Hold Enter — same action-menu gesture as the rest of the UI
|
||||
_reset_menu.begin("Diagnostics", 1);
|
||||
_reset_menu.addItem("Reset counters");
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_CANCEL) { _task->gotoToolsScreen(); return true; }
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user