fix(companion): CLI NaN, APC on TX changes, tz-aware alarm, late ACKs, ping timeout and widths, TX limits

- set af / dutycycle / rxdelay reject nan.
- A TX power change from the app or CLI re-baselines APC.
- CMD_SET_RADIO_PARAMS stores repeat as 0/1.
- The alarm reschedules itself when the time zone changes.
- A late ACK for an earlier DM attempt marks it delivered.
- A new auto-advert interval counts from the last advert.
- Both UIs take the TX power range from radioctl, up to MAX_LORA_TX_POWER
  (30 dBm on the GAT562 30S).
- The ping timeout comes from the airtime; 3-byte path hash mode pings with
  4-byte trace hashes, and trace flags are right for width 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-10-12 18:00:49 +02:00
co-authored by Claude Opus 5.5
parent cd294613ea
commit 28d8291e33
14 changed files with 76 additions and 29 deletions
+10 -1
View File
@@ -1940,7 +1940,7 @@ void MyMesh::handleCmdFrame(size_t len) {
_prefs.cr = cr;
_prefs.freq = (float)freq / 1000.0;
_prefs.bw = (float)bw / 1000.0;
_prefs.client_repeat = repeat;
_prefs.client_repeat = repeat ? 1 : 0; // a bool for every reader, whatever byte the app sent
savePrefs();
applyRepeaterRadio(); // companion params, or the repeater profile if relaying with one set
@@ -1971,6 +1971,7 @@ void MyMesh::handleCmdFrame(size_t len) {
// Store what the radio actually applied, not the raw request -- on a
// board with an external-PA gain curve those can differ (see setTxPower()).
_prefs.tx_power_dbm = radio_driver.getTxPower();
if (_prefs.tx_apc) applyApc(); // the new ceiling: re-baseline APC, as the device's own setting does
savePrefs();
writeOKFrame();
}
@@ -2882,10 +2883,18 @@ void MyMesh::loop() {
_last_rtc_save_ms = millis();
}
if (_prefs.advert_auto_interval_sec != _auto_advert_sec) {
// Interval changed (Settings, CLI, app): count the new one from the last
// advert instead of waiting out what was left of the old one.
bool first = _auto_advert_sec == UINT32_MAX; // boot: keep the advert due straight away
_auto_advert_sec = _prefs.advert_auto_interval_sec;
if (!first) _next_auto_advert_ms = _last_auto_advert_ms + _auto_advert_sec * 1000UL;
}
if (_prefs.advert_auto_interval_sec > 0 && millisHasNowPassed(_next_auto_advert_ms)) {
// The position only where Privacy lets adverts carry it, at its precision.
mesh::Packet* pkt = createOwnAdvert();
if (pkt) sendZeroHop(pkt);
_last_auto_advert_ms = _ms->getMillis();
_next_auto_advert_ms = futureMillis(_prefs.advert_auto_interval_sec * 1000UL);
}
}