mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-02 19:02:00 +00:00
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:
@@ -101,6 +101,9 @@ private:
|
||||
_alarm_check_ms = now_ms;
|
||||
uint32_t now_wall = rtc_clock.getCurrentTime();
|
||||
if (now_wall < 1000000000UL) return; // need a real time sync first
|
||||
// A time zone change (Settings, CLI tz.offset, the app) moves the local
|
||||
// alarm time: reschedule here rather than trusting every caller to say so.
|
||||
if (_alarm_tz != _prefs->tz_offset_hours) { _alarm_tz = _prefs->tz_offset_hours; _alarm_next_fire = 0; }
|
||||
if (_alarm_next_fire == 0) _alarm_next_fire = computeAlarmNextFire(now_wall);
|
||||
if (now_wall < _alarm_next_fire) return;
|
||||
if (now_wall - _alarm_next_fire < ALARM_CATCHUP_SECS) {
|
||||
@@ -124,6 +127,7 @@ private:
|
||||
NodePrefs* _prefs = nullptr;
|
||||
UiEventQueue* _events = nullptr;
|
||||
uint32_t _alarm_next_fire = 0; // unix; 0 = (re)compute lazily once time is valid
|
||||
int8_t _alarm_tz = 0; // the tz_offset_hours _alarm_next_fire was computed with
|
||||
uint32_t _alarm_check_ms = 0; // throttle the wall-clock read to ~2 Hz
|
||||
bool _timer_running = false;
|
||||
uint32_t _timer_deadline_ms = 0;
|
||||
|
||||
@@ -459,6 +459,28 @@ public:
|
||||
e.ack_status = ACK_OK;
|
||||
#ifdef HIST_ARCHIVE
|
||||
if (_arc) _arc->dmUpdate(e);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
// A late ACK for an earlier attempt still proves delivery. Only the latest
|
||||
// attempt's tag is kept, so rebuild the earlier ones (BaseChatMesh::
|
||||
// composeMsgPacket(): timestamp, attempt & 3, text, our public key).
|
||||
for (int i = 0; i < _dm_hist_count; i++) {
|
||||
DmHistEntry& e = _dm_hist[(_dm_hist_head + i) % DM_HIST_MAX];
|
||||
if (!e.outgoing || e.ack_status != ACK_PENDING || e.attempt == 0) continue;
|
||||
uint8_t buf[5 + MSG_TEXT_BUF];
|
||||
int tl = strnlen(e.text, MSG_TEXT_BUF - 1);
|
||||
memcpy(buf, &e.msg_ts, 4);
|
||||
memcpy(&buf[5], e.text, tl);
|
||||
for (uint8_t a = 0; a < e.attempt && a < 4; a++) { // the hash only sees attempt & 3
|
||||
buf[4] = (uint8_t)(a & 3);
|
||||
uint32_t crc;
|
||||
mesh::Utils::sha256((uint8_t*)&crc, 4, buf, 5 + tl, the_mesh.self_id.pub_key, PUB_KEY_SIZE);
|
||||
if (crc != ack_crc) continue;
|
||||
e.ack_status = ACK_OK;
|
||||
#ifdef HIST_ARCHIVE
|
||||
if (_arc) _arc->dmUpdate(e);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,18 +4,17 @@
|
||||
// view can keep showing it. The reply arrives through MyMesh's ping callback,
|
||||
// which is a plain function pointer -- routed here via a single static instance.
|
||||
//
|
||||
// The ping timeout is still decided by the view (NearbyScreen's
|
||||
// PING_TIMEOUT_MS), which calls clear() when it gives up.
|
||||
// The view gives up after timeoutMs() (from the airtime, so it scales with
|
||||
// the spreading factor) and calls clear().
|
||||
|
||||
class PingEngine {
|
||||
public:
|
||||
enum StartResult : uint8_t { STARTED = 0, BUSY, UNSUPPORTED, SEND_FAILED };
|
||||
enum StartResult : uint8_t { STARTED = 0, BUSY, SEND_FAILED };
|
||||
|
||||
void begin(NodePrefs* prefs) { _prefs = prefs; s_instance = this; }
|
||||
|
||||
StartResult start(const uint8_t* pub_key) {
|
||||
if (_active || !pub_key) return BUSY;
|
||||
if (_prefs && _prefs->path_hash_mode > 1) return UNSUPPORTED; // no 3-byte path hash support
|
||||
|
||||
_active = true;
|
||||
_tag = 0;
|
||||
@@ -25,7 +24,11 @@ public:
|
||||
|
||||
// Always install the callback before sending so the response cannot race it.
|
||||
the_mesh.setPingCallback(onPingResult, NULL);
|
||||
_tag = the_mesh.sendPing(pub_key, _prefs ? _prefs->path_hash_mode + 1 : 1);
|
||||
// The trace's hash width follows the path hash mode; a trace has no 3-byte
|
||||
// width, so mode 2 pings with 4 bytes of the key.
|
||||
uint8_t mode = _prefs ? _prefs->path_hash_mode : 0;
|
||||
_timeout_ms = 3000;
|
||||
_tag = the_mesh.sendPing(pub_key, mode >= 2 ? 4 : mode + 1, &_timeout_ms);
|
||||
if (_tag == 0) {
|
||||
clear();
|
||||
return SEND_FAILED;
|
||||
@@ -41,6 +44,7 @@ public:
|
||||
}
|
||||
|
||||
bool isActive() const { return _active; }
|
||||
uint32_t timeoutMs() const { return _timeout_ms; }
|
||||
void getResult(int16_t& snr_out_x4, int16_t& snr_back_x4, uint32_t& rtt_ms) const {
|
||||
snr_out_x4 = _snr_out_x4;
|
||||
snr_back_x4 = _snr_back_x4;
|
||||
@@ -70,6 +74,7 @@ private:
|
||||
int16_t _snr_out_x4 = 0;
|
||||
int16_t _snr_back_x4 = 0;
|
||||
uint32_t _rtt_ms = 0;
|
||||
uint32_t _timeout_ms = 3000;
|
||||
};
|
||||
|
||||
// Header-only Core, single TU (see UiCore.h) -- the one definition lives here.
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
|
||||
namespace radioctl {
|
||||
|
||||
// The TX power range the device's own settings offer (the app and CLI take
|
||||
// -9 up). The top is the board's limit: 30 dBm on the GAT562 30S, not 22.
|
||||
static const int TX_MIN_DBM = 2;
|
||||
static const int TX_MAX_DBM = MAX_LORA_TX_POWER;
|
||||
|
||||
// freq / bw / sf / cr -> the radio (or the repeater profile, while relaying with one).
|
||||
static void applyParams() { the_mesh.applyRepeaterRadio(); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user