From 9e61b56e7062e4ba5307c913276cbe2f6ac393a3 Mon Sep 17 00:00:00 2001 From: kallanreed <3761006+kallanreed@users.noreply.github.com> Date: Wed, 22 Oct 2025 16:44:20 -0700 Subject: [PATCH 1/7] Use correct macro for logging test --- src/helpers/sensors/EnvironmentSensorManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index bb70c0b5..024d4aad 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -632,7 +632,7 @@ void EnvironmentSensorManager::start_gps() { _location->begin(); _location->reset(); -#ifndef PIN_GPS_RESET +#ifndef PIN_GPS_EN MESH_DEBUG_PRINTLN("Start GPS is N/A on this board. Actual GPS state unchanged"); #endif } From 8e404e9aea4e967b564e1474f976375e8b285e8d Mon Sep 17 00:00:00 2001 From: mesher-de <250407498+mesher-de@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:56:18 +0100 Subject: [PATCH 2/7] add advert.zerohop command to CLI --- src/helpers/CommonCLI.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 6dcf7018..797cce95 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -200,6 +200,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch // Reset clock getRTCClock()->setCurrentTime(1715770351); // 15 May 2024, 8:50pm _board->reboot(); // doesn't return + } else if (memcmp(command, "advert.zerohop", 14) == 0 && (command[14] == 0 || command[14] == ' ')) { + // send zerohop advert + _callbacks->sendSelfAdvertisement(1500, false); // longer delay, give CLI response time to be sent first + strcpy(reply, "OK - zerohop advert sent"); } else if (memcmp(command, "advert", 6) == 0) { // send flood advert _callbacks->sendSelfAdvertisement(1500, true); // longer delay, give CLI response time to be sent first From b43319d1a4930f487584f9743e29f45137749d2f Mon Sep 17 00:00:00 2001 From: Robert Ekl Date: Mon, 23 Feb 2026 18:53:20 -0600 Subject: [PATCH 3/7] docs: correct CLI command forms and targeted typos --- docs/cli_commands.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index c316bd6c..02e14ea2 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -51,7 +51,7 @@ - `time ` **Parameters:** -- `epoc_seconds`: Unix epoc time +- `epoch_seconds`: Unix epoch time --- @@ -134,7 +134,7 @@ --- -### End capture of rx log to node sotrage +### End capture of rx log to node storage **Usage:** `log stop` --- @@ -198,7 +198,7 @@ **Default:** Varies by board -**Notes:** This setting only controls the power level of the LoRa chip. Some nodes have an additional power amplifier stage which increases the total output. Referr to the node's manual for the correct setting to use. **Setting a value too high may violate the laws in your country.** +**Notes:** This setting only controls the power level of the LoRa chip. Some nodes have an additional power amplifier stage which increases the total output. Refer to the node's manual for the correct setting to use. **Setting a value too high may violate the laws in your country.** --- @@ -228,6 +228,7 @@ **Default:** `869.525` **Note:** Requires reboot to apply +**Serial Only:** `set freq ` ### System @@ -293,17 +294,16 @@ #### View or change this node's admin password **Usage:** -- `get password` -- `set password ` +- `password ` **Parameters:** -- `password`: Admin password +- `new_password`: New admin password **Set by build flag:** `ADMIN_PASSWORD` **Default:** `password` -**Note:** Echoed back for confirmation +**Note:** Command reply echoes the updated password for confirmation. **Note:** Any node using this password will be added to the admin ACL list. @@ -768,7 +768,7 @@ region save - `gps advert ` **Parameters:** -- `policy`: `none`|`shared`|`prefs` +- `policy`: `none`|`share`|`prefs` - `none`: don't include location in adverts - `share`: share gps location (from SensorManager) - `prefs`: location stored in node's lat and lon settings From 90fcd1043f098e28d66e9c45b527ccffb226ad9b Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Mon, 2 Mar 2026 23:11:52 +0100 Subject: [PATCH 4/7] Fix ikoka handheld build The extends was wrong. --- variants/ikoka_handheld_nrf/platformio.ini | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/variants/ikoka_handheld_nrf/platformio.ini b/variants/ikoka_handheld_nrf/platformio.ini index d2bbeffe..d387164b 100644 --- a/variants/ikoka_handheld_nrf/platformio.ini +++ b/variants/ikoka_handheld_nrf/platformio.ini @@ -48,7 +48,7 @@ build_src_filter = ${ikoka_handheld_nrf.build_src_filter} +<../examples/companion_radio/*.cpp> [env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble] -extends = ikoka_nrf52 +extends = ikoka_handheld_nrf build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags} -D BLE_PIN_CODE=123456 -D LORA_TX_POWER=20 @@ -56,7 +56,7 @@ build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter} + [env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble] -extends = ikoka_nrf52 +extends = ikoka_handheld_nrf build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags} -D BLE_PIN_CODE=123456 -D LORA_TX_POWER=20 @@ -65,20 +65,20 @@ build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter} + [env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb] -extends = ikoka_nrf52 +extends = ikoka_handheld_nrf build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags} -D LORA_TX_POWER=20 build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter} [env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb] -extends = ikoka_nrf52 +extends = ikoka_handheld_nrf build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags} -D LORA_TX_POWER=20 -D DISPLAY_ROTATION=2 build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter} [env:ikoka_handheld_nrf_e22_30dbm_repeater] -extends = ikoka_nrf52 +extends = ikoka_handheld_nrf build_flags = ${ikoka_handheld_nrf.build_flags} -D ADVERT_NAME='"ikoka_handheld Repeater"' @@ -91,7 +91,7 @@ build_src_filter = ${ikoka_handheld_nrf.build_src_filter} +<../examples/simple_repeater/*.cpp> [env:ikoka_handheld_nrf_e22_30dbm_room_server] -extends = ikoka_nrf52 +extends = ikoka_handheld_nrf build_flags = ${ikoka_handheld_nrf.build_flags} -D ADVERT_NAME='"ikoka_handheld Room"' From 780720c117e2994000ab9ba3ac783ada77578025 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Mon, 2 Mar 2026 23:13:13 +0100 Subject: [PATCH 5/7] and board! --- variants/ikoka_handheld_nrf/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/ikoka_handheld_nrf/platformio.ini b/variants/ikoka_handheld_nrf/platformio.ini index d387164b..1f920b62 100644 --- a/variants/ikoka_handheld_nrf/platformio.ini +++ b/variants/ikoka_handheld_nrf/platformio.ini @@ -1,5 +1,6 @@ [ikoka_handheld_nrf] extends = nrf52_base +board = seeed-xiao-afruitnrf52-nrf52840 build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I lib/nrf52/s140_nrf52_7.3.0_API/include From c0c9c17cf59e422f1fa6b547b03f3e5927c9ad7e Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Mon, 2 Mar 2026 23:15:34 +0100 Subject: [PATCH 6/7] and buildscript --- variants/ikoka_handheld_nrf/platformio.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/variants/ikoka_handheld_nrf/platformio.ini b/variants/ikoka_handheld_nrf/platformio.ini index 1f920b62..821b0057 100644 --- a/variants/ikoka_handheld_nrf/platformio.ini +++ b/variants/ikoka_handheld_nrf/platformio.ini @@ -1,6 +1,7 @@ [ikoka_handheld_nrf] extends = nrf52_base board = seeed-xiao-afruitnrf52-nrf52840 +board_build.ldscript = boards/nrf52840_s140_v7.ld build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I lib/nrf52/s140_nrf52_7.3.0_API/include @@ -50,6 +51,7 @@ build_src_filter = ${ikoka_handheld_nrf.build_src_filter} [env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_ble] extends = ikoka_handheld_nrf +board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags} -D BLE_PIN_CODE=123456 -D LORA_TX_POWER=20 @@ -58,6 +60,7 @@ build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter} [env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_ble] extends = ikoka_handheld_nrf +board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags} -D BLE_PIN_CODE=123456 -D LORA_TX_POWER=20 @@ -67,12 +70,14 @@ build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter} [env:ikoka_handheld_nrf_e22_30dbm_096_companion_radio_usb] extends = ikoka_handheld_nrf +board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags} -D LORA_TX_POWER=20 build_src_filter = ${ikoka_handheld_nrf_ssd1306_companion.build_src_filter} [env:ikoka_handheld_nrf_e22_30dbm_096_rotated_companion_radio_usb] extends = ikoka_handheld_nrf +board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld build_flags = ${ikoka_handheld_nrf_ssd1306_companion.build_flags} -D LORA_TX_POWER=20 -D DISPLAY_ROTATION=2 From e233346bf04727597636f03cc5cef9c22d705ea3 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Thu, 5 Mar 2026 16:26:09 +1100 Subject: [PATCH 7/7] * repeater: new "get/set loop.detect {off | minimal | moderate | strict }" --- examples/simple_repeater/MyMesh.cpp | 31 +++++++++++++++++++++++++ examples/simple_repeater/MyMesh.h | 1 + src/helpers/CommonCLI.cpp | 36 +++++++++++++++++++++++++++-- src/helpers/CommonCLI.h | 6 +++++ 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 81c1dcb4..e53e66fd 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -396,6 +396,23 @@ File MyMesh::openAppend(const char *fname) { #endif } +static uint8_t max_loop_minimal[] = { 0, /* 1-byte */ 4, /* 2-byte */ 2, /* 3-byte */ 1 }; +static uint8_t max_loop_moderate[] = { 0, /* 1-byte */ 2, /* 2-byte */ 1, /* 3-byte */ 1 }; +static uint8_t max_loop_strict[] = { 0, /* 1-byte */ 1, /* 2-byte */ 1, /* 3-byte */ 1 }; + +bool MyMesh::isLooped(const mesh::Packet* packet, const uint8_t max_counters[]) { + uint8_t hash_size = packet->getPathHashSize(); + uint8_t hash_count = packet->getPathHashCount(); + uint8_t n = 0; + const uint8_t* path = packet->path; + while (hash_count > 0) { // count how many times this node is already in the path + if (self_id.isHashMatch(path, hash_size)) n++; + hash_count--; + path += hash_size; + } + return n >= max_counters[hash_size]; +} + bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (_prefs.disable_fwd) return false; if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false; @@ -403,6 +420,20 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) { MESH_DEBUG_PRINTLN("allowPacketForward: unknown transport code, or wildcard not allowed for FLOOD packet"); return false; } + if (packet->isRouteFlood() && _prefs.loop_detect != LOOP_DETECT_OFF) { + const uint8_t* maximums; + if (_prefs.loop_detect == LOOP_DETECT_MINIMAL) { + maximums = max_loop_minimal; + } else if (_prefs.loop_detect == LOOP_DETECT_MINIMAL) { + maximums = max_loop_moderate; + } else { + maximums = max_loop_strict; + } + if (isLooped(packet, maximums)) { + MESH_DEBUG_PRINTLN("allowPacketForward: FLOOD packet loop detected!"); + return false; + } + } return true; } diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 591f6366..fe6a7f83 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -128,6 +128,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { mesh::Packet* createSelfAdvert(); File openAppend(const char* fname); + bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]); protected: float getAirtimeBudgetFactor() const override { diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 9a580f58..f3cba406 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -64,7 +64,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { 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((uint8_t *)&_prefs->path_hash_mode, sizeof(_prefs->path_hash_mode)); // 121 - file.read(pad, 2); // 122 + file.read((uint8_t *)&_prefs->loop_detect, sizeof(_prefs->loop_detect)); // 122 + file.read(pad, 1); // 123 file.read((uint8_t *)&_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 file.read((uint8_t *)&_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 file.read((uint8_t *)&_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 @@ -150,7 +151,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { 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((uint8_t *)&_prefs->path_hash_mode, sizeof(_prefs->path_hash_mode)); // 121 - file.write(pad, 2); // 122 + file.write((uint8_t *)&_prefs->loop_detect, sizeof(_prefs->loop_detect)); // 122 + file.write(pad, 1); // 123 file.write((uint8_t *)&_prefs->flood_max, sizeof(_prefs->flood_max)); // 124 file.write((uint8_t *)&_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125 file.write((uint8_t *)&_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126 @@ -334,6 +336,16 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch *reply = 0; // set null terminator } else if (memcmp(config, "path.hash.mode", 14) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->path_hash_mode); + } else if (memcmp(config, "loop.detect", 11) == 0) { + if (_prefs->loop_detect == LOOP_DETECT_OFF) { + strcpy(reply, "> off"); + } else if (_prefs->loop_detect == LOOP_DETECT_MINIMAL) { + strcpy(reply, "> minimal"); + } else if (_prefs->loop_detect == LOOP_DETECT_MODERATE) { + strcpy(reply, "> moderate"); + } else { + strcpy(reply, "> strict"); + } } else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) { sprintf(reply, "> %d", (int32_t) _prefs->tx_power_dbm); } else if (memcmp(config, "freq", 4) == 0) { @@ -575,6 +587,26 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else { strcpy(reply, "Error, must be 0,1, or 2"); } + } else if (memcmp(config, "loop.detect ", 12) == 0) { + config += 12; + uint8_t mode; + if (memcmp(config, "off", 3) == 0) { + mode = LOOP_DETECT_OFF; + } else if (memcmp(config, "minimal", 7) == 0) { + mode = LOOP_DETECT_MINIMAL; + } else if (memcmp(config, "moderate", 8) == 0) { + mode = LOOP_DETECT_MODERATE; + } else if (memcmp(config, "strict", 6) == 0) { + mode = LOOP_DETECT_STRICT; + } else { + mode = 0xFF; + strcpy(reply, "Error, must be: off, minimal, moderate, or strict"); + } + if (mode != 0xFF) { + _prefs->loop_detect = mode; + savePrefs(); + strcpy(reply, "OK"); + } } else if (memcmp(config, "tx ", 3) == 0) { _prefs->tx_power_dbm = atoi(&config[3]); savePrefs(); diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 1e454ec2..51013640 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -13,6 +13,11 @@ #define ADVERT_LOC_SHARE 1 #define ADVERT_LOC_PREFS 2 +#define LOOP_DETECT_OFF 0 +#define LOOP_DETECT_MINIMAL 1 +#define LOOP_DETECT_MODERATE 2 +#define LOOP_DETECT_STRICT 3 + struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; @@ -53,6 +58,7 @@ struct NodePrefs { // persisted to file float adc_multiplier; char owner_info[120]; uint8_t path_hash_mode; // which path mode to use when sending + uint8_t loop_detect; }; class CommonCLICallbacks {