From 7dc4de3c7f7bd95ad5e3b65a2097b4516bb19691 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Thu, 11 Dec 2025 18:56:50 +0100 Subject: [PATCH 1/4] Mark implicit ACK for MQTT as MQTT transport --- src/mesh/MeshModule.h | 5 ++--- src/mqtt/MQTT.cpp | 9 ++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index eda3f8881cb..7e08ee9ca08 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -80,6 +80,8 @@ class MeshModule static AdminMessageHandleResult handleAdminMessageForAllModules(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, meshtastic_AdminMessage *response); + meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, + uint8_t hopLimit = 0); #if HAS_SCREEN virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; } virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset @@ -170,9 +172,6 @@ class MeshModule virtual bool wantUIFrame() { return false; } virtual Observable *getUIFrameObservable() { return NULL; } - meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, - uint8_t hopLimit = 0); - /// Send an error response for the specified packet. meshtastic_MeshPacket *allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p); diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index ad35e152a66..7c33f036012 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -87,10 +87,13 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length) // Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message. // We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node // receives it when we get our own packet back. Then we'll stop our retransmissions. - if (isFromUs(e.packet)) - routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index); - else + if (isFromUs(e.packet)) { + auto pAck = routingModule->allocAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index); + pAck->transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MQTT; + router->sendLocal(pAck); + } else { LOG_INFO("Ignore downlink message we originally sent"); + } return; } if (isFromUs(e.packet)) { From 8f1849e43a480b1d29f9b13db02b36b7d9820909 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Thu, 11 Dec 2025 18:59:36 +0100 Subject: [PATCH 2/4] TRUNK --- src/platform/nrf52/main-nrf52.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp index 5d1ba20ba98..472107229ce 100644 --- a/src/platform/nrf52/main-nrf52.cpp +++ b/src/platform/nrf52/main-nrf52.cpp @@ -337,7 +337,7 @@ void cpuDeepSleep(uint32_t msecToWake) #endif #ifdef TTGO_T_ECHO - // To power off the T-Echo, the display must be set + // To power off the T-Echo, the display must be set // as an input pin; otherwise, there will be leakage current. pinMode(PIN_EINK_CS, INPUT); pinMode(PIN_EINK_DC, INPUT); From 1f2babf6ddfd7b2bd0c1070ce62ce98be0eadb85 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Thu, 11 Dec 2025 19:23:56 +0100 Subject: [PATCH 3/4] Fix build --- src/mesh/MeshModule.h | 7 ++++--- src/modules/RoutingModule.cpp | 6 ++++++ src/modules/RoutingModule.h | 3 +++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/mesh/MeshModule.h b/src/mesh/MeshModule.h index 7e08ee9ca08..e7178bcfedc 100644 --- a/src/mesh/MeshModule.h +++ b/src/mesh/MeshModule.h @@ -80,8 +80,6 @@ class MeshModule static AdminMessageHandleResult handleAdminMessageForAllModules(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request, meshtastic_AdminMessage *response); - meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, - uint8_t hopLimit = 0); #if HAS_SCREEN virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; } virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset @@ -172,6 +170,9 @@ class MeshModule virtual bool wantUIFrame() { return false; } virtual Observable *getUIFrameObservable() { return NULL; } + meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, + uint8_t hopLimit = 0); + /// Send an error response for the specified packet. meshtastic_MeshPacket *allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p); @@ -224,4 +225,4 @@ class MeshModule /** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet * This ensures that if the request packet was sent reliably, the reply is sent that way as well. */ -void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to); \ No newline at end of file +void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to); diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index 05173983c6d..662f5379a8f 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -75,6 +75,12 @@ uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit return Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); // Use the default hop limit } +meshtastic_MeshPacket *RoutingModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, + uint8_t hopLimit) +{ + return MeshModule::allocAckNak(err, to, idFrom, chIndex, hopLimit); +} + RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg) { isPromiscuous = true; diff --git a/src/modules/RoutingModule.h b/src/modules/RoutingModule.h index a4e0679d041..5d4b9596f48 100644 --- a/src/modules/RoutingModule.h +++ b/src/modules/RoutingModule.h @@ -16,6 +16,9 @@ class RoutingModule : public ProtobufModule virtual void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0, bool ackWantsAck = false); + meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, + uint8_t hopLimit = 0); + // Given the hopStart and hopLimit upon reception of a request, return the hop limit to use for the response uint8_t getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit); From 0ab96cc6fb0ccbc859c93a234c274e9357ebb116 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Fri, 12 Dec 2025 09:14:01 +0100 Subject: [PATCH 4/4] Make sure implicit ACKs from MQTT do not stop retransmissions in ReliableRouter --- src/mesh/ReliableRouter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 00066a7a344..7619fc106b1 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -150,7 +150,9 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0; // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records - if (ackId || nakId) { + if ((ackId || nakId) && + // Implicit ACKs from MQTT should not stop retransmissions + !(isFromUs(p) && p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MQTT)) { LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions", ackId ? "ACK" : "NAK", ackId); if (ackId) { stopRetransmission(p->to, ackId);