Commit graph

13 commits

Author SHA1 Message Date
torlando-tech
5a9fd950de fix: TDD fixes for BLE deduplication cooldown asymmetry and test stability
- Fix BLE cooldown asymmetry: set deduplication cooldown BEFORE deciding
  which connection to keep, ensuring cooldown is applied regardless of
  whether central or peripheral connection is retained
- Fix InterfaceManagementViewModel test OOM: make STATUS_POLL_INTERVAL_MS
  testable (const→var) and add conditional return when interval <= 0
- Fix KISS.unescape() to properly skip invalid escape sequences (0xDB
  followed by invalid byte) and trailing FESC bytes instead of corrupting
  the data buffer
- Add comprehensive unit tests for all fixes following strict TDD approach

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 18:56:16 -05:00
torlando-tech
4a70196e31 fix: TDD fixes for PR #36 and custom mode UX improvements
- Fix Python RSSI/SNR race condition with proper lock acquisition
- Add manual device name validation (32 char limit, RNode name check)
- Add regulatory warnings for custom mode without region selection
- Preserve user-modified field errors during wizard navigation
- Improve custom mode UX: highlight selection, skip modem/slot steps
- Auto-expand advanced settings in custom mode review
- Hide region/modem/slot summary cards in custom mode
- Split large test file to pass detekt LargeClass rule

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 16:00:12 -05:00
torlando-tech
6fc4b519a6 fix: increase TX power validation limit to support regional maximums
The Python RNode interface hardcoded a 22 dBm maximum TX power, which
rejected valid regional settings (US allows 30 dBm, NZ allows 36 dBm).

Changes:
- Update Python validation to accept TX power up to 36 dBm (NZ max)
- Add Python tests for regional TX power limits (US 30, EU 27, NZ 36)
- Add Kotlin tests for NZ region validation (36 dBm max)

The Kotlin validator already correctly uses regional limits. The RNode
firmware provides final hardware validation and returns error 0x40 if
TX power exceeds device capability.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 14:14:45 -05:00
torlando-tech
cf49225939 fix: Python RNode interface thread safety and CI improvements
- Add lock around self.online status in _set_online() and process_outgoing()
  to prevent frame loss during online/offline transitions
- Add lock around RSSI/SNR updates for consistency with other radio state
- Log warning for malformed KISS escape sequences instead of silent ignore
- Add Python unit tests for thread safety fixes (6 new tests, 68 total)
- Add Python pytest step to CI workflow before Gradle tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 13:58:30 -05:00
torlando-tech
166bb16300 fix: address 8 critical issues from PR review with TDD
Critical fixes:
- Fix memory leak: RSSI polling now stopped in onCleared()
- Fix silent BLE scan errors: user-friendly error messages added
- Fix double-check locking bug in Python RNode initialization
- Fix interface registration order: start() before Transport register
- Fix race condition: use threading.Event for read loop control
- Fix write retry: implement exponential backoff (0.3s, 1s, 3s)
- Fix BLE write latch race: null check prevents stale callbacks
- Fix MTU request hang: 2-second timeout falls back to discoverServices

Tests added:
- RSSI polling cancellation test
- BLE scan error handling test
- Thread safety tests for read loop
- Write retry exponential backoff tests

Also includes ktlint format auto-fixes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 13:07:10 -05:00
torlando-tech
d8300627a9 feat: add event-driven UI refresh for RNode connection status
- Register interface with RNS.Transport before start() to fix race
  condition where auto-reconnect succeeds but interface wasn't tracked
- Add online status callback chain: Python → KotlinRNodeBridge →
  ReticulumServiceBinder → ServiceReticulumProtocol → ViewModel
- ViewModel now observes interfaceStatusChanged flow for immediate
  refresh when RNode connects/disconnects
- Change diagnostic logs from INFO to DEBUG level for production
- Add unit tests for RNodeOnlineStatusListener functionality

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 11:40:28 -05:00
torlando-tech
b6b34603ec fix: add BLE write synchronization to prevent dropped commands
Android BLE is asynchronous - writeCharacteristic() only queues the write.
Without waiting for onCharacteristicWrite() callback, subsequent writes
are silently dropped by the BLE stack.

Changes:
- Add CountDownLatch-based synchronization in KotlinRNodeBridge
- Each BLE write now waits for callback confirmation before proceeding
- Add small delays in Python for framebuffer writes (defensive)

This fixes reliability issues with RNode framebuffer commands on BLE.
Note: T114 still won't show framebuffer due to upstream firmware bug
(bt_ssp_pin set at startup, never cleared).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
af930401d6 feat: add human-readable RNode error messages with UI callback
Surface RNode hardware errors to users with helpful messages:
- 0x01: Radio initialization failed
- 0x02: Transmission failed
- 0x04: Data queue overflowed
- 0x40: Invalid configuration (suggests reducing TX power)

