From 6bc8dd28d438ea5a1d59cefdfec121f6f88baf69 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 16 Jul 2025 18:51:18 +1000 Subject: [PATCH] * CommonCLI: new "multi.acks" config setting --- examples/simple_repeater/main.cpp | 3 ++ examples/simple_room_server/main.cpp | 15 ++++++-- src/Mesh.cpp | 57 ++++++++-------------------- src/Mesh.h | 5 +++ src/helpers/CommonCLI.cpp | 11 +++++- src/helpers/CommonCLI.h | 2 +- 6 files changed, 44 insertions(+), 49 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 2ec94c4b..cee3a008 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -339,6 +339,9 @@ protected: int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } + uint8_t getExtraAckTransmitCount() const override { + return _prefs.multi_acks; + } void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override { if (packet->getPayloadType() == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin client (unknown at this stage) diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 87038ac7..1ab08d9b 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -424,6 +424,9 @@ protected: int getAGCResetInterval() const override { return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds } + uint8_t getExtraAckTransmitCount() const override { + return _prefs.multi_acks; + } bool allowPacketForward(const mesh::Packet* packet) override { if (_prefs.disable_fwd) return false; @@ -583,12 +586,16 @@ protected: if (ack) sendFlood(ack, TXT_ACK_DELAY); delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS; } else { - mesh::Packet* a1 = createMultiAck(ack_hash, 1); - if (a1) sendDirect(a1, client->out_path, client->out_path_len, TXT_ACK_DELAY); + uint32_t d = TXT_ACK_DELAY; + if (getExtraAckTransmitCount() > 0) { + mesh::Packet* a1 = createMultiAck(ack_hash, 1); + if (a1) sendDirect(a1, client->out_path, client->out_path_len, d); + d += 300; + } mesh::Packet* a2 = createAck(ack_hash); - if (a2) sendDirect(a2, client->out_path, client->out_path_len, TXT_ACK_DELAY + 300); - delay_millis = TXT_ACK_DELAY + REPLY_DELAY_MILLIS + 300; + if (a2) sendDirect(a2, client->out_path, client->out_path_len, d); + delay_millis = d + REPLY_DELAY_MILLIS; } } else { delay_millis = 0; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 0d9b88c7..b055d811 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -22,6 +22,9 @@ uint32_t Mesh::getRetransmitDelay(const mesh::Packet* packet) { uint32_t Mesh::getDirectRetransmitDelay(const Packet* packet) { return 0; // by default, no delay } +uint8_t Mesh::getExtraAckTransmitCount() const { + return 0; +} uint32_t Mesh::getCADFailRetryDelay() const { return _rng->nextInt(1, 4)*120; @@ -99,7 +102,6 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } else if (!_tables->hasSeen(pkt)) { onAckRecv(pkt, ack_crc); action = routeRecvPacket(pkt); - // routeRecvAcks(pkt, 0); // experimental, double Acks in flood mode(?) } break; } @@ -280,7 +282,6 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { memcpy(&ack_crc, tmp.payload, 4); onAckRecv(&tmp, ack_crc); - // routeRecvAcks(&tmp, ((uint32_t)remaining) * 600); // expect multipart ACK 300ms apart (x2) //action = routeRecvPacket(&tmp); // NOTE: currently not needed, as multipart ACKs not sent Flood } } else { @@ -324,37 +325,6 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) { return ACTION_RELEASE; } -#if 0 -void Mesh::routeRecvAcks(Packet* packet, uint32_t delay_millis) { - if (packet->isRouteFlood() && !packet->isMarkedDoNotRetransmit() - && packet->path_len + PATH_HASH_SIZE <= MAX_PATH_SIZE && allowPacketForward(packet)) { - // append this node's hash to 'path' - packet->path_len += self_id.copyHashTo(&packet->path[packet->path_len]); - - uint32_t crc; - memcpy(&crc, packet->payload, 4); - - delay_millis += getRetransmitDelay(packet); - auto a1 = createMultiAck(crc, 1); - if (a1) { - memcpy(a1->path, packet->path, a1->path_len = packet->path_len); - a1->header &= ~PH_ROUTE_MASK; - a1->header |= ROUTE_TYPE_FLOOD; - sendPacket(a1, 1, delay_millis); - } - - delay_millis += 300; - auto a2 = createAck(crc); - if (a2) { - memcpy(a2->path, packet->path, a2->path_len = packet->path_len); - a2->header &= ~PH_ROUTE_MASK; - a2->header |= ROUTE_TYPE_FLOOD; - sendPacket(a2, 1, delay_millis); - } - } -} -#endif - DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) { uint8_t remaining = pkt->payload[0] >> 4; // num of packets in this multipart sequence still to be sent uint8_t type = pkt->payload[0] & 0x0F; @@ -369,7 +339,7 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet* pkt) { if (!_tables->hasSeen(&tmp)) { // don't retransmit! removeSelfFromPath(&tmp); - routeDirectRecvAcks(&tmp, ((uint32_t)remaining) * 600); // expect multipart ACKs 300ms apart (x2) + routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300); // expect multipart ACKs 300ms apart (x2) } } return ACTION_RELEASE; @@ -380,16 +350,19 @@ void Mesh::routeDirectRecvAcks(Packet* packet, uint32_t delay_millis) { uint32_t crc; memcpy(&crc, packet->payload, 4); - delay_millis += getDirectRetransmitDelay(packet); - auto a1 = createMultiAck(crc, 1); - if (a1) { - memcpy(a1->path, packet->path, a1->path_len = packet->path_len); - a1->header &= ~PH_ROUTE_MASK; - a1->header |= ROUTE_TYPE_DIRECT; - sendPacket(a1, 0, delay_millis); + uint8_t extra = getExtraAckTransmitCount(); + while (extra > 0) { + delay_millis += getDirectRetransmitDelay(packet) + 300; + auto a1 = createMultiAck(crc, extra); + if (a1) { + memcpy(a1->path, packet->path, a1->path_len = packet->path_len); + a1->header &= ~PH_ROUTE_MASK; + a1->header |= ROUTE_TYPE_DIRECT; + sendPacket(a1, 0, delay_millis); + } + extra--; } - delay_millis += 300; auto a2 = createAck(crc); if (a2) { memcpy(a2->path, packet->path, a2->path_len = packet->path_len); diff --git a/src/Mesh.h b/src/Mesh.h index da734572..a8fdb2a4 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -59,6 +59,11 @@ protected: */ virtual uint32_t getDirectRetransmitDelay(const Packet* packet); + /** + * \returns number of extra (Direct) ACK transmissions wanted. + */ + virtual uint8_t getExtraAckTransmitCount() const; + /** * \brief Perform search of local DB of peers/contacts. * \returns Number of peers with matching hash diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index df510c28..afb7dfbc 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -51,7 +51,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112 file.read((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113 file.read((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 - file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 + file.read((uint8_t *) &_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 file.read((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 file.read(pad, 3); // 121 @@ -69,6 +69,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->sf = constrain(_prefs->sf, 7, 12); _prefs->cr = constrain(_prefs->cr, 5, 8); _prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30); + _prefs->multi_acks = constrain(_prefs->multi_acks, 0, 1); file.close(); } @@ -106,7 +107,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112 file.write((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113 file.write((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114 - file.write((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115 + file.write((uint8_t *) &_prefs->multi_acks, sizeof(_prefs->multi_acks)); // 115 file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116 file.write((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120 file.write(pad, 3); // 121 @@ -180,6 +181,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold); } else if (memcmp(config, "agc.reset.interval", 18) == 0) { sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4); + } else if (memcmp(config, "multi.acks", 10) == 0) { + sprintf(reply, "> %d", (uint32_t) _prefs->multi_acks); } else if (memcmp(config, "allow.read.only", 15) == 0) { sprintf(reply, "> %s", _prefs->allow_read_only ? "on" : "off"); } else if (memcmp(config, "flood.advert.interval", 21) == 0) { @@ -235,6 +238,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch _prefs->agc_reset_interval = atoi(&config[19]) / 4; savePrefs(); strcpy(reply, "OK"); + } else if (memcmp(config, "multi.acks ", 11) == 0) { + _prefs->multi_acks = atoi(&config[11]); + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "allow.read.only ", 16) == 0) { _prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0; savePrefs(); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index b50bf7f3..91a5bd3b 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -21,7 +21,7 @@ struct NodePrefs { // persisted to file uint8_t sf; uint8_t cr; uint8_t allow_read_only; - uint8_t reserved2; + uint8_t multi_acks; float bw; uint8_t flood_max; uint8_t interference_threshold;