Commit graph

13 commits

Author SHA1 Message Date
torlando-tech
cd723e08c3 feat: Add connection handshake to trigger peripheral callbacks
Sends empty WRITE to RX characteristic immediately after connection
to guarantee remote side's on_central_connected callback fires.

Problem: Peripheral callback triggered by WRITE events, not connections.
When central connects and only READs (Identity characteristic), the
peripheral's on_central_connected never fires, preventing peer interface
spawning on the peripheral side.

Solution: After reading Identity, write empty bytes to RX characteristic.
This triggers the WRITE callback which calls _handle_central_connected(),
ensuring bidirectional peer interface spawning.

Benefits:
- Works for Pi-to-Pi (ensures both sides spawn interfaces)
- Works for Android-to-Pi (Pi spawns interface when Android connects)
- Minimal overhead (single empty GATT write)
- Backwards compatible (empty write is harmless)

Implementation:
- Added after Identity read in _connect_to_peer()
- Uses write_gatt_char() with response=True for reliability
- Non-critical failure (logged as warning, doesn't block connection)
- TODO comment for future handshake protocol enhancements

This solves the asymmetric peer spawning issue seen in testing where
only the central side had a peer interface.

Tested: Enables bidirectional data flow for single-direction discoveries.
2025-10-31 17:43:10 -04:00
torlando-tech
27d7ea91a3 fix: Remove non-existent tunnel() method calls
Removed calls to self.owner.tunnel(peer_if) which caused AttributeError.

Root cause: Transport class doesn't have a tunnel() method. The tunnel()
method was incorrectly assumed based on other interface patterns, but
direct peer interfaces (like I2PInterface) only use:
  RNS.Transport.interfaces.append(peer_if)

No tunnel registration is needed for direct peer connections.

Changes:
- Removed tunnel() call from central connection spawn (~line 1607)
- Removed tunnel() call from peripheral connection spawn (~line 1778)
- Added explanatory comment about I2PInterface pattern

This fixes the AttributeError seen in Pi logs:
  "failed to connect: AttributeError: type object 'Transport'
   has no attribute 'tunnel'"

Peer interfaces still register correctly via RNS.Transport.interfaces[].

Tested: Interface spawning works, AttributeError eliminated.
2025-10-31 15:18:43 -04:00
torlando-tech
fae7a8c954 fix: Add debug logging and accept RSSI -127 from BlueZ
Fixes critical discovery issues caused by BlueZ/Bleak limitations.

Root cause analysis (via nRF Connect + debug logging):
1. Bleak doesn't parse service UUIDs from advertisement data (service_uuids=[])
   despite UUIDs being present (verified with nRF Connect showing correct UUID)
2. Name-based fallback works but RSSI -127 caused rejection
3. BlueZ hides connected/known devices from scan results

Changes:
- Added debug logging to detection_callback to diagnose Bleak data parsing
- Accept RSSI -127 as valid (BlueZ sentinel for "RSSI unknown")
- Confirmed name fallback pattern (RNS-*) works when service UUID fails

Test results:
- nRF Connect confirms correct UUID in advertisement: 37145b00-442d-4a94-917f-8f42c5da28e3
- Bleak sees device name "RNS-Pi1" but service_uuids=[]
- After bluetoothctl remove + RSSI fix: discovered via name pattern
- Asymmetric success: Pi 1→Pi 2 peer interface spawned, 72 bytes transmitted

Known issues:
- Bleak/BlueZ doesn't populate service_uuids from advertisement (Linux limitation)
- BlueZ auto-reconnects and hides devices from scans (requires bluetoothctl remove)
- Asymmetric discovery due to scan-hiding issue

Related: BLE_TEST_RESULTS_2025_10_31.md, BLE_DISCOVERY_TROUBLESHOOTING.md
2025-10-31 15:08:20 -04:00
torlando-tech
693cf185e4 fix: Replace placeholder BLE UUIDs with Reticulum standard UUIDs
Fixed discovery failure caused by GATT server advertising wrong service UUIDs.

Root cause: BLEGATTServer and BLEInterface were using placeholder/test UUIDs
(00000001-5824-4f48-9e1a-3b3e8f0c1234 etc.) instead of the Reticulum standard
UUID namespace (37145b00-442d-4a94-917f-8f42c5da28e*).

This caused Pis to advertise services that scanners couldn't recognize,
blocking all BLE discovery and connection attempts.

Changes:
- BLEGATTServer.py: Updated all 4 service/characteristic UUIDs
- BLEInterface.py: Updated all 4 service/characteristic UUIDs

Diagnosed using nRF Connect mobile app which showed wrong UUIDs being advertised.

Related: BLE_TEST_RESULTS_2025_10_31.md
2025-10-31 14:04:06 -04:00
torlando-tech
3c28070d3b Fix: Add missing tunnel registration for BLEPeerInterface
Problem:
- BLEPeerInterface was spawning but never calling owner.tunnel(self)
- Result: 0 tunnel table entries, no data transmission
- Peer interfaces showed as "reachable" but Transport couldn't route through them

Solution:
- Added owner.tunnel(peer_if) call after interface creation
- Applied to both spawn locations (central and peripheral connections)
- Pattern matches Android implementation in android_ble_interface.py

Changes:
- Line 1599-1601: Added tunnel registration for central connections
- Line 1770-1772: Added tunnel registration for peripheral connections

Testing:
- Peer interfaces now appear in rnstatus output
- BLEPeerInterface[RNS-Pi2/central] visible and marked as "reachable"
- AttributeError logged during tunnel() but interface still spawns
- Further investigation needed for data transmission

References:
- BLE_DATA_ROUTING_ISSUE.md - Root cause analysis
- BLE_SESSION_2025_10_31_PROGRESS.md - Detailed session notes
- android_ble_interface.py:403 - Reference implementation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 11:42:08 -04:00
torlando-tech
905f9b684c refactor: use final_init() hook with infinite wait instead of timeout polling
Replace timeout-based polling with cleaner event-driven approach using Interface.final_init() lifecycle hook. Launches background thread that waits indefinitely for Transport.identity (which is guaranteed to load), then starts GATT server with valid 16-byte identity value.

Benefits:
- No arbitrary timeout (Transport.identity WILL load, just timing varies)
- Uses proper Interface lifecycle hook (final_init)
- Non-blocking background thread
- GATT server guaranteed to have valid identity when it starts
- Cleaner separation of concerns

Same polling mechanism as I2PInterface, but better integrated with Interface lifecycle.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 21:30:51 -04:00
torlando-tech
1a4de3b4ea fix: resolve deadlock between interface online and Transport.identity loading
Move self.online = True BEFORE waiting for Transport.identity to break circular dependency. Reticulum loads Transport.identity only after interfaces are online, so blocking before self.online = True creates infinite wait.

New sequence:
1. Set self.online = True (unblocks Reticulum startup)
2. Reticulum loads Transport.identity from storage
3. Wait completes successfully
4. Identity set on GATT server
5. GATT server starts with valid 16-byte identity

Reduced timeout from 30s to 10s since identity should load within 1s once interface is online.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 21:15:42 -04:00
torlando-tech
6ca2e85142 fix: ensure Transport.identity loaded before GATT server starts
Change from async deferred loading to synchronous wait before GATT server startup. This ensures the Identity characteristic is created with a valid 16-byte value instead of empty [], preventing BlueZ from rejecting or corrupting the advertisement which caused "0 matching service UUID" discovery failures.

The bug: Identity characteristic was being created with value=[] because the GATT server thread started before Transport.identity was loaded from storage (~1s timing window). BlueZ may silently reject advertisements when validating GATT databases with empty READ characteristics.

The fix: Block interface startup for up to 30s waiting for Transport.identity (typically available within 0.5-1s), then set it on GATT server BEFORE starting the server thread. Identity characteristic now always has valid 16-byte value when registered with BlueZ.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 20:40:31 -04:00
torlando-tech
76bfa019ca fix: defer Transport.identity loading to avoid startup timing issue
Move Transport.identity extraction from synchronous startup to async background task. The identity is loaded from storage AFTER interface initialization, causing "Transport.identity not available yet" warning. Now polls for identity every 1s for up to 30s and sets it when available.

Fixes Protocol v2 identity characteristic serving on GATT server.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 20:16:46 -04:00
torlando-tech
b05295ff67 feat: add Identity characteristic for stable peer tracking across MAC rotations
Implements BLE Protocol v2 with Transport identity GATT characteristic to solve Android MAC address rotation issues. Adds IDENTITY_CHAR_UUID (00000004-...) that serves the 16-byte RNS.Transport.identity.hash, enabling reliable bidirectional mesh connectivity with Android devices whose BLE MAC addresses rotate every ~15 minutes.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 19:39:33 -04:00
torlando-tech
a0f361a3f4 feat: add Bluetooth adapter power state handling and auto-power-on
Addresses the common "Not Powered" error where Bluetooth adapters are
powered off, preventing BLE operations. This issue is particularly common
on Raspberry Pi after boot or system updates.

Changes:

1. README.md - Added comprehensive troubleshooting section
   - New section: "Bluetooth adapter not powered"
   - Documented symptoms, causes, and 4 solution methods
   - Instructions for automatic power-on at boot
   - Cross-referenced from other sections

2. BLEInterface.py - Enhanced error handling in _discover_peers()
   - Detect "Not Powered" errors specifically
   - Show clear, actionable error messages instead of stack traces
   - Provide direct solution commands
   - Link to troubleshooting documentation
   - Gracefully handle error without crashing

3. install.sh - Automatic power state checking
   - New "Step 5B: Bluetooth Adapter Power State" section
   - Check if adapter is powered using `bluetoothctl show`
   - Automatically power on adapter if needed
   - Verify operation succeeded
   - Provide troubleshooting steps if power-on fails
   - Check for rfkill blocks

4. examples/bluetooth-power-on.service - New systemd service
   - Ensures Bluetooth is powered on at boot
   - Optional but recommended for production
   - Includes installation instructions in README

5. examples/config_example.toml - Added troubleshooting entry #7
   - Documents power state issue in config comments
   - Cross-references systemd service example
   - Notes that installer (v1.x+) handles this automatically

Impact:
- Users get clear guidance instead of cryptic stack traces
- Installer automatically fixes the issue during setup
- Reduces support burden for common power state errors
- Enables automatic recovery at boot via systemd service

Fixes: "Not Powered" / "No powered Bluetooth adapters found" errors
Tested on: Raspberry Pi Zero 2 W with Raspberry Pi OS Lite 64-bit

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 00:10:57 -04:00
torlando-tech
80d8ff7c24 fix: support custom config directories via RNS.Reticulum.configdir
The BLE interface now dynamically resolves the interface directory
by checking RNS.Reticulum.configdir when loaded via exec() by Reticulum.
This allows users to specify custom config directories using the
--config flag without encountering import errors.

Changes:
- Update BLEInterface.py to use RNS.Reticulum.configdir when available
- Add fallback to default ~/.reticulum/interfaces for backward compatibility
- Add comprehensive test coverage for config directory resolution
- Update documentation to mention custom config directory support

Fixes #2

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 19:09:48 -04:00
torlando-tech
486f210ae4 Initial commit: BLE Reticulum interface 2025-10-26 19:14:14 -04:00