From 69b9977fc11e13135ec7f5559bbb4f7139e9a3b8 Mon Sep 17 00:00:00 2001 From: Austin Lane Date: Tue, 9 Dec 2025 07:48:30 -0500 Subject: [PATCH 01/10] Fix apply device-install permissions device-install.sh doesn't exist for non-esp32 targets --- .github/workflows/build_one_target.yml | 4 ++-- .github/workflows/main_matrix.yml | 8 ++++---- .github/workflows/merge_queue.yml | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build_one_target.yml b/.github/workflows/build_one_target.yml index 02aad5a9c..9d9e0114b 100644 --- a/.github/workflows/build_one_target.yml +++ b/.github/workflows/build_one_target.yml @@ -139,8 +139,8 @@ jobs: - name: Device scripts permissions run: | - chmod +x ./output/device-install.sh - chmod +x ./output/device-update.sh + chmod +x ./output/device-install.sh || true + chmod +x ./output/device-update.sh || true - name: Zip firmware run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index b4f4c3d11..f48a7ebd0 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -207,8 +207,8 @@ jobs: - name: Device scripts permissions run: | - chmod +x ./output/device-install.sh - chmod +x ./output/device-update.sh + chmod +x ./output/device-install.sh || true + chmod +x ./output/device-update.sh || true - name: Zip firmware run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output @@ -338,8 +338,8 @@ jobs: - name: Device scripts permissions run: | - chmod +x ./output/device-install.sh - chmod +x ./output/device-update.sh + chmod +x ./output/device-install.sh || true + chmod +x ./output/device-update.sh || true - name: Zip firmware run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output diff --git a/.github/workflows/merge_queue.yml b/.github/workflows/merge_queue.yml index b9bb3ceed..a71afad9d 100644 --- a/.github/workflows/merge_queue.yml +++ b/.github/workflows/merge_queue.yml @@ -188,8 +188,8 @@ jobs: - name: Device scripts permissions run: | - chmod +x ./output/device-install.sh - chmod +x ./output/device-update.sh + chmod +x ./output/device-install.sh || true + chmod +x ./output/device-update.sh || true - name: Zip firmware run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output @@ -303,8 +303,8 @@ jobs: - name: Device scripts permissions run: | - chmod +x ./output/device-install.sh - chmod +x ./output/device-update.sh + chmod +x ./output/device-install.sh || true + chmod +x ./output/device-update.sh || true - name: Zip firmware run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output From e691bd97324cd01960b783721218a7db4e1dff44 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 9 Dec 2025 08:02:04 -0600 Subject: [PATCH 02/10] Revert "Cut NRF52 bluetooth power usage by 300% - testers needed! (#8858)" This reverts commit ae8d3fbb3d4fb45a0162bc70b6f8ccb37fc677c0. --- src/platform/nrf52/NRF52Bluetooth.cpp | 34 ++----------------- .../nrf52840/heltec_mesh_node_t114/variant.h | 4 +-- variants/nrf52840/tracker-t1000-e/variant.h | 4 +-- 3 files changed, 6 insertions(+), 36 deletions(-) diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index f1bc43312..4f7fb4776 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -64,16 +64,6 @@ void onConnect(uint16_t conn_handle) connection->getPeerName(central_name, sizeof(central_name)); LOG_INFO("BLE Connected to %s", central_name); - // negotiate connections params as soon as possible - - ble_gap_conn_params_t newParams; - newParams.min_conn_interval = 24; - newParams.max_conn_interval = 40; - newParams.slave_latency = 5; - newParams.conn_sup_timeout = 400; - - sd_ble_gap_conn_param_update(conn_handle, &newParams); - // Notify UI (or any other interested firmware components) meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED); bluetoothStatus->updateStatus(&newStatus); @@ -129,7 +119,7 @@ void startAdv(void) Bluefruit.Advertising.addService(meshBleService); /* Start Advertising * - Enable auto advertising if disconnected - * - Interval: fast mode = 20 ms, slow mode = 417,5 ms + * - Interval: fast mode = 20 ms, slow mode = 152.5 ms * - Timeout for fast mode is 30 seconds * - Start(timeout) with timeout = 0 will advertise forever (until connected) * @@ -137,7 +127,7 @@ void startAdv(void) * https://developer.apple.com/library/content/qa/qa1931/_index.html */ Bluefruit.Advertising.restartOnDisconnect(true); - Bluefruit.Advertising.setInterval(32, 668); // in unit of 0.625 ms + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X } @@ -282,24 +272,6 @@ void NRF52Bluetooth::setup() // Set the connect/disconnect callback handlers Bluefruit.Periph.setConnectCallback(onConnect); Bluefruit.Periph.setDisconnectCallback(onDisconnect); - - // Set slave latency to 5 to conserve power - // Despite name this does not impact data transfer - // https://docs.silabs.com/bluetooth/2.13/bluetooth-general-system-and-performance/optimizing-current-consumption-in-bluetooth-low-energy-devices - - Bluefruit.Periph.setConnSlaveLatency(5); - - // TODO: Adafruit defaul min, max interval seems to be (20,30) [in 1.25 ms units] -> (25.00, 31.25) milliseconds - // so using formula Interval Max * (Slave Latency + 1) ≤ 2 seconds - // max slave latency we can use is 30 (max available in BLE) - // and even double max inteval (see apple doc linked above for formulas) - // See Periph.SetConnInterval method - - // Tweak this later for even more power savings once those changes are confirmed to work well. - // Changing min, max interval may slow BLE transfer a bit - bumping slave latency will most likely not. - - - #ifndef BLE_DFU_SECURE bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); bledfu.begin(); // Install the DFU helper @@ -328,7 +300,7 @@ void NRF52Bluetooth::setup() void NRF52Bluetooth::resumeAdvertising() { Bluefruit.Advertising.restartOnDisconnect(true); - Bluefruit.Advertising.setInterval(32, 668); // in unit of 0.625 ms + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode Bluefruit.Advertising.start(0); } diff --git a/variants/nrf52840/heltec_mesh_node_t114/variant.h b/variants/nrf52840/heltec_mesh_node_t114/variant.h index 03c5aafd2..28404fcce 100644 --- a/variants/nrf52840/heltec_mesh_node_t114/variant.h +++ b/variants/nrf52840/heltec_mesh_node_t114/variant.h @@ -21,8 +21,8 @@ /** Master clock frequency */ #define VARIANT_MCK (64000000ul) -//#define USE_LFXO // Board uses 32khz crystal for LF -#define USE_LFRC // Board uses RC for LF +#define USE_LFXO // Board uses 32khz crystal for LF + /*---------------------------------------------------------------------------- * Headers *----------------------------------------------------------------------------*/ diff --git a/variants/nrf52840/tracker-t1000-e/variant.h b/variants/nrf52840/tracker-t1000-e/variant.h index ff63a4155..5b6719e12 100644 --- a/variants/nrf52840/tracker-t1000-e/variant.h +++ b/variants/nrf52840/tracker-t1000-e/variant.h @@ -22,9 +22,7 @@ /** Master clock frequency */ #define VARIANT_MCK (64000000ul) -//#define USE_LFXO // Board uses 32khz crystal for LF - -#define USE_LFRC +#define USE_LFXO // Board uses 32khz crystal for LF /*---------------------------------------------------------------------------- * Headers From d75680a2dd426fef9a66a2737d1a56acbdfdc05a Mon Sep 17 00:00:00 2001 From: Igor Danilov <59930161+polarikus@users.noreply.github.com> Date: Tue, 9 Dec 2025 21:24:41 +0300 Subject: [PATCH 03/10] Fix #8915 [Bug]: Exception Decoder does not recognize the backtrace (#8917) --- bin/exception_decoder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/exception_decoder.py b/bin/exception_decoder.py index ec94ce20e..ffe6d3f24 100755 --- a/bin/exception_decoder.py +++ b/bin/exception_decoder.py @@ -75,7 +75,7 @@ TOOLS = { } BACKTRACE_REGEX = re.compile( - r"(?:\s+(0x40[0-2](?:\d|[a-f]|[A-F]){5}):0x(?:\d|[a-f]|[A-F]){8})\b" + r"\b(0x4[0-9a-fA-F]{7,8}):0x[0-9a-fA-F]{8}\b" ) EXCEPTION_REGEX = re.compile("^Exception \\((?P[0-9]*)\\):$") COUNTER_REGEX = re.compile( @@ -89,7 +89,7 @@ POINTER_REGEX = re.compile( STACK_BEGIN = ">>>stack>>>" STACK_END = "<<[0-9a-f]+):\W+(?P[0-9a-f]+) (?P[0-9a-f]+) (?P[0-9a-f]+) (?P[0-9a-f]+)(\W.*)?$" + r"^(?P[0-9a-f]+):\W+(?P[0-9a-f]+) (?P[0-9a-f]+) (?P[0-9a-f]+) (?P[0-9a-f]+)(\W.*)?$" ) StackLine = namedtuple("StackLine", ["offset", "content"]) @@ -223,7 +223,7 @@ class AddressResolver(object): if match is None: if last is not None and line.startswith("(inlined by)"): line = line[12:].strip() - self._address_map[last] += "\n \-> inlined by: " + line + self._address_map[last] += "\n \\-> inlined by: " + line continue if match.group("result") == "?? ??:0": From aa605fc4a2211974e8e9b5c22bf496f69a2b16ee Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 9 Dec 2025 15:27:13 -0500 Subject: [PATCH 04/10] Actions: Fix release manifest formating (#8918) --- .github/workflows/main_matrix.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index f48a7ebd0..acd63f28f 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -280,9 +280,9 @@ jobs: - name: Generate Release manifest run: | - jq -n --arg ver "${{ needs.version.outputs.long }}" --arg targets "${{ toJson(needs.setup.outputs.all) }}" '{ + jq -n --arg ver "${{ needs.version.outputs.long }}" --argjson targets ${{ toJson(needs.setup.outputs.all) }} '{ "version": $ver, - "targets": ($targets | fromjson) + "targets": $targets }' > firmware-${{ needs.version.outputs.long }}.json - name: Save Release manifest artifact From c55bea846094d491809c1f2c1277d351e08e5771 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 9 Dec 2025 16:11:07 -0500 Subject: [PATCH 05/10] ARCtastic (#8904) -- Do It Live! Actions Runner Controller Co-authored-by: Jonathan Bennett --- .github/actionlint.yaml | 1 + .github/workflows/build_firmware.yml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index f7bf95f83..f79e4fdb5 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -2,4 +2,5 @@ self-hosted-runner: # Labels of self-hosted runner in array of strings. labels: + - arctastic - test-runner diff --git a/.github/workflows/build_firmware.yml b/.github/workflows/build_firmware.yml index c3b70d4c9..28e4ee994 100644 --- a/.github/workflows/build_firmware.yml +++ b/.github/workflows/build_firmware.yml @@ -18,7 +18,8 @@ permissions: read-all jobs: pio-build: name: build-${{ inputs.platform }} - runs-on: ubuntu-24.04 + # Use 'arctastic' self-hosted runner pool when building in the main repo + runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }} outputs: artifact-id: ${{ steps.upload.outputs.artifact-id }} steps: From aa72e397f29462001ea29c90b1c08fa3c81fa593 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 9 Dec 2025 17:40:37 -0500 Subject: [PATCH 06/10] PIO: Fix closedcube lib reference (#8920) Fixes ClosedCube reinstalling on every build --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 218b75443..25997e11d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -207,7 +207,7 @@ lib_deps = # renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6 # renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001 - ClosedCube OPT3001@1.1.2 + closedcube/ClosedCube OPT3001@1.1.2 # renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2 boschsensortec/bsec2@1.10.2610 # renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library From 5910cc2e269cac13275d198434d34be210eb3d9d Mon Sep 17 00:00:00 2001 From: Alex Samorukov Date: Wed, 10 Dec 2025 13:23:23 +0100 Subject: [PATCH 07/10] Use PSRAM to reduce heap usage percentage on ESP32 with PSRAM (#8891) * Use PSRAM for malloc > 256bytes to get more heap memory * Use dynamic allocator on boards with PSRAM to free more heap * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Move heap_caps_malloc_extmem_enable() to the top of the init * Update src/main.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Meadors Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/main.cpp | 5 +++++ src/mesh/Router.cpp | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f8d89e1ba..eb6dea327 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -439,6 +439,11 @@ void setup() LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n"); +#if defined(ARCH_ESP32) && defined(BOARD_HAS_PSRAM) + // use PSRAM for malloc calls > 256 bytes + heap_caps_malloc_extmem_enable(256); +#endif + #if defined(DEBUG_MUTE) && defined(DEBUG_PORT) DEBUG_PORT.printf("\r\n\r\n//\\ E S H T /\\ S T / C\r\n"); DEBUG_PORT.printf("Version %s for %s from %s\r\n", optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO)); diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 05f47d7f4..54a34fd35 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -37,8 +37,8 @@ static MemoryDynamic dynamicPool; Allocator &packetPool = dynamicPool; -#elif defined(ARCH_STM32WL) -// On STM32 there isn't enough heap left over for the rest of the firmware if we allocate this statically. +#elif defined(ARCH_STM32WL) || defined(BOARD_HAS_PSRAM) +// On STM32 and boards with PSRAM, there isn't enough heap left over for the rest of the firmware if we allocate this statically. // For now, make it dynamic again. #define MAX_PACKETS \ (MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \ From 2032ff1c32cf222c3d2a41d9b041f13c7fe71c6d Mon Sep 17 00:00:00 2001 From: Jason P Date: Wed, 10 Dec 2025 11:09:37 -0600 Subject: [PATCH 08/10] Create new screen colors for BaseUI (#8921) * Create new colors for BaseUI * Update Ice color --- src/graphics/draw/MenuHandler.cpp | 37 ++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index f782dabb6..2a7f479b4 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -1041,12 +1041,13 @@ void menuHandler::switchToMUIMenu() void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) { - static const char *optionsArray[] = {"Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Teal", - "Pink", "White"}; + static const char *optionsArray[] = { + "Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Blue", "Teal", "Cyan", "Ice", "Pink", + "White", "Gray"}; BannerOverlayOptions bannerOptions; bannerOptions.message = "Select Screen Color"; bannerOptions.optionsArrayPtr = optionsArray; - bannerOptions.optionsCount = 10; + bannerOptions.optionsCount = 14; bannerOptions.bannerCallback = [display](int selected) -> void { #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \ HAS_TFT || defined(HACKADAY_COMMUNICATOR) @@ -1082,20 +1083,40 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) TFT_MESH_g = 153; TFT_MESH_b = 255; } else if (selected == 7) { - LOG_INFO("Setting color to Teal"); - TFT_MESH_r = 64; - TFT_MESH_g = 224; - TFT_MESH_b = 208; + LOG_INFO("Setting color to Blue"); + TFT_MESH_r = 0; + TFT_MESH_g = 0; + TFT_MESH_b = 255; } else if (selected == 8) { + LOG_INFO("Setting color to Teal"); + TFT_MESH_r = 16; + TFT_MESH_g = 102; + TFT_MESH_b = 102; + } else if (selected == 9) { + LOG_INFO("Setting color to Cyan"); + TFT_MESH_r = 0; + TFT_MESH_g = 255; + TFT_MESH_b = 255; + } else if (selected == 10) { + LOG_INFO("Setting color to Ice"); + TFT_MESH_r = 173; + TFT_MESH_g = 216; + TFT_MESH_b = 230; + } else if (selected == 11) { LOG_INFO("Setting color to Pink"); TFT_MESH_r = 255; TFT_MESH_g = 105; TFT_MESH_b = 180; - } else if (selected == 9) { + } else if (selected == 12) { LOG_INFO("Setting color to White"); TFT_MESH_r = 255; TFT_MESH_g = 255; TFT_MESH_b = 255; + } else if (selected == 13) { + LOG_INFO("Setting color to Gray"); + TFT_MESH_r = 128; + TFT_MESH_g = 128; + TFT_MESH_b = 128; } else { menuQueue = system_base_menu; screen->runNow(); From ff0a4ea3207cba2eeb8b9b61ec1b7dc1510f3b21 Mon Sep 17 00:00:00 2001 From: Jason P Date: Wed, 10 Dec 2025 16:30:26 -0600 Subject: [PATCH 09/10] Update System Frame for improved rendering on devices (#8923) --- src/graphics/draw/DebugRenderer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/graphics/draw/DebugRenderer.cpp b/src/graphics/draw/DebugRenderer.cpp index 1b3a148d6..ceb3b83f5 100644 --- a/src/graphics/draw/DebugRenderer.cpp +++ b/src/graphics/draw/DebugRenderer.cpp @@ -532,8 +532,10 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x const int labelX = x; int barsOffset = (isHighResolution) ? 24 : 0; #ifdef USE_EINK +#ifndef T_DECK_PRO barsOffset -= 12; #endif +#endif #if defined(M5STACK_UNITC6L) const int barX = x + 45 + barsOffset; #else @@ -574,7 +576,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x #endif // Value string display->setTextAlignment(TEXT_ALIGN_RIGHT); - display->drawString(SCREEN_WIDTH - 2, getTextPositions(display)[line], combinedStr); + display->drawString(SCREEN_WIDTH, getTextPositions(display)[line], combinedStr); }; // === Memory values === From fff2bbf4a0158aa499fbc6904e0d0e60d7dfbb0f Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:05:26 -0800 Subject: [PATCH 10/10] Use truncated position for smart position (#8906) --- src/modules/PositionModule.cpp | 54 +++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 8b6a9f19c..776c3b5a6 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -45,8 +45,12 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes { auto p = *pptr; - // If inbound message is a replay (or spoof!) of our own messages, we shouldn't process - // (why use second-hand sources for our own data?) + const auto transport = mp.transport_mechanism; + if (isFromUs(&mp) && !IS_ONE_OF(transport, meshtastic_MeshPacket_TransportMechanism_TRANSPORT_INTERNAL, + meshtastic_MeshPacket_TransportMechanism_TRANSPORT_API)) { + LOG_WARN("Ignoring packet supposedly from us over external transport"); + return true; + } // FIXME this can in fact happen with packets sent from EUD (src=RX_SRC_USER) // to set fixed location, EUD-GPS location or just the time (see also issue #900) @@ -472,19 +476,53 @@ void PositionModule::sendLostAndFoundText() delete[] message; } +// Helper: return imprecise (truncated + centered) lat/lon as int32 using current precision +static inline void computeImpreciseLatLon(int32_t inLat, int32_t inLon, uint8_t precisionBits, int32_t &outLat, int32_t &outLon) +{ + if (precisionBits > 0 && precisionBits < 32) { + // Build mask for top 'precisionBits' bits of a 32-bit unsigned field + const uint32_t mask = (precisionBits == 32) ? UINT32_MAX : (UINT32_MAX << (32 - precisionBits)); + // Note: latitude_i/longitude_i are stored as signed 32-bit in meshtastic code but + // the bitmask logic used previously operated as unsigned—preserve that behavior by + // casting to uint32_t for masking, then back to int32_t. + uint32_t lat_u = static_cast(inLat) & mask; + uint32_t lon_u = static_cast(inLon) & mask; + + // Add the "center of cell" offset used elsewhere: + // The code previously added (1 << (31 - precision)) to produce the middle of the possible location. + uint32_t center_offset = (1u << (31 - precisionBits)); + lat_u += center_offset; + lon_u += center_offset; + + outLat = static_cast(lat_u); + outLon = static_cast(lon_u); + } else { + // full precision: return input unchanged + outLat = inLat; + outLon = inLon; + } +} + struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition) { - // The minimum distance to travel before we are able to send a new position packet. const uint32_t distanceTravelThreshold = Default::getConfiguredOrDefault(config.position.broadcast_smart_minimum_distance, 100); - // Determine the distance in meters between two points on the globe - float distanceTraveledSinceLastSend = GeoCoord::latLongToMeter( - lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7, currentPosition.latitude_i * 1e-7, currentPosition.longitude_i * 1e-7); + int32_t lastLatImprecise, lastLonImprecise; + int32_t currentLatImprecise, currentLonImprecise; - return SmartPosition{.distanceTraveled = abs(distanceTraveledSinceLastSend), + computeImpreciseLatLon(lastGpsLatitude, lastGpsLongitude, precision, lastLatImprecise, lastLonImprecise); + computeImpreciseLatLon(currentPosition.latitude_i, currentPosition.longitude_i, precision, currentLatImprecise, + currentLonImprecise); + + float distMeters = GeoCoord::latLongToMeter(lastLatImprecise * 1e-7, lastLonImprecise * 1e-7, currentLatImprecise * 1e-7, + currentLonImprecise * 1e-7); + + float distanceTraveled = fabsf(distMeters); + + return SmartPosition{.distanceTraveled = distanceTraveled, .distanceThreshold = distanceTravelThreshold, - .hasTraveledOverThreshold = abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold}; + .hasTraveledOverThreshold = distanceTraveled >= distanceTravelThreshold}; } void PositionModule::handleNewPosition()