Skip to content

refactor: Replace Nordic, use Kable backend for Desktop and Android with BLE support#4818

Merged
jamesarich merged 81 commits intomainfrom
feat/kable-ble
Mar 16, 2026
Merged

refactor: Replace Nordic, use Kable backend for Desktop and Android with BLE support#4818
jamesarich merged 81 commits intomainfrom
feat/kable-ble

Conversation

@jamesarich
Copy link
Copy Markdown
Collaborator

@jamesarich jamesarich commented Mar 16, 2026

This pull request removes all dependencies on Nordic Semiconductor's BLE libraries and interfaces, replacing them with the multiplatform Kable BLE library. The codebase and documentation are updated to reflect this migration, and all related configuration, code, and test references to Nordic BLE are removed. Additionally, new dependency injection and lint configuration changes are introduced to support the updated BLE stack.

BLE Stack Migration and Dependency Cleanup:

  • Migrated the BLE stack from Nordic Semiconductor's libraries to the multiplatform Kable BLE library, updating the architecture description in README.md to reflect this change.
  • Removed all Nordic BLE library dependencies from app/build.gradle.kts, including both implementation and test dependencies. [1] [2]
  • Cleaned up all code references to Nordic BLE in the application, including imports and injected dependencies in MainActivity.kt and MeshUtilApplication.kt. [1] [2] [3] [4] [5]
  • Updated BLE interface usage in AndroidRadioInterfaceService.kt to use the new BleRadioInterface instead of the removed NordicBleInterface.

Dependency Injection and Module Updates:

  • Removed the inclusion of the now-unnecessary CoreDiModule from the Koin module configuration and added a new CoroutineDispatchers provider for dependency injection. [1] [2] [3]

Lint and Baseline Updates:

  • Updated the detekt-baseline.xml to remove issues related to the old Nordic BLE interface and add new lint issues relevant to the current codebase.

…ort-term. Add Kable backend only for jvmMain in core:ble first (desktop BLE enablement)'
This commit removes the documentation and metadata associated with the `android_kable_migration_20260314` track, as the migration from Nordic to Kable on Android has been completed.

Specific changes include:
- Deleted track index, specification (`spec.md`), and implementation plan (`plan.md`).
- Removed `metadata.json` for the migration track.

Signed-off-by: James Rich <[email protected]>
…tion from Nordic to Kable for the BLE transport
This commit enhances the BLE connection stability by implementing more robust retry mechanisms, particularly for Android, and refining the handling of bonded vs. scanned devices.

Specific changes include:
- **Connection Logic**: Improved `KableBleConnection` to dynamically toggle `autoConnect` on Android. It now retries connections with `autoConnect = true` if an initial direct connection fails, resolving common GATT 133 errors.
- **Retry Mechanisms**: Added explicit delays and logging to `BleRadioInterface` connection and scan attempts to allow the BLE stack to settle between operations.
- **Platform Configuration**: Updated `platformConfig` to support dynamic `autoConnect` providers and suppressed magic number/lint warnings for MTU and delay constants.
- **Dependency Clean up**: Removed unused `BluetoothRepository` from `BleRadioInterfaceSpec` and cleaned up unnecessary Koin module imports.
- **Code Quality**: Standardized formatting, added `@Suppress` annotations for handled exceptions and magic numbers, and improved logging across `DirectBleDevice`, `KableBleConnection`, and `AndroidBluetoothRepository`.

Signed-off-by: James Rich <[email protected]>
…bility

This commit enhances the reliability of Bluetooth Low Energy (BLE) connections by implementing a reconnection loop in `BleRadioInterface` and adding failure suppression for characteristic observations in `KableBleConnection`.

Specific changes include:
- **`BleRadioInterface`**:
    - Wrapped the connection logic in a `while(isActive)` loop to automatically attempt reconnection when a link is dropped.
    - Added a suspension point that waits for a `Disconnected` state before triggering a retry.
    - Introduced a 5-second delay after connection failures to prevent rapid retry loops.
    - Refactored `connectionState` monitoring to ensure `isFullyConnected` is reset correctly upon disconnection.
- **`KableBleConnection`**:
    - Configured an `observationExceptionHandler` for Kable `Peripheral` instances to suppress and log observation failures, preventing them from crashing the connection flow.
    - Ensured existing peripherals are properly disconnected and closed before creating a new one.

Signed-off-by: James Rich <[email protected]>
This commit applies code formatting, adds missing copyright headers, and suppresses lint warnings across several modules to improve code quality and maintainability.

