From a8a60015769c712b3af25cd6c671cb0c9c706698 Mon Sep 17 00:00:00 2001 From: entr0p1 <1475255+entr0p1@users.noreply.github.com> Date: Mon, 17 Aug 2026 23:01:52 +1000 Subject: [PATCH 1/8] Duplicate MAX_CLIENTS definition MAX_CLIENTS is defined in both src/helpers/ClientACL.h and examples/simple_repeater/MyMesh.h. Appears it was centralised in the former header file some time ago. Both are included in some places and, depending on which order they're in, either value can win. This change drops the duplicate entry from the repeater firmware and bumps the central limit up to 32 (per the original repeater firmware value). Changes: - Remove MAX_CLIENTS from repeater MyMesh.h - Increase MAX_CLIENTS limit in ClientACL.h to 32 --- examples/simple_repeater/MyMesh.h | 4 ---- src/helpers/ClientACL.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 04bd4fb9..cac6c4a2 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -59,10 +59,6 @@ struct RepeaterStats { uint32_t n_recv_errors; }; -#ifndef MAX_CLIENTS - #define MAX_CLIENTS 32 -#endif - struct NeighbourInfo { mesh::Identity id; uint32_t advert_timestamp; diff --git a/src/helpers/ClientACL.h b/src/helpers/ClientACL.h index b758f706..e0654464 100644 --- a/src/helpers/ClientACL.h +++ b/src/helpers/ClientACL.h @@ -34,7 +34,7 @@ struct ClientInfo { }; #ifndef MAX_CLIENTS - #define MAX_CLIENTS 20 + #define MAX_CLIENTS 32 #endif class ClientACL { From 38f10cd12b0c766db9599973b2fc751da29af908 Mon Sep 17 00:00:00 2001 From: Dominik Tyrala Date: Mon, 17 Aug 2026 15:50:46 +0200 Subject: [PATCH 2/8] Fixes BLE exceeding flash size --- variants/xiao_c3/platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/xiao_c3/platformio.ini b/variants/xiao_c3/platformio.ini index 587c5c27..c9c107c6 100644 --- a/variants/xiao_c3/platformio.ini +++ b/variants/xiao_c3/platformio.ini @@ -73,6 +73,7 @@ lib_deps = [env:Xiao_C3_companion_radio_ble] extends = Xiao_esp32_C3 +board_build.partitions = min_spiffs.csv ; get around 4mb flash limit build_src_filter = ${Xiao_esp32_C3.build_src_filter} +<../examples/companion_radio/*.cpp> + From b76242043399cd5a3a4bfe1e36b505a69165ab25 Mon Sep 17 00:00:00 2001 From: Thomas Osterried Date: Wed, 19 Aug 2026 09:35:25 +0200 Subject: [PATCH 3/8] Skip empty channel slots in searchChannelsByHash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An unconfigured slot has an all-zero secret, so it matches null-key group traffic (a sender with an unset PSK). The zero-key MAC validates against the empty slot and the foreign message is delivered as if it belonged to that channel — every node with a free slot is a null-key sink. Skip empty slots. --- src/helpers/BaseChatMesh.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 972a97e9..616ee39b 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -368,6 +368,12 @@ void BaseChatMesh::handleReturnPathRetry(const ContactInfo& contact, const uint8 int BaseChatMesh::searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel dest[], int max_matches) { int n = 0; for (int i = 0; i < MAX_GROUP_CHANNELS && n < max_matches; i++) { + // Skip empty/unconfigured slots. An empty slot has an all-zero secret and + // therefore matches null-key group traffic (a node transmitting with an + // unset PSK): the zero-key MAC validates against the empty slot and the + // foreign message is delivered as if it belonged to that channel. Any node + // with a free channel slot would otherwise act as a null-key sink. + if (channels[i].name[0] == 0) continue; if (channels[i].channel.hash[0] == hash[0]) { dest[n++] = channels[i].channel; } From 3a440ac41af0e19f2f79aba7b5efd16a80988963 Mon Sep 17 00:00:00 2001 From: hansimgamr <77758818+hansimgamr@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:06:43 -0400 Subject: [PATCH 4/8] Add charging indicator to companion_radio battery icon Show a small lightning-bolt icon to the left of the battery indicator on the ui-new home screen while the device is externally powered, and a plug icon once the battery reads full. The bolt/plug sits beside the battery so the fill bar stays clean and uninterrupted. When a buzzer is present, the mute icon shifts one slot further left so the two never overlap. Charging state is derived from board.isExternalPowered(), so this works on any board that reports external power (e.g. the nRF52 VBUS-detect path). Co-Authored-By: Claude Opus 4.8 --- examples/companion_radio/ui-new/UITask.cpp | 14 ++++++++++++-- examples/companion_radio/ui-new/icons.h | 10 ++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 59b58461..617f5e87 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -146,11 +146,21 @@ class HomeScreen : public UIScreen { int fillWidth = (batteryPercentage * (iconWidth - 4)) / 100; display.fillRect(iconX + 2, iconY + 2, fillWidth, iconHeight - 4); - // show muted icon if buzzer is muted + // while charging, show a bolt (or a plug once full) just left of the battery, + // keeping the fill bar itself clean and uninterrupted + bool charging = board.isExternalPowered(); + if (charging) { + const uint8_t* symbol = (batteryPercentage < 100) ? charging_icon : plug_icon; + display.setColor(UIColor::title_txt); + display.drawXbm(iconX - 9, iconY + 1, symbol, 8, 8); + } + + // show muted icon if buzzer is muted (shifted further left when the charging + // icon already occupies the slot immediately left of the battery) #ifdef PIN_BUZZER if (_task->isBuzzerQuiet()) { display.setColor(UIColor::warning_txt); - display.drawXbm(iconX - 9, iconY + 1, muted_icon, 8, 8); + display.drawXbm(iconX - (charging ? 18 : 9), iconY + 1, muted_icon, 8, 8); } #endif } diff --git a/examples/companion_radio/ui-new/icons.h b/examples/companion_radio/ui-new/icons.h index cbe23790..8d9ab175 100644 --- a/examples/companion_radio/ui-new/icons.h +++ b/examples/companion_radio/ui-new/icons.h @@ -119,4 +119,14 @@ static const uint8_t advert_icon[] = { static const uint8_t muted_icon[] = { 0x20, 0x6a, 0xea, 0xe4, 0xe4, 0xea, 0x6a, 0x20 +}; + +// small lightning bolt, 8x8px, shown next to the battery icon while charging +static const uint8_t charging_icon[] = { + 0x18, 0x30, 0x60, 0xFC, 0x18, 0x30, 0x60, 0xC0 +}; + +// small power plug, 8x8px, shown next to the battery icon once fully charged +static const uint8_t plug_icon[] = { + 0x24, 0x24, 0x7E, 0x7E, 0x7E, 0x3C, 0x18, 0x18 }; \ No newline at end of file From 41c60da5759ccb2c0a04e3b7a354e66e51c2de03 Mon Sep 17 00:00:00 2001 From: hansimgamr <77758818+hansimgamr@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:14:12 -0400 Subject: [PATCH 5/8] Show plug at >=95% instead of exactly 100% Boards without a charge-complete signal only infer "full" from voltage, and a real pack rarely reads the full BATT_MAX_MILLIVOLTS (4.2V), so the plug icon was effectively never shown. Treat "full" as a high band (>= 95%) so the plug appears when the battery is charged rather than requiring an exact 100%. Co-Authored-By: Claude Opus 4.8 --- examples/companion_radio/ui-new/UITask.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 617f5e87..55755ef1 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -150,7 +150,10 @@ class HomeScreen : public UIScreen { // keeping the fill bar itself clean and uninterrupted bool charging = board.isExternalPowered(); if (charging) { - const uint8_t* symbol = (batteryPercentage < 100) ? charging_icon : plug_icon; + // There's no charge-complete signal on most boards, so "full" is a high + // voltage band rather than an exact 100% (a real pack rarely reads 4.2V). + const int BATT_FULL_PCT = 95; + const uint8_t* symbol = (batteryPercentage >= BATT_FULL_PCT) ? plug_icon : charging_icon; display.setColor(UIColor::title_txt); display.drawXbm(iconX - 9, iconY + 1, symbol, 8, 8); } From 0b652b56294d2d6a23c7ca62b14b8215b1ccca4a Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sat, 22 Aug 2026 16:16:32 +1200 Subject: [PATCH 6/8] add support for rak12500 and rak12501 gps on rak3401 --- variants/rak3401/platformio.ini | 1 + variants/rak3401/variant.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/variants/rak3401/platformio.ini b/variants/rak3401/platformio.ini index 48e71922..c285e3ec 100644 --- a/variants/rak3401/platformio.ini +++ b/variants/rak3401/platformio.ini @@ -6,6 +6,7 @@ build_flags = ${nrf52_base.build_flags} ${sensor_base.build_flags} -I variants/rak3401 -D RAK_3401 + -D RAK_BOARD -D NRF52_POWER_MANAGEMENT -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper diff --git a/variants/rak3401/variant.h b/variants/rak3401/variant.h index 98827886..e0c24759 100644 --- a/variants/rak3401/variant.h +++ b/variants/rak3401/variant.h @@ -188,8 +188,8 @@ static const uint8_t AREF = PIN_AREF; // Power is on the controllable 3V3_S rail #define PIN_GPS_PPS (17) // Pulse per second input from the GPS -#define PIN_GPS_RX PIN_SERIAL1_RX -#define PIN_GPS_TX PIN_SERIAL1_TX +#define PIN_GPS_TX PIN_SERIAL1_RX +#define PIN_GPS_RX PIN_SERIAL1_TX #define PIN_GPS_1PPS PIN_GPS_PPS #define GPS_BAUD_RATE 9600 From ab4c33826dbcaf80aad89ac94048acc104e553d5 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Sun, 23 Aug 2026 14:14:45 +1200 Subject: [PATCH 7/8] don't toggle io pins on rak3401 when probing gps --- src/helpers/sensors/EnvironmentSensorManager.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 54305692..6f460775 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -831,11 +831,15 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ } #endif + #ifndef RAK_3401 //set initial waking state pinMode(ioPin,OUTPUT); digitalWrite(ioPin,LOW); delay(500); digitalWrite(ioPin,HIGH); + #endif + + // give gps time to power up delay(500); //Try to init RAK12500 on I2C @@ -871,7 +875,9 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ return true; } + #ifndef RAK_3401 pinMode(ioPin, INPUT); + #endif MESH_DEBUG_PRINTLN("GPS did not init with this IO pin... try the next"); return false; } @@ -880,8 +886,10 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ void EnvironmentSensorManager::start_gps() { gps_active = true; #ifdef RAK_WISBLOCK_GPS + #ifndef RAK_3401 pinMode(gpsResetPin, OUTPUT); digitalWrite(gpsResetPin, HIGH); + #endif return; #endif @@ -896,8 +904,10 @@ void EnvironmentSensorManager::start_gps() { void EnvironmentSensorManager::stop_gps() { gps_active = false; #ifdef RAK_WISBLOCK_GPS + #ifndef RAK_3401 // rak3401 shouldn't turn off WB_IO2 as it powers the PA pinMode(gpsResetPin, OUTPUT); digitalWrite(gpsResetPin, LOW); + #endif return; #endif From 7dc2d54818809a44d04d7d3132ea9a416faa331b Mon Sep 17 00:00:00 2001 From: liamcottle Date: Mon, 24 Aug 2026 13:45:19 +1200 Subject: [PATCH 8/8] add UI_NO_HIBERNATE build flag to disable hibernate screen on wio tracker l1 --- examples/companion_radio/ui-new/UITask.cpp | 6 ++++++ variants/wio-tracker-l1/platformio.ini | 2 ++ 2 files changed, 8 insertions(+) diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 55755ef1..969ac3dd 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -102,7 +102,9 @@ class HomeScreen : public UIScreen { #if UI_SENSORS_PAGE == 1 SENSORS, #endif +#ifndef UI_NO_HIBERNATE SHUTDOWN, +#endif Count // keep as last }; @@ -459,6 +461,7 @@ public: if (sensors_scroll) sensors_scroll_offset = (sensors_scroll_offset+1)%sensors_nb; else sensors_scroll_offset = 0; #endif +#ifndef UI_NO_HIBERNATE } else if (_page == HomePage::SHUTDOWN) { display.setColor(UIColor::corp_blue); display.setTextSize(1); @@ -470,6 +473,7 @@ public: display.drawXbm((display.width() - 32) / 2, 18, power_icon, 32, 32); display.drawTextCentered(display.width() / 2, 64 - 11, "hibernate:" PRESS_LABEL); } +#endif } return 5000; // next render after 5000 ms } @@ -516,10 +520,12 @@ public: return true; } #endif +#ifndef UI_NO_HIBERNATE if (c == KEY_ENTER && _page == HomePage::SHUTDOWN) { _shutdown_init = true; // need to wait for button to be released return true; } +#endif return false; } }; diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index fc958ea2..19139c28 100644 --- a/variants/wio-tracker-l1/platformio.ini +++ b/variants/wio-tracker-l1/platformio.ini @@ -65,6 +65,7 @@ build_flags = ${WioTrackerL1.build_flags} -D MAX_GROUP_CHANNELS=40 -D DISPLAY_CLASS=SH1106Display -D UI_HAS_JOYSTICK=1 + -D UI_NO_HIBERNATE -D OFFLINE_QUEUE_SIZE=256 -D PIN_BUZZER=12 -D QSPIFLASH=1 @@ -95,6 +96,7 @@ build_flags = ${WioTrackerL1.build_flags} -D OFFLINE_QUEUE_SIZE=256 -D DISPLAY_CLASS=SH1106Display -D UI_HAS_JOYSTICK=1 + -D UI_NO_HIBERNATE -D PIN_BUZZER=12 -D QSPIFLASH=1 -D ADVERT_NAME='"@@MAC"'