Conversation
This commit updates the project documentation and technical stack to reflect the implementation progress of the Desktop target and refined Kotlin Multiplatform (KMP) development guidelines. Key updates include the roadmap for Desktop serial/USB transport, the transition to Kable for BLE, and expanded testing/interop strategies.
Specific changes include:
- **Project Tracking:** Introduced a new track for "Desktop Serial/USB Transport via jSerialComm," including specification, implementation plan, and metadata.
- **Roadmap Updates:**
- Marked Desktop BLE (via Kable JVM) and Native Notifications/System Tray as completed.
- Added MenuBar integration to the completed Tier 2 tasks.
- Updated transport status table to reflect current KMP progress across Android, Desktop, and iOS.
- Added detailed long-term plans for platform-native UI interop (Maps, Camera, Web) and logic/UI splitting for iOS.
- **Technical Stack & Guidelines:**
- Documented the KMP testing strategy, recommending `Mokkery`/`Mockative` for mocking and `Turbine` for Flow assertions.
- Formally updated `AGENTS.md` and `GEMINI.md` to specify Kable as the primary BLE stack, replacing Nordic-specific libraries.
- Added design principles regarding zero platform leaks in `commonMain` and preferred DI patterns over `expect`/`actual`.
- **Status Reporting:** Updated `kmp-status.md` to include native notifications and system tray icon support for the Desktop target.
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
…a SerialTransport' as complete
…on Management' as complete
…lComm' as complete
…port via jSerialComm'
… modules This commit adds the standard GPLv3 license headers to Kotlin files in the `feature:connections` module and applies minor code formatting to `DesktopRadioInterfaceService.kt`. Specific changes: - Added 2026 copyright and license headers to `JvmUsbDeviceData.kt` and `UsbScanner.kt`. - Applied code formatting to the `supportedDeviceTypes` list in `DesktopRadioInterfaceService.kt`. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds Desktop (JVM) Serial/USB transport support using jSerialComm, integrating it into device discovery/connection flows and documenting the updated architecture.
Changes:
- Added a JVM
SerialTransportimplementation (jSerialComm-backed) plus a basic JVM test suite. - Integrated USB/serial device scanning into the connections feature and enabled serial connect/send paths in the desktop radio service.
- Updated version catalog/build config and refreshed KMP/transport documentation/roadmap.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| gradle/libs.versions.toml | Adds jSerialComm to the version catalog for JVM serial transport. |
| feature/connections/src/jvmMain/kotlin/org/meshtastic/feature/connections/model/JvmUsbDeviceData.kt | Introduces JVM USB device model wrapping a serial port name. |
| feature/connections/src/jvmMain/kotlin/org/meshtastic/feature/connections/domain/usecase/JvmUsbScanner.kt | Implements desktop polling scanner for available serial ports. |
| feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/domain/usecase/UsbScanner.kt | Adds a common interface to abstract USB/serial scanning across platforms. |
| feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/domain/usecase/CommonGetDiscoveredDevicesUseCase.kt | Plumbs USB discovery flow into the discovered-devices use case. |
| docs/roadmap.md | Updates transport matrix and desktop feature gap tracking. |
| docs/kmp-status.md | Notes desktop notifications/system tray support in the status page. |
| desktop/src/main/kotlin/org/meshtastic/desktop/radio/DesktopRadioInterfaceService.kt | Adds USB device type support and serial connect/send/cleanup handling. |
| core/network/src/jvmTest/kotlin/org/meshtastic/core/network/SerialTransportTest.kt | Adds JVM tests to validate jSerialComm availability and basic failure behavior. |
| core/network/src/jvmMain/kotlin/org/meshtastic/core/network/SerialTransport.kt | Implements the jSerialComm-backed serial transport with read loop + framing integration. |
| core/network/build.gradle.kts | Adds jSerialComm for jvmMain and mockk for jvmTest. |
| conductor/tech-stack.md | Documents jSerialComm and expands KMP testing guidance. |
| conductor/archive/desktop_serial_transport_20260317/spec.md | Archives spec for the desktop serial transport feature. |
| conductor/archive/desktop_serial_transport_20260317/plan.md | Archives implementation plan/checkpoints for the feature. |
| conductor/archive/desktop_serial_transport_20260317/metadata.json | Adds archived track metadata for the feature work. |
| conductor/archive/desktop_serial_transport_20260317/index.md | Adds index linking the archived spec/plan/metadata. |
| GEMINI.md | Updates BLE documentation to reflect Kable usage. |
| AGENTS.md | Updates BLE documentation to reflect Kable usage. |
You can also share your feedback on Copilot code review. Take the survey.
| val usbFlow = usbScanner?.scanUsbDevices() ?: kotlinx.coroutines.flow.flowOf(emptyList()) | ||
|
|
||
| return combine(nodeDb, recentAddressesDataSource.recentAddresses) { db, recentList -> | ||
| return combine(nodeDb, recentAddressesDataSource.recentAddresses, usbFlow) { db, recentList, usbList -> |
| listOf(DeviceListEntry.Mock(demoModeLabel)) | ||
| } else { | ||
| emptyList() | ||
| usbList |
| } | ||
| } | ||
|
|
||
| private fun startSerialConnection(portName: String) { |
| if (serial.startConnection()) { | ||
| onConnect() | ||
| } else { |
| return try { | ||
| val port = SerialPort.getCommPort(portName) ?: return false | ||
| port.setComPortParameters(baudRate, DATA_BITS, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY) | ||
| port.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 0, 0) |
| close() | ||
| } | ||
| } |
| override fun close() { | ||
| readJob?.cancel() | ||
| readJob = null | ||
| serialPort?.takeIf { it.isOpen }?.closePort() | ||
| serialPort = null | ||
| super.close() | ||
| } |
| override fun scanUsbDevices(): Flow<List<DeviceListEntry.Usb>> = flow { | ||
| while (coroutineContext.isActive) { | ||
| val ports = | ||
| SerialTransport.getAvailablePorts().map { portName -> |
| emit(ports) | ||
| delay(POLL_INTERVAL_MS) | ||
| } | ||
| } |
docs/roadmap.md
Outdated
| @@ -55,9 +55,10 @@ here| **Migrate to JetBrains Compose Multiplatform dependencies** | High | Low | | |||
| | TCP | Desktop (JVM) | ✅ Done — shared `StreamFrameCodec` + `TcpTransport` in `core:network` | | |||
| | Serial/USB | Desktop (JVM) | ❌ Next — jSerialComm | | |||
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4836 +/- ##
==========================================
+ Coverage 13.38% 13.44% +0.06%
==========================================
Files 539 542 +3
Lines 17753 17822 +69
Branches 2651 2667 +16
==========================================
+ Hits 2376 2396 +20
- Misses 15058 15104 +46
- Partials 319 322 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
This commit ensures that coroutine cancellations are properly propagated in the `SerialTransport` read loop and prevents redundant disconnect calls during scope teardown. Specific changes include: - Added explicit catch blocks for `kotlinx.coroutines.CancellationException` in both the inner and outer serial read loops to ensure exceptions are rethrown and not swallowed by generic handlers. - Updated the `finally` block to only invoke `onDeviceDisconnect(true)` if the coroutine scope is still active (`isActive`). - Maintained existing error logging for non-cancellation exceptions. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
…tic complexity - Suppress CyclomaticComplexMethod to satisfy detekt rules. - Log SerialTransport IO exceptions at debug level if the coroutine was gracefully cancelled, avoiding noise during planned disconnects. - Maintain error logging for unhandled/unexpected IO exceptions.
- Mark Serial/USB on Desktop as 'Done' across roadmap and kmp-status - Update AGENTS.md and GEMINI.md descriptions to list SerialTransport - Check off the desktop README todo item
…nections feature status
This pull request introduces support for direct Serial/USB communication with Meshtastic devices on the Desktop (JVM) target, using the jSerialComm library. It implements a new
SerialTransportclass that integrates with the existing multiplatform architecture, updates documentation and build scripts, and adds tests to ensure correctness. The desktop app can now scan for, connect to, and communicate with devices over USB serial, achieving transport parity with Android.Desktop Serial/USB Transport Feature:
SerialTransportimplementation incore/networkusing jSerialComm for the JVM target. This class discovers available serial ports, manages connections, and integrates with the existingRadioTransportinterface and stream framing logic. (core/network/src/jvmMain/kotlin/org/meshtastic/core/network/SerialTransport.kt)desktop/src/main/kotlin/org/meshtastic/desktop/radio/DesktopRadioInterfaceService.kt) [1] [2] [3] [4] [5]Build and Dependency Updates:
jSerialCommas a dependency for the JVM target in the network module, and includedmockkfor JVM tests. (core/network/build.gradle.kts) [1] [2]Documentation and Architecture:
AGENTS.md,GEMINI.md,conductor/tech-stack.md,conductor/archive/desktop_serial_transport_20260317/*) [1] [2] [3] [4] [5] [6] [7]Testing:
SerialTransportto verify interface compliance, port discovery, and connection error handling. (core/network/src/jvmTest/kotlin/org/meshtastic/core/network/SerialTransportTest.kt)