From 47a82bdb98c46841847be0034818ea3d8da279f1 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Wed, 24 Sep 2025 23:36:14 +0200 Subject: [PATCH 1/4] Fix duplicated lines from merge (#8104) --- variants/nrf52840/heltec_mesh_solar/variant.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/variants/nrf52840/heltec_mesh_solar/variant.h b/variants/nrf52840/heltec_mesh_solar/variant.h index d37e078f9..7c43d8ba7 100644 --- a/variants/nrf52840/heltec_mesh_solar/variant.h +++ b/variants/nrf52840/heltec_mesh_solar/variant.h @@ -69,7 +69,6 @@ No longer populated on PCB #define WIRE_INTERFACES_COUNT 2 -#ifndef HELTEC_MESH_SOLAR_OLED #ifndef HELTEC_MESH_SOLAR_OLED // I2C bus 0 #define PIN_WIRE_SDA (0 + 6) @@ -80,8 +79,6 @@ No longer populated on PCB // Available on header pins, for general use #define PIN_WIRE1_SDA (0 + 30) #define PIN_WIRE1_SCL (0 + 5) -#define PIN_WIRE1_SDA (0 + 30) -#define PIN_WIRE1_SCL (0 + 5) /* * Lora radio From 0ad6b813fcf2076c63052d2a2e6856136fccb46e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 25 Sep 2025 04:45:14 -0500 Subject: [PATCH 2/4] Upgrade trunk (#8105) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 491ab32ba..e9bbba9ff 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,7 +9,7 @@ plugins: lint: enabled: - checkov@3.2.471 - - renovate@41.125.3 + - renovate@41.127.2 - prettier@3.6.2 - trufflehog@3.90.8 - yamllint@1.37.1 From 18058ef507ac1b643dbc295de12acc94e53010d3 Mon Sep 17 00:00:00 2001 From: Chloe Bethel Date: Thu, 25 Sep 2025 10:50:56 +0100 Subject: [PATCH 3/4] Fix 2.4GHz reconfiguration on LR11xx (#8102) Co-authored-by: Ben Meadors --- src/mesh/LR11x0Interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp index f83522c8b..0e23405e5 100644 --- a/src/mesh/LR11x0Interface.cpp +++ b/src/mesh/LR11x0Interface.cpp @@ -155,7 +155,7 @@ template bool LR11x0Interface::reconfigure() if (err != RADIOLIB_ERR_NONE) RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); - err = lora.setBandwidth(bw); + err = lora.setBandwidth(bw, wideLora() && (getFreq() > 1000.0f)); if (err != RADIOLIB_ERR_NONE) RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); From fd5ca8b73c8a92a10490d021cb8ceb104f53e483 Mon Sep 17 00:00:00 2001 From: Clive Blackledge Date: Thu, 25 Sep 2025 03:17:51 -0700 Subject: [PATCH 4/4] Feat/0-cost hops for favorite routers (#7992) * feat: implement router hop preservation for router-to-router communication - Preserve hop_limit when both local device and previous relay are routers/CLIENT_BASE - Only preserve hops for favorite routers to prevent abuse - Apply to both FloodingRouter and NextHopRouter - Update hop counting logic in MeshService for router-to-router communication This allows routers to communicate over longer distances without consuming hop limits, improving mesh network efficiency for infrastructure nodes. * chore: update protobufs submodule to latest * Optimized to check friend list first before nodedb. * Reverting unintended changes * revert: remove protobufs submodule update This reverts the protobufs submodule back to a84657c22 to remove unintended changes from this branch. * Slight rewrite to remove flawed NO_RELAY_NODE logic and added logic to add isFirstHop. If isFirstHop, always decrease hop_limit to avoid retry logic. * DRY code. Remove NodeInfo logic that was left over. * Trunk formatting --- src/mesh/FloodingRouter.cpp | 11 ++++++-- src/mesh/NextHopRouter.cpp | 9 ++++++- src/mesh/Router.cpp | 52 +++++++++++++++++++++++++++++++++++++ src/mesh/Router.h | 12 +++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index f805055c8..14d17778e 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -1,5 +1,6 @@ #include "FloodingRouter.h" - +#include "MeshTypes.h" +#include "NodeDB.h" #include "configuration.h" #include "mesh-pb-constants.h" @@ -90,7 +91,12 @@ void FloodingRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p) if (isRebroadcaster()) { meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it - tosend->hop_limit--; // bump down the hop count + // Use shared logic to determine if hop_limit should be decremented + if (shouldDecrementHopLimit(p)) { + tosend->hop_limit--; // bump down the hop count + } else { + LOG_INFO("favorite-ROUTER/CLIENT_BASE-to-ROUTER/CLIENT_BASE flood: preserving hop_limit"); + } #if USERPREFS_EVENT_MODE if (tosend->hop_limit > 2) { // if we are "correcting" the hop_limit, "correct" the hop_start by the same amount to preserve hops away. @@ -98,6 +104,7 @@ void FloodingRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p) tosend->hop_limit = 2; } #endif + tosend->next_hop = NO_NEXT_HOP_PREFERENCE; // this should already be the case, but just in case LOG_INFO("Rebroadcast received floodmsg"); diff --git a/src/mesh/NextHopRouter.cpp b/src/mesh/NextHopRouter.cpp index 9bb8b240c..791b6a749 100644 --- a/src/mesh/NextHopRouter.cpp +++ b/src/mesh/NextHopRouter.cpp @@ -1,4 +1,5 @@ #include "NextHopRouter.h" +#include "NodeDB.h" NextHopRouter::NextHopRouter() {} @@ -108,7 +109,13 @@ bool NextHopRouter::perhapsRelay(const meshtastic_MeshPacket *p) meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it LOG_INFO("Relaying received message coming from %x", p->relay_node); - tosend->hop_limit--; // bump down the hop count + // Use shared logic to determine if hop_limit should be decremented + if (shouldDecrementHopLimit(p)) { + tosend->hop_limit--; // bump down the hop count + } else { + LOG_INFO("Router/CLIENT_BASE-to-favorite-router/CLIENT_BASE relay: preserving hop_limit"); + } + NextHopRouter::send(tosend); return true; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 09fb079c5..171c383ba 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -69,6 +69,58 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA cryptLock = new concurrency::Lock(); } +bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p) +{ + // First hop MUST always decrement to prevent retry issues + bool isFirstHop = (p->hop_start != 0 && p->hop_start == p->hop_limit); + if (isFirstHop) { + return true; // Always decrement on first hop + } + + // Check if both local device and previous relay are routers (including CLIENT_BASE) + bool localIsRouter = + IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_ROUTER, meshtastic_Config_DeviceConfig_Role_ROUTER_LATE, + meshtastic_Config_DeviceConfig_Role_CLIENT_BASE); + + // If local device isn't a router, always decrement + if (!localIsRouter) { + return true; + } + + // For subsequent hops, check if previous relay is a favorite router + // Optimized search for favorite routers with matching last byte + // Check ordering optimized for IoT devices (cheapest checks first) + for (int i = 0; i < nodeDB->getNumMeshNodes(); i++) { + meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); + if (!node) + continue; + + // Check 1: is_favorite (cheapest - single bool) + if (!node->is_favorite) + continue; + + // Check 2: has_user (cheap - single bool) + if (!node->has_user) + continue; + + // Check 3: role check (moderate cost - multiple comparisons) + if (!IS_ONE_OF(node->user.role, meshtastic_Config_DeviceConfig_Role_ROUTER, + meshtastic_Config_DeviceConfig_Role_ROUTER_LATE, meshtastic_Config_DeviceConfig_Role_CLIENT_BASE)) { + continue; + } + + // Check 4: last byte extraction and comparison (most expensive) + if (nodeDB->getLastByteOfNodeNum(node->num) == p->relay_node) { + // Found a favorite router match + LOG_DEBUG("Identified favorite relay router 0x%x from last byte 0x%x", node->num, p->relay_node); + return false; // Don't decrement hop_limit + } + } + + // No favorite router match found, decrement hop_limit + return true; +} + /** * do idle processing * Mostly looking in our incoming rxPacket queue and calling handleReceived. diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 58ca50f3d..075248af9 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -104,6 +104,18 @@ class Router : protected concurrency::OSThread, protected PacketHistory */ virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) { return false; } + /** + * Determine if hop_limit should be decremented for a relay operation. + * Returns false (preserve hop_limit) only if all conditions are met: + * - It's NOT the first hop (first hop must always decrement) + * - Local device is a ROUTER, ROUTER_LATE, or CLIENT_BASE + * - Previous relay is a favorite ROUTER, ROUTER_LATE, or CLIENT_BASE + * + * @param p The packet being relayed + * @return true if hop_limit should be decremented, false to preserve it + */ + bool shouldDecrementHopLimit(const meshtastic_MeshPacket *p); + /** * Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to * update routing tables etc... based on what we overhear (even for messages not destined to our node)