touch: stop the deferred-login branch eating the companion app's responses (#124)

Partial fix for "no ping and telemetry response" reported from the device AND the
phone app. onContactResponse's deferred-login branch runs before every pending_*
matcher and, while armed, consumed ANY response from that contact and returned. Its
comment justifies this with "we have sent no REQ to this contact, so any RESPONSE
here is the login reply" -- true of the UI, but the companion app can independently
have a STATUS / TELEMETRY / BINARY request in flight to the same node. There is also
no distinct login-failure code on the wire (a failure is just "not
RESP_SERVER_LOGIN_OK"), so a non-OK frame is indistinguishable from the app's reply,
and the app's response was swallowed with the phone left waiting.

A LOGIN_OK is unambiguously ours, so it is still always consumed. Otherwise the
branch now falls through when the app has a request pending on that same contact,
letting its matcher see the frame; the UI's own reply deadline disarms us, which is
all the early disarm here ever provided. Behaviour is unchanged whenever the app is
not waiting, which is the common case.

Deliberately NOT changed: sendStatusPingForUI / sendTelemetryRequestForUI zero the
app's pending_status / pending_telemetry on entry. That looks like deliberate
arbitration so a UI-initiated request routes to the UI rather than the phone, and
removing it could double-handle a response. Wants its own change and a bench test.

Note this is unlikely to be the whole of #124: the leading theory remains the
repeater's per-client replay high-water mark, which no client-side change can undo.

Builds clean on T-Deck and Heltec V4 TFT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kaj SchittecatandClaude Opus 4.8 committed 2026-07-30 11:38:36 +02:00
1 parent 8e6a6d5f64
commit 8ae0df9400
1 file changed
+23 -7
+23 -7
View File
@@ -2241,14 +2241,30 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
if (_ui_login_then && len > 4 && memcmp(&_ui_login_then, contact.id.pub_key, 4) == 0) {
const bool login_ok = (data[4] == RESP_SERVER_LOGIN_OK)
|| (len > 5 && memcmp(&data[4], "OK", 2) == 0);
const UiReqKind k = _ui_login_then_kind;
cancelUIDeferredLogin();
if (login_ok) {
ContactInfo& rc = const_cast<ContactInfo&>(contact);
if (k == UiReqKind::Telemetry) sendTelemetryRequestForUI(rc);
else sendStatusPingForUI(rc);
// The comment above assumes ANY response arriving while armed must be our login reply.
// That holds for the UI, but the COMPANION APP can have its own STATUS / TELEMETRY /
// BINARY request in flight to this same node, and there is no distinct "login failed"
// code on the wire (a failure is merely "not RESP_SERVER_LOGIN_OK"), so a non-OK frame
// is indistinguishable from the app's reply. Swallowing it here ate the app's response
// and returned, leaving the phone waiting forever — a contributor to the "no ping or
// telemetry response, from the device AND the app" reports (#124).
// A LOGIN_OK is unambiguously ours, so always take it. Otherwise, if the app is waiting
// on this same contact, fall through and let its matcher have the frame; our own reply
// deadline still disarms us, which is all the early disarm here ever bought.
const bool app_waiting =
(pending_status && memcmp(&pending_status, contact.id.pub_key, 4) == 0) ||
(pending_telemetry && memcmp(&pending_telemetry, contact.id.pub_key, 4) == 0) ||
(pending_req && memcmp(&pending_req, contact.id.pub_key, 4) == 0);
if (login_ok || !app_waiting) {
const UiReqKind k = _ui_login_then_kind;
cancelUIDeferredLogin();
if (login_ok) {
ContactInfo& rc = const_cast<ContactInfo&>(contact);
if (k == UiReqKind::Telemetry) sendTelemetryRequestForUI(rc);
else sendStatusPingForUI(rc);
}
return; // login frame consumed (OK fired the REQ; fail disarmed)
}
return; // login frame consumed (OK fired the REQ; fail disarmed)
}
#endif