|
@@ -513,6 +513,8 @@ static uint8_t g_lastSeq[NSLOT] = { 0 };
|
|
|
static uint8_t g_lastInputRid[NSLOT] = { 0 };
|
|
static uint8_t g_lastInputRid[NSLOT] = { 0 };
|
|
|
static uint32_t g_stNew[NSLOT] = {};
|
|
static uint32_t g_stNew[NSLOT] = {};
|
|
|
static uint32_t g_stCrc[NSLOT] = {}, g_stNoRx[NSLOT] = {};
|
|
static uint32_t g_stCrc[NSLOT] = {}, g_stNoRx[NSLOT] = {};
|
|
|
|
|
+// The controller announced a power-off (F2): its silence is real, not a fade.
|
|
|
|
|
+static bool g_slotPoweredOff[NSLOT] = {};
|
|
|
static uint32_t g_chF1[3] = { 0, 0, 0 };
|
|
static uint32_t g_chF1[3] = { 0, 0, 0 };
|
|
|
// Cycle gate: fires once per g_pollUs; each fire polls every warm slot so all run at ~250 Hz (oversampling
|
|
// Cycle gate: fires once per g_pollUs; each fire polls every warm slot so all run at ~250 Hz (oversampling
|
|
|
// the controller's ~270 Hz report generation so no fresh trackpad sample is dropped -- see config.h).
|
|
// the controller's ~270 Hz report generation so no fresh trackpad sample is dropped -- see config.h).
|
|
@@ -3409,6 +3411,7 @@ uint8_t rfConnTx(uint8_t ch, uint8_t s1, const uint8_t *payload, uint8_t plen,
|
|
|
hapticOnReconnect(s);
|
|
hapticOnReconnect(s);
|
|
|
faultDiagTrace(FR_RFUP, s);
|
|
faultDiagTrace(FR_RFUP, s);
|
|
|
}
|
|
}
|
|
|
|
|
+ g_slotPoweredOff[s] = (rtype == 0xF2);
|
|
|
g_connRx++;
|
|
g_connRx++;
|
|
|
// link alive -> loop() suppresses the redundant E1 beacon
|
|
// link alive -> loop() suppresses the redundant E1 beacon
|
|
|
g_connReplyMs[s] = millis();
|
|
g_connReplyMs[s] = millis();
|
|
@@ -3990,7 +3993,10 @@ uint8_t rfConnTx(uint8_t ch, uint8_t s1, const uint8_t *payload, uint8_t plen,
|
|
|
// (~2 ms) waiting for a reply that never comes -- with two controllers powered off together that is ~100%
|
|
// (~2 ms) waiting for a reply that never comes -- with two controllers powered off together that is ~100%
|
|
|
// radio duty, the churn window the power-off watchdog hang (issue #72 repro) lives in. Recovery stays snappy:
|
|
// radio duty, the churn window the power-off watchdog hang (issue #72 repro) lives in. Recovery stays snappy:
|
|
|
// the first reply re-warms the slot to full rate, so a controller returning from a fade waits <= one retry.
|
|
// the first reply re-warms the slot to full rate, so a controller returning from a fade waits <= one retry.
|
|
|
-#define SLOT_QUIET_MS 300u
|
|
|
|
|
|
|
+// A weak link fades for seconds, and the controller drops the session if it
|
|
|
|
|
+// hears no polls for a while, so a fading slot must keep full-rate polling
|
|
|
|
|
+// through that window. Only an announced power-off backs off immediately.
|
|
|
|
|
+#define SLOT_QUIET_MS 2500u
|
|
|
#define SLOT_QUIET_RETRY_MS 50u
|
|
#define SLOT_QUIET_RETRY_MS 50u
|
|
|
static unsigned long g_slotLastAttemptMs[NSLOT] = {};
|
|
static unsigned long g_slotLastAttemptMs[NSLOT] = {};
|
|
|
|
|
|
|
@@ -4088,6 +4094,14 @@ static void rfConnStep()
|
|
|
// real puck: BARE E3 (just the opcode) -- the controller streams F1 to any E3 ack
|
|
// real puck: BARE E3 (just the opcode) -- the controller streams F1 to any E3 ack
|
|
|
uint8_t p[1] = { 0xE3 };
|
|
uint8_t p[1] = { 0xE3 };
|
|
|
rx = rfConnTx(ch, s1, p, 1);
|
|
rx = rfConnTx(ch, s1, p, 1);
|
|
|
|
|
+ // One retransmit for a slot that was just replying: a
|
|
|
|
|
+ // weak link loses replies, not the session. Skipped for
|
|
|
|
|
+ // absent slots and when it would overrun the poll cycle.
|
|
|
|
|
+ if (!rx && g_connReplyMs[k] &&
|
|
|
|
|
+ nowMs - g_connReplyMs[k] < 300u &&
|
|
|
|
|
+ (uint32_t)(micros() - nowUs) + g_rxWin + 700u <
|
|
|
|
|
+ (uint32_t)g_pollUs)
|
|
|
|
|
+ rx = rfConnTx(ch, s1, p, 1);
|
|
|
}
|
|
}
|
|
|
if (rx)
|
|
if (rx)
|
|
|
g_chF1[0]++;
|
|
g_chF1[0]++;
|
|
@@ -4132,7 +4146,8 @@ static void rfConnStep()
|
|
|
everReplied ? (nowMs - g_connReplyMs[k]) : 0;
|
|
everReplied ? (nowMs - g_connReplyMs[k]) : 0;
|
|
|
bool cold = everReplied && silentMs > SLOT_COLD_MS;
|
|
bool cold = everReplied && silentMs > SLOT_COLD_MS;
|
|
|
bool quiet = everReplied && !cold &&
|
|
bool quiet = everReplied && !cold &&
|
|
|
- silentMs > SLOT_QUIET_MS;
|
|
|
|
|
|
|
+ (silentMs > SLOT_QUIET_MS ||
|
|
|
|
|
+ (g_slotPoweredOff[k] && silentMs > 300u));
|
|
|
bool phantom = !everReplied;
|
|
bool phantom = !everReplied;
|
|
|
unsigned long retry = (cold || phantom) ?
|
|
unsigned long retry = (cold || phantom) ?
|
|
|
SLOT_COLD_RETRY_MS :
|
|
SLOT_COLD_RETRY_MS :
|