Add error callback mechanism to propagate errors from Python interface
through Kotlin bridge to the UI layer.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
b72d0227e8 fix: disable external framebuffer when logo setting is turned off
When user disables the logo display setting and applies changes,
explicitly call disable_external_framebuffer() to restore normal
RNode UI without requiring a device restart.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
d75bd2d77f feat: display Columba logo on RNode OLED via external framebuffer
Add support for displaying the Columba constellation logo on RNode's
OLED display when connected. The logo is sent via KISS protocol using
the external framebuffer commands (CMD_FB_EXT, CMD_FB_WRITE).

Changes:
- Add conversion script to render icon to 64x64 monochrome bitmap
- Add columba_logo.py with 512-byte framebuffer data
- Add framebuffer methods to ColumbaRNodeInterface
- Auto-display logo after successful RNode connection
- Enable by default via enable_framebuffer config option

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
45f4d02482 feat: add automatic RNode reconnection after disconnect
When the RNode disconnects (power cycle, out of range, etc.), the
interface now automatically attempts to reconnect:

- Starts a background reconnection loop on disconnect detection
- Tries to reconnect every 10 seconds, up to 30 attempts (~5 minutes)
- Logs progress: "Reconnection attempt X/30 for RNode..."
- Stops reconnection loop when connection succeeds or interface is stopped

Also fixes CompanionDeviceManager-triggered reconnection:
- initialize_rnode_interface() now checks for existing offline interface
- Calls start() to reconnect instead of failing due to missing config
- Handles case where interface already exists but config was cleared

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
acb10d8e95 fix: improve RNode BLE connection stability and link establishment
- Set HW_MTU to 500 to prevent RNS from truncating packet data before
  link_id computation, which was causing link establishment failures
- Increase BLE stabilization delay from 0.5s to 1.5s to allow connection
  to fully establish before configuration
- Add retry logic to writes (3 attempts with 0.3s delays) to handle
  transient BLE connection issues
- Add diagnostic logging to writeSync() for easier debugging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:48 -05:00
torlando-tech
2b46964125 feat: add RNode LoRa interface support via Bluetooth
Implements RNode interface support for LoRa communication via paired
Bluetooth RNode devices. Uses a Kotlin Bridge architecture where Kotlin
handles Bluetooth I/O and Python handles the KISS protocol.

- **KotlinRNodeBridge**: Handles Bluetooth Classic (SPP/RFCOMM) and BLE
  (Nordic UART Service) connections to RNode hardware. Manages connection
  lifecycle, data buffering, and provides read/write APIs to Python.

- **ColumbaRNodeInterface**: Python interface implementing KISS protocol
  for RNode communication. Handles frame escaping, command parsing, radio
  configuration, and integrates with RNS Transport layer.

- **UI Components**: Added RNode configuration fields to InterfaceConfigDialog
  including device name selector, connection mode (Classic/BLE), frequency,
  bandwidth, spreading factor, coding rate, and TX power settings.

- Supports both Bluetooth Classic (UUID: 00001101-0000-1000-8000-00805F9B34FB)
  and BLE via Nordic UART Service (UUID: 6e400001-b5a3-f393-e0a9-e50e24dcca9e)
- Thread-safe circular buffer for BLE packet reassembly
- Automatic device discovery from paired devices list
- Connection state management with callbacks

- Full KISS protocol implementation (FEND/FESC escape sequences)
- RNode detection and firmware version validation
- Radio parameter configuration (frequency, bandwidth, SF, CR, TX power)
- Airtime limiting support (short-term and long-term)
- Required RNS Transport interface attributes for compatibility

- set_rnode_bridge() to receive Kotlin bridge reference
- initialize_rnode_interface() called during bridge setup
- RNode interface registered with RNS.Transport.interfaces

1. **Chaquopy ByteArray conversion**: Raw bytes from Kotlin needed explicit
   `bytes()` conversion in Python due to Chaquopy's jarray handling.

2. **KISS frame format**: Initial detection commands were missing FEND
   delimiters, causing RNode to not respond to detection requests.

3. **RNS Transport compatibility**: Required iteratively adding interface
   attributes (bitrate, rxb, txb, mode, mtu, HW_MTU, FIXED_MTU,
   AUTOCONFIGURE_MTU, announce_rate_target, ifac_size, etc.) and methods
   (sent_announce(), received_announce(), process_held_announces(),
   should_ingress_limit()) to satisfy RNS Transport requirements.

4. **Owner inbound routing**: Changed from owner.inbound() to direct
   RNS.Transport.inbound() calls since owner was ReticulumWrapper, not
   Transport.

Successfully tested bidirectional communication:
- Announces sent and received between Columba and Sideband via LoRa
- Links established with ~1.8s RTT over LoRa
- Messages delivered from Columba to Sideband
- Messages received from Sideband (routing to correct identity required)

- python/rnode_interface.py (NEW): KISS protocol and RNode interface
- reticulum/rnode/KotlinRNodeBridge.kt (NEW): Bluetooth bridge
- python/reticulum_wrapper.py: RNode bridge integration
- ReticulumServiceBinder.kt: Bridge initialization in setupBridges()
- InterfaceConfigDialog.kt: RNode UI configuration fields
- InterfaceManagementViewModel.kt: RNode state management
- ReticulumConfig.kt: RNode data model with targetDeviceName, connectionMode

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 21:58:47 -05:00