Specific changes include:
- **`core:ble`**: Added a comment to `DirectBleDevice.bond()` to clarify its implementation and applied formatting to `KableBleConnection.kt`.
- **`feature:firmware`**:
    - Applied formatting and suppressed `MagicNumber` and `TooGenericExceptionCaught` warnings in `KableNordicDfuHandler.kt`.
    - Added missing copyright headers to `KableNordicDfuHandlerTest.kt`.
    - Cleaned up imports and formatting in `BleOtaTransport.kt` and `BleOtaTransportTest.kt`.
- **`feature:settings`**: Removed a stale comment in `PositionConfigItemList.kt`.
- **`app`**: Refactored `BleRadioInterface` and its spec for better readability and updated its unit tests with consistent formatting.
- **`core:ui`**: Cleaned up unused imports in `TimeTickWithLifecycle.kt`.
- **`core:ble` (Android)**: Suppressed generic exception warnings and improved error handling in `AndroidBluetoothRepository.kt`.

Signed-off-by: James Rich <[email protected]>
This commit corrects a formatting issue in the import section of `BleRadioInterface.kt`.

Specific changes:
- Fixed an issue where `kotlinx.coroutines.flow.onEach` and `kotlinx.coroutines.isActive` imports were merged onto a single line.

Signed-off-by: James Rich <[email protected]>
@github-actions github-actions bot added the enhancement New feature or request label Mar 16, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 2.91971% with 399 lines in your changes missing coverage. Please review.
✅ Project coverage is 13.10%. Comparing base (0e5f945) to head (a197ecd).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...shtastic/app/repository/radio/BleRadioInterface.kt 0.00% 153 Missing ⚠️
...tlin/org/meshtastic/core/ble/KableBleConnection.kt 0.00% 74 Missing ⚠️
.../meshtastic/core/ble/AndroidBluetoothRepository.kt 0.00% 65 Missing ⚠️
...meshtastic/core/ble/KableMeshtasticRadioProfile.kt 0.00% 43 Missing ⚠️
...meshtastic/feature/firmware/ota/BleOtaTransport.kt 20.00% 16 Missing ⚠️
...n/kotlin/org/meshtastic/core/ble/KableBleDevice.kt 0.00% 12 Missing ⚠️
.../kotlin/org/meshtastic/core/ble/KableBleScanner.kt 0.00% 12 Missing ⚠️
.../kotlin/org/meshtastic/core/ble/DirectBleDevice.kt 0.00% 10 Missing ⚠️
...rg/meshtastic/core/ble/KableBluetoothRepository.kt 0.00% 5 Missing ⚠️
...stic/app/repository/radio/BleRadioInterfaceSpec.kt 0.00% 2 Missing ⚠️
... and 5 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4818      +/-   ##
==========================================
+ Coverage   12.18%   13.10%   +0.92%     
==========================================
  Files         531      535       +4     
  Lines       17578    17653      +75     
  Branches     2630     2646      +16     
==========================================
+ Hits         2142     2314     +172     
+ Misses      15131    15023     -108     
- Partials      305      316      +11     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Signed-off-by: James Rich <[email protected]>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes a broad refactor to migrate Meshtastic’s BLE stack from Nordic’s libraries to the Kable multiplatform BLE backend, wiring the new abstractions through Android and Desktop, updating DI/build configuration, and adjusting tests/docs accordingly.

Changes:

  • Introduces Kable-based BLE implementations in core:ble (scanner/connection/profile) and updates Desktop to support BLE connections.
  • Migrates Android BLE transport to use the shared BleScanner/BleConnection abstractions (and removes Nordic usages/deps in touched modules).
  • Updates/relocates tests and documentation to reflect the new BLE architecture.

Reviewed changes

Copilot reviewed 81 out of 82 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
gradle/libs.versions.toml Adds Kable version + library alias to the version catalog.
feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/radio/component/PositionConfigItemList.kt Removes Nordic permission wrapper usage in UI.
feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/radio/component/DeviceConfigItemList.kt Replaces Nordic receiver helper with explicit receiver lifecycle via DisposableEffect.
feature/settings/build.gradle.kts Removes Nordic Android Common Library deps from this module.
feature/firmware/src/androidUnitTest/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportTest.kt Deletes Nordic-mock-based unit test (migration cleanup).
feature/firmware/src/androidUnitTest/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportServiceDiscoveryTest.kt Deletes Nordic-mock-based unit tests (migration cleanup).
feature/firmware/src/androidUnitTest/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportNordicMockTest.kt Deletes Nordic-mock-based unit test (migration cleanup).
feature/firmware/src/androidUnitTest/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportMtuTest.kt Deletes Nordic-mock-based unit test (migration cleanup).
feature/firmware/src/androidUnitTest/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportErrorTest.kt Deletes Nordic-mock-based unit test (migration cleanup).
feature/firmware/src/androidMain/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransport.kt Ports BLE OTA transport to Kable-based BLE abstractions.
feature/firmware/src/androidMain/kotlin/org/meshtastic/feature/firmware/NordicDfuHandler.kt Removes Nordic DFU handler implementation.
feature/firmware/src/androidMain/kotlin/org/meshtastic/feature/firmware/KableNordicDfuHandler.kt Adds a new (Kable-intended) DFU handler.
feature/firmware/src/androidMain/kotlin/org/meshtastic/feature/firmware/FirmwareDfuService.kt Removes Nordic DFU foreground service implementation.
feature/firmware/src/androidMain/kotlin/org/meshtastic/feature/firmware/AndroidFirmwareUpdateManager.kt Switches injected DFU handler type to the new handler.
feature/firmware/src/androidMain/AndroidManifest.xml Removes DFU service declaration.
feature/firmware/src/androidHostTest/kotlin/org/meshtastic/feature/firmware/ota/UnifiedOtaProtocolTest.kt Adds host tests for OTA protocol parsing/formatting.
feature/firmware/src/androidHostTest/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandlerTest.kt Updates host test to use CommonUri + platform URI conversion.
feature/firmware/src/androidHostTest/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransportTest.kt Adds host tests for Kable-based BLE OTA transport behavior.
feature/firmware/src/androidHostTest/kotlin/org/meshtastic/feature/firmware/KableNordicDfuHandlerTest.kt Adds host test coverage around new DFU handler wiring.
feature/firmware/src/androidHostTest/kotlin/org/meshtastic/feature/firmware/FirmwareRetrieverTest.kt Updates tests to match refactored firmware extraction APIs.
feature/firmware/build.gradle.kts Adds Kable core; rewires tests to androidHostTest deps.
feature/firmware/README.md Updates docs to reference Kable-based BLE OTA transport.
feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/components/CurrentlyConnectedInfo.kt Adjusts RSSI polling error logging/loop behavior.
docs/kmp-status.md Updates KMP status docs to indicate Kable BLE.
docs/decisions/ble-strategy.md Updates the BLE strategy decision to reflect full Kable migration.
desktop/src/main/kotlin/org/meshtastic/desktop/radio/DesktopRadioInterfaceService.kt Adds BLE support to Desktop radio interface service.
desktop/src/main/kotlin/org/meshtastic/desktop/radio/DesktopBleInterface.kt Renames/migrates Desktop BLE transport to Kable-based abstractions.
desktop/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt Adds core BLE module + injects BLE deps into Desktop radio service.
core/ui/src/androidMain/kotlin/org/meshtastic/core/ui/component/TimeTickWithLifecycle.kt Removes Nordic receiver helper and registers receiver directly.
core/ui/build.gradle.kts Removes Nordic common core dependency.
core/network/src/androidUnitTest/kotlin/org/meshtastic/core/network/radio/NordicBleInterfaceTest.kt Deletes Nordic BLE interface tests.
core/network/src/androidUnitTest/kotlin/org/meshtastic/core/network/radio/NordicBleInterfaceRetryTest.kt Deletes Nordic BLE retry tests.
core/network/src/androidUnitTest/kotlin/org/meshtastic/core/network/radio/BleRadioInterfaceTest.kt Adds tests for new BLE radio interface path.
core/ble/src/test/kotlin/org/meshtastic/core/ble/BluetoothRepositoryTest.kt Removes Nordic-mock-based BLE repository tests.
core/ble/src/test/kotlin/org/meshtastic/core/ble/BleScannerTest.kt Removes Nordic-mock-based scanner tests.
core/ble/src/jvmMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt Adds JVM/desktop Kable peripheral setup actuals.
core/ble/src/jvmMain/kotlin/org/meshtastic/core/ble/KableBluetoothRepository.kt Adds Desktop BluetoothRepository implementation.
core/ble/src/commonTest/kotlin/org/meshtastic/core/ble/MeshtasticRadioProfileTest.kt Adds common tests for profile abstraction via a fake.
core/ble/src/commonTest/kotlin/org/meshtastic/core/ble/KableStateMappingTest.kt Adds tests for mapping Kable state to app connection state.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/MeshtasticRadioProfile.kt Moves/defines MeshtasticRadioProfile in core:ble namespace.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableStateMapping.kt Adds Kable StateBleConnectionState mapping helper.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt Adds expect declarations for platform-specific peripheral setup.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableMeshtasticRadioProfile.kt Implements Meshtastic BLE GATT profile using Kable.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleScanner.kt Implements BleScanner via Kable Scanner.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleDevice.kt Implements BleDevice wrapper over Kable Advertisement.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleConnectionFactory.kt Replaces Android/Nordic connection factory with Kable factory.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/KableBleConnection.kt Implements BleConnection on top of Kable Peripheral.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/DirectBleDevice.kt Adds address-only BleDevice implementation for bonded-list flows.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/BleServiceExtensions.kt Adds helper to convert BleService into MeshtasticRadioProfile.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/BleScanner.kt Extends scan API to accept an address filter.
core/ble/src/commonMain/kotlin/org/meshtastic/core/ble/ActiveBleConnection.kt Adds global active-peripheral tracker.
core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/di/CoreBleAndroidModule.kt Removes Nordic environment/central manager provisioning.
core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/KablePlatformSetup.kt Adds Android Kable peripheral setup (autoConnect, MTU request).
core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/AndroidBluetoothRepository.kt Reworks Android BluetoothRepository to avoid Nordic environment.
core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/AndroidBleScanner.kt Removes Nordic-based Android scanner implementation.
core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/AndroidBleDevice.kt Removes Nordic-based Android device wrapper.
core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/AndroidBleConnection.kt Removes Nordic-based Android connection implementation.
core/ble/build.gradle.kts Removes Nordic deps, adds Kable core, rewires test source set.
core/ble/README.md Updates module docs to describe Kable-based architecture.
conductor/tracks.md Minor doc formatting update.
conductor/tech-stack.md Adds Kable to the tech stack docs.
conductor/product.md Updates product architecture goals to include core:ble.
conductor/archive/desktop_ble_kable_20260314/spec.md Adds archived spec documentation for the migration track.
conductor/archive/desktop_ble_kable_20260314/plan.md Adds archived plan documentation for the migration track.
conductor/archive/desktop_ble_kable_20260314/metadata.json Adds archived metadata for the migration track.
conductor/archive/desktop_ble_kable_20260314/index.md Adds archived index for the migration track.
conductor/archive/android_kable_migration_20260314/spec.md Adds archived Android migration spec.
conductor/archive/android_kable_migration_20260314/plan.md Adds archived Android migration plan.
conductor/archive/android_kable_migration_20260314/metadata.json Adds archived Android migration metadata.
conductor/archive/android_kable_migration_20260314/index.md Adds archived Android migration index.
app/src/main/kotlin/org/meshtastic/app/repository/radio/MeshtasticRadioServiceImpl.kt Removes Nordic-specific radio service implementation.
app/src/main/kotlin/org/meshtastic/app/repository/radio/InterfaceFactory.kt Switches Bluetooth spec wiring to new BLE spec type.
app/src/main/kotlin/org/meshtastic/app/repository/radio/BleRadioInterfaceSpec.kt Replaces Nordic spec with Kable-based BLE spec.
app/src/main/kotlin/org/meshtastic/app/repository/radio/BleRadioInterfaceFactory.kt Renames factory + returns new BLE transport implementation.
app/src/main/kotlin/org/meshtastic/app/repository/radio/BleRadioInterface.kt Adds new Kable-backed BLE radio transport implementation.
app/src/main/kotlin/org/meshtastic/app/repository/radio/AndroidRadioInterfaceService.kt Updates type checks from Nordic interface to new BLE interface.
app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt Removes old DI module include; adds CoroutineDispatchers provider.
app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt Removes Nordic AndroidEnvironment teardown call.
app/src/main/kotlin/org/meshtastic/app/MainActivity.kt Removes Nordic environment CompositionLocal provider.
app/detekt-baseline.xml Updates baseline (but currently contains merge conflict marker).
app/build.gradle.kts Removes Nordic deps + Nordic mock test deps.
README.md Updates top-level docs to describe Kable BLE stack.
Comments suppressed due to low confidence (1)

feature/firmware/src/androidMain/kotlin/org/meshtastic/feature/firmware/ota/BleOtaTransport.kt:82

  • scanForOtaDevice() relies on BleScanner.scan(timeout=...) to terminate, but the new Kable scanner implementation returns a Flow that runs until cancelled. As written, .firstOrNull { ... } can hang indefinitely when no device is found. Wrap the scan collection in withTimeoutOrNull(SCAN_TIMEOUT) (or otherwise enforce the timeout) so connect() can fail/retry predictably.

You can also share your feedback on Copilot code review. Take the survey.

@jamesarich jamesarich added this pull request to the merge queue Mar 16, 2026
Merged via the queue into main with commit 0b2e89c Mar 16, 2026
5 checks passed
@jamesarich jamesarich deleted the feat/kable-ble branch March 16, 2026 23:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants