Enhances the installation script to automatically detect and handle pipx installations of RNS, eliminating the need for manual dependency injection in most cases. Changes to install.sh: - Add pipx detection logic that checks for RNS in pipx paths - Verify pipx command availability and RNS listing - Install build dependencies (build-essential, python3-dev, libdbus-dev) for Debian/Ubuntu and (base-devel, gobject-introspection) for Arch - Implement automated pipx inject for all BLE dependencies (bleak, bluezero, dbus-python) - Add progress messages for long-running dbus-python compilation - Verify all dependencies after injection - Use correct Python executable for setcap based on install mode Changes to README.md: - Update Option A description to mention pipx detection - Add note to Option C that install.sh now handles pipx automatically - Keep manual instructions for troubleshooting/fallback Benefits: - Consistent "one-command installation" experience for all users - Reduces user errors from manual pipx injection - Provides clear error messages with recovery instructions - Maintains manual documentation as fallback Related to #11 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
527 lines
18 KiB
Markdown
527 lines
18 KiB
Markdown
# Reticulum BLE Interface
|
|
|
|
A Bluetooth Low Energy (BLE) interface for [Reticulum Network Stack](https://reticulum.network), enabling mesh networking over BLE without additional hardware on Linux devices.
|
|
|
|
**⚠️ Platform**: Linux-only (requires BlueZ 5.x for GATT server functionality)
|
|
**✅ Tested on**: Raspberry Pi Zero W
|
|
|
|
## Features
|
|
|
|
- **Zero dongle requirements**: Works with built-in BLE radios (Raspberry Pi, Linux laptops, etc.)
|
|
- **Auto-discovery**: Automatically finds and connects to nearby Reticulum BLE nodes
|
|
- **Multi-peer mesh**: Supports up to 7 simultaneous connections for mesh networking (may support more, untested)
|
|
- **Dual mode operation**: Acts as both central (scanner/client) and peripheral (advertiser/server)
|
|
- **Connection prioritization**: RSSI-based smart peer selection with connection history tracking
|
|
- **Packet fragmentation**: Handles BLE MTU limitations (20-512 bytes) transparently
|
|
- **Enhanced error handling**: Retry logic, exponential backoff, connection recovery
|
|
- **Power management**: Three power modes (aggressive/balanced/saver) for battery efficiency or CPU limitations. Saver mode tested on Raspberry Pi Zero W.
|
|
|
|
## Installation
|
|
|
|
**Prerequisites:**
|
|
- Python 3.8 or higher
|
|
- Reticulum Network Stack already installed ([installation guide](https://reticulum.network))
|
|
- Linux with BlueZ 5.x
|
|
|
|
### Option A: Automated Installation (Recommended)
|
|
|
|
The installation script automatically detects your Reticulum setup and installs dependencies in the correct environment:
|
|
|
|
```bash
|
|
# Download and run installer
|
|
git clone https://github.com/torlando-tech/ble-reticulum.git
|
|
cd ble-reticulum
|
|
chmod +x install.sh
|
|
./install.sh
|
|
|
|
# For custom config directory:
|
|
# ./install.sh --config /path/to/custom/config
|
|
```
|
|
|
|
The script will:
|
|
1. ✓ Detect if Reticulum is in a venv, pipx, or system-wide
|
|
2. ✓ Install system dependencies (BlueZ, dbus, build tools if needed)
|
|
3. ✓ Install Python packages in the correct environment (via pipx inject if needed)
|
|
4. ✓ Copy BLE interface files to `~/.reticulum/interfaces/` (or custom config directory if specified)
|
|
5. ✓ Enable BlueZ experimental mode (required for proper BLE connectivity)
|
|
6. ✓ Optionally set up Bluetooth permissions
|
|
|
|
**BlueZ Experimental Mode**: The installer automatically enables BlueZ experimental mode, which is required for proper BLE connectivity. This allows the BLE interface to use LE-specific connection methods instead of defaulting to Classic Bluetooth (BR/EDR), preventing connection errors like "br-connection-profile-unavailable".
|
|
|
|
To skip this configuration (not recommended):
|
|
```bash
|
|
./install.sh --skip-experimental
|
|
```
|
|
|
|
**Pi Zero W Optimization**: The installer automatically detects Raspberry Pi Zero W (32-bit ARM with Python 3.13) and downloads pre-built wheels for packages with C extensions. This saves ~20 minutes of compilation time compared to building from source. See [Pre-built Wheels](#pre-built-wheels-for-raspberry-pi-zero-w) for details.
|
|
|
|
### Option B: Manual Installation
|
|
|
|
#### 1. Install System Dependencies
|
|
|
|
**Debian/Ubuntu/Raspberry Pi OS:**
|
|
```bash
|
|
sudo apt-get update
|
|
sudo apt-get install python3-pip python3-gi python3-dbus python3-cairo bluez
|
|
```
|
|
|
|
**Arch Linux:**
|
|
```bash
|
|
sudo pacman -S base-devel gobject-introspection python-pip python-dbus python-cairo bluez bluez-utils
|
|
```
|
|
|
|
**Why these packages?**
|
|
- `base-devel`: Build tools (gcc, make, meson) required for compiling PyGObject
|
|
- `gobject-introspection`: Development files for GObject introspection (required for PyGObject compilation)
|
|
- `python-dbus`: D-Bus Python bindings for BlueZ communication
|
|
- `python-cairo`: Cairo graphics library
|
|
- `bluez` / `bluez-utils`: Bluetooth stack and utilities for Linux
|
|
|
|
**Note for Arch users:** PyGObject is intentionally NOT installed as a system package on Arch due to version incompatibility (Arch has 3.54.5, but bluezero requires <3.52.0). Instead, pip will compile the compatible PyGObject version (3.50.2) during installation. This adds ~2 minutes to installation time but ensures compatibility.
|
|
|
|
#### 2. Install Python Dependencies
|
|
|
|
**IMPORTANT:** Install in the same environment as Reticulum!
|
|
|
|
Since we installed system packages for PyGObject, dbus-python, and pycairo in step 1, we only need to install the pure-Python packages:
|
|
|
|
**If Reticulum is in a virtual environment:**
|
|
```bash
|
|
# Activate the same venv where Reticulum is installed
|
|
source /path/to/reticulum-venv/bin/activate
|
|
pip install bleak==1.1.1 bluezero
|
|
```
|
|
|
|
**If Reticulum is installed system-wide:**
|
|
```bash
|
|
# Install system-wide (may need sudo)
|
|
pip install bleak==1.1.1 bluezero
|
|
# OR
|
|
sudo pip install bleak==1.1.1 bluezero
|
|
```
|
|
|
|
**Note:** The system packages (python3-gi, python3-dbus, python3-cairo) provide PyGObject, dbus-python, and pycairo, eliminating the need for lengthy compilation from source.
|
|
|
|
#### 3. Copy BLE Interface Files
|
|
|
|
```bash
|
|
# Copy to Reticulum's interface directory
|
|
mkdir -p ~/.reticulum/interfaces
|
|
cp src/RNS/Interfaces/BLE*.py ~/.reticulum/interfaces/
|
|
```
|
|
|
|
#### 4. Enable BlueZ Experimental Mode (Required)
|
|
|
|
BlueZ experimental mode is required for proper BLE connectivity. Without it, BlueZ may attempt Classic Bluetooth (BR/EDR) connections instead of BLE (LE) connections, causing connection failures.
|
|
|
|
Enable experimental mode (BlueZ >= 5.49):
|
|
```bash
|
|
sudo systemctl edit bluetooth
|
|
```
|
|
|
|
Add these lines:
|
|
```
|
|
[Service]
|
|
ExecStart=
|
|
ExecStart=/usr/lib/bluetooth/bluetoothd -E
|
|
```
|
|
|
|
Save and restart Bluetooth:
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl restart bluetooth
|
|
```
|
|
|
|
Verify it's enabled:
|
|
```bash
|
|
ps aux | grep bluetoothd
|
|
# Should show: /usr/lib/bluetooth/bluetoothd -E
|
|
```
|
|
|
|
#### 5. Grant Bluetooth Permissions
|
|
|
|
For non-root operation:
|
|
```bash
|
|
sudo setcap 'cap_net_raw,cap_net_admin+eip' $(which python3)
|
|
```
|
|
|
|
**Note:** If Reticulum is in a venv, grant permissions to that Python:
|
|
```bash
|
|
sudo setcap 'cap_net_raw,cap_net_admin+eip' /path/to/venv/bin/python3
|
|
```
|
|
|
|
### Option C: pipx Installation (RNS installed via pipx)
|
|
|
|
If you installed Reticulum via `pipx install rns`, the BLE interface requires additional setup because pipx creates isolated virtual environments that cannot access system-installed packages.
|
|
|
|
**Note:** The automated installation script (Option A: `./install.sh`) now detects and handles pipx installations automatically. The instructions below are for manual installation or troubleshooting.
|
|
|
|
#### 1. Install System Dependencies
|
|
|
|
**Arch Linux:**
|
|
```bash
|
|
sudo pacman -S base-devel gobject-introspection python-dbus python-cairo bluez bluez-utils
|
|
```
|
|
|
|
**Debian/Ubuntu/Raspberry Pi OS:**
|
|
```bash
|
|
sudo apt-get update
|
|
sudo apt-get install build-essential python3-dev python3-gi python3-dbus python3-cairo bluez libdbus-1-dev
|
|
```
|
|
|
|
#### 2. Inject BLE Dependencies into pipx Environment
|
|
|
|
Because pipx creates isolated environments, you must inject the BLE dependencies into the RNS environment:
|
|
|
|
```bash
|
|
# Inject BLE dependencies into pipx RNS environment
|
|
pipx inject rns bleak==1.1.1 bluezero dbus-python
|
|
```
|
|
|
|
**Note:** This will compile `dbus-python` from source, which requires the system development libraries installed in step 1.
|
|
|
|
#### 3. Copy BLE Interface Files
|
|
|
|
```bash
|
|
# Copy to Reticulum's interface directory
|
|
mkdir -p ~/.reticulum/interfaces
|
|
cp src/RNS/Interfaces/BLE*.py ~/.reticulum/interfaces/
|
|
```
|
|
|
|
#### 4. Grant Bluetooth Permissions
|
|
|
|
Find the Python executable used by pipx for RNS:
|
|
|
|
```bash
|
|
# Find pipx RNS Python path
|
|
PIPX_RNS_PYTHON=$(pipx runpip rns show rns | grep Location | awk '{print $2}' | sed 's/lib\/python.*/bin\/python3/')
|
|
|
|
# Grant capabilities
|
|
sudo setcap 'cap_net_raw,cap_net_admin+eip' "$PIPX_RNS_PYTHON"
|
|
```
|
|
|
|
Alternatively, find the path manually:
|
|
```bash
|
|
# List pipx environments
|
|
ls ~/.local/pipx/venvs/
|
|
|
|
# Grant capabilities to the rns venv Python
|
|
sudo setcap 'cap_net_raw,cap_net_admin+eip' ~/.local/pipx/venvs/rns/bin/python3
|
|
```
|
|
|
|
#### 5. Enable BlueZ Experimental Mode
|
|
|
|
The BLE interface requires BlueZ experimental features for proper BLE connectivity:
|
|
|
|
```bash
|
|
# Edit BlueZ service configuration
|
|
sudo systemctl edit bluetooth.service
|
|
```
|
|
|
|
Add the following content:
|
|
```ini
|
|
[Service]
|
|
ExecStart=
|
|
ExecStart=/usr/lib/bluetooth/bluetoothd --experimental
|
|
```
|
|
|
|
Then reload and restart:
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl restart bluetooth.service
|
|
|
|
# Verify experimental mode is enabled
|
|
systemctl status bluetooth.service | grep -i experimental
|
|
```
|
|
|
|
#### Why pipx Requires Special Handling
|
|
|
|
pipx creates isolated virtual environments with `--no-site-packages` to prevent package conflicts. This means:
|
|
- System packages like `python-dbus` (installed via apt/pacman) are not accessible
|
|
- `dbus-python` must be compiled from source within the pipx environment
|
|
- `pipx inject` installs packages directly into RNS's isolated environment
|
|
|
|
This isolation is intentional and prevents conflicts, but requires the extra injection step for system-dependent packages like `dbus-python`.
|
|
|
|
## Quick Start
|
|
|
|
### 1. Configure Reticulum
|
|
|
|
Add the BLE interface to your Reticulum configuration (`~/.reticulum/config`):
|
|
|
|
```toml
|
|
[[BLE Interface]]
|
|
type = BLEInterface
|
|
enabled = yes
|
|
|
|
# Optional: set short device name (max 8 chars recommended, default: none)
|
|
# device_name = RNS
|
|
```
|
|
|
|
For detailed configuration options, see [`examples/config_example.toml`](examples/config_example.toml).
|
|
|
|
**Custom Config Directory**: If you use a custom Reticulum config directory with `--config`, the BLE interface will automatically use that directory to find its companion modules. No additional configuration needed!
|
|
|
|
### 2. Start Reticulum
|
|
|
|
```bash
|
|
rnsd --verbose
|
|
```
|
|
|
|
The interface will:
|
|
1. Start advertising as a peripheral (if enabled)
|
|
2. Scan for nearby BLE peers
|
|
3. Automatically connect to discovered peers
|
|
4. Form a mesh network with other BLE nodes
|
|
|
|
### 3. Verify Operation
|
|
|
|
```bash
|
|
# Check interface status
|
|
rnstatus
|
|
|
|
# Monitor announces
|
|
rnid -a
|
|
```
|
|
|
|
## Configuration
|
|
|
|
The BLE interface supports extensive configuration options. See [`examples/config_example.toml`](examples/config_example.toml) for a fully documented example with all available options.
|
|
|
|
### Key Configuration Options
|
|
|
|
- **`device_name`**: Optional BLE device name (default: none, keep short if used, max 8 chars recommended)
|
|
- **`service_uuid`**: BLE service UUID (must match on all devices)
|
|
- **`enable_peripheral`**: Accept incoming connections (default: yes)
|
|
- **`enable_central`**: Scan and connect to peers (default: yes)
|
|
- **`discovery_interval`**: How often to scan for new peers (default: 5.0 seconds)
|
|
- **`max_connections`**: Maximum simultaneous connections (default: 7)
|
|
- **`min_rssi`**: Minimum signal strength in dBm (default: -85)
|
|
- **`power_mode`**: Power management (aggressive/balanced/saver)
|
|
|
|
## Testing
|
|
|
|
For detailed testing information, see [TESTING.md](TESTING.md).
|
|
|
|
Quick test using example script (no BLE hardware required):
|
|
```bash
|
|
cd examples
|
|
python ble_minimal_test.py test
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### No peers discovered
|
|
- Verify Bluetooth is enabled: `bluetoothctl show`
|
|
- Check `service_uuid` matches on all devices
|
|
- Try `power_mode = aggressive` for faster discovery
|
|
- Increase `min_rssi` to -90 for longer range
|
|
|
|
### Connection timeouts
|
|
- Increase `connection_timeout` to 60
|
|
- Reduce `max_connections` to 3-5
|
|
- Check for BLE/WiFi interference (both use 2.4 GHz)
|
|
- Verify peer is within range (typically 10-30m)
|
|
- If logs show "Operation already in progress" errors, this is handled automatically in v2.2.1+ with connection state tracking and rate limiting (see [BLE_PROTOCOL_v2.2.md](BLE_PROTOCOL_v2.2.md) § Troubleshooting for details)
|
|
|
|
### GATT server failed to start
|
|
- Ensure BlueZ 5.x is installed: `bluetoothd --version`
|
|
- Check Bluetooth permissions (see Installation → Manual Installation → step 4)
|
|
- Try `sudo rnsd` temporarily to verify (not recommended for production)
|
|
- Set `enable_peripheral = no` to disable peripheral mode
|
|
|
|
### Permission denied errors
|
|
- Grant capabilities to Python (see Installation → Manual Installation → step 5)
|
|
- Or run with sudo: `sudo rnsd` (not recommended)
|
|
|
|
### BR/EDR connection errors (br-connection-profile-unavailable, ProfileUnavailable)
|
|
These errors occur when BlueZ attempts Classic Bluetooth (BR/EDR) connections instead of BLE (LE) connections. This is the most common BLE connection issue.
|
|
|
|
**Symptoms:**
|
|
- Devices connect then immediately disconnect
|
|
- Errors: "br-connection-profile-unavailable", "ProfileUnavailable"
|
|
- "ConnectDevice() unavailable" in logs
|
|
- Devices get blacklisted after multiple failures
|
|
|
|
**Solution:**
|
|
Enable BlueZ experimental mode (see Installation → Manual Installation → step 4). If you used the automated installer, re-run it without `--skip-experimental`.
|
|
|
|
### Bluetooth adapter not powered / "No powered Bluetooth adapters found"
|
|
The Bluetooth adapter exists but is powered off, preventing BLE operations.
|
|
|
|
**Symptoms:**
|
|
- Error: `dbus.exceptions.DBusException: org.bluez.Error.Failed: Not Powered`
|
|
- Error: `BleakError: No powered Bluetooth adapters found.`
|
|
- BLE interface fails to start or discover peers
|
|
- GATT server startup fails immediately
|
|
|
|
**Cause:**
|
|
The Bluetooth adapter is in a powered-off state. This commonly happens on Raspberry Pi after boot or system updates.
|
|
|
|
**Solution:**
|
|
Power on the Bluetooth adapter:
|
|
|
|
```bash
|
|
# Option 1: Using bluetoothctl (recommended)
|
|
bluetoothctl power on
|
|
|
|
# Option 2: If adapter is RF-blocked
|
|
sudo rfkill unblock bluetooth
|
|
|
|
# Option 3: Using hciconfig (older systems)
|
|
sudo hciconfig hci0 up
|
|
|
|
# Verify adapter is powered:
|
|
bluetoothctl show
|
|
# Should display "Powered: yes"
|
|
```
|
|
|
|
**Automatic power-on at boot:**
|
|
Ensure Bluetooth service is enabled and starts at boot:
|
|
|
|
```bash
|
|
# Enable Bluetooth service
|
|
sudo systemctl enable bluetooth
|
|
sudo systemctl start bluetooth
|
|
|
|
# For persistent power-on, create a systemd service:
|
|
# See examples/bluetooth-power-on.service
|
|
```
|
|
|
|
The automated installer (v1.x+) automatically checks and powers on the Bluetooth adapter during installation.
|
|
|
|
### pipx: ModuleNotFoundError for dbus, gi, or bluezero
|
|
If you installed RNS via pipx and get import errors like `ModuleNotFoundError: No module named 'dbus'`, `No module named 'gi'`, or `No module named 'bluezero'`:
|
|
|
|
**Cause:** pipx creates isolated environments that don't access system packages.
|
|
|
|
**Solution:** Follow the [pipx installation instructions](#option-c-pipx-installation-rns-installed-via-pipx) to inject the required dependencies:
|
|
```bash
|
|
pipx inject rns bleak==1.1.1 bluezero dbus-python
|
|
```
|
|
|
|
**Verification:** Test if the modules are accessible:
|
|
```bash
|
|
pipx run rns python3 -c "import dbus, gi, bleak, bluezero; print('All modules found')"
|
|
```
|
|
## Architecture
|
|
|
|
The BLE interface consists of four main components:
|
|
|
|
- **`BLEInterface.py`**: Main interface implementation, handles discovery and connections
|
|
- **`BLEGATTServer.py`**: GATT server for peripheral mode (accepting connections)
|
|
- **`BLEFragmentation.py`**: Packet fragmentation/reassembly for BLE MTU limits
|
|
- **`BLEAgent.py`**: Per-peer connection management
|
|
|
|
## Development Setup
|
|
|
|
For contributors and developers who want to work on the BLE interface code itself.
|
|
|
|
**Note:** This setup is different from the production installation above. Use a virtual environment for development to avoid conflicts.
|
|
|
|
```bash
|
|
# Clone repository
|
|
git clone https://github.com/torlando-tech/ble-reticulum.git
|
|
cd ble-reticulum
|
|
|
|
# Create and activate virtual environment
|
|
python3 -m venv venv
|
|
source venv/bin/activate
|
|
|
|
# Install RNS (required for tests)
|
|
pip install rns
|
|
|
|
# Install all dependencies (runtime + development + testing)
|
|
pip install -r requirements-dev.txt
|
|
|
|
# Create package structure for tests
|
|
touch src/RNS/__init__.py
|
|
touch src/RNS/Interfaces/__init__.py
|
|
|
|
# Run tests
|
|
pytest
|
|
|
|
# Run tests with coverage
|
|
pytest --cov=src/RNS/Interfaces --cov-report=html
|
|
```
|
|
|
|
For detailed development and testing guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md) and [TESTING.md](TESTING.md).
|
|
|
|
## Pre-built Wheels for Raspberry Pi Zero W
|
|
|
|
To speed up installation on 32-bit ARM devices (Raspberry Pi Zero W, Pi 1, Pi 2), we provide pre-built wheels for packages with C extensions that would otherwise require lengthy compilation from source.
|
|
|
|
### Automatic Installation
|
|
|
|
The `install.sh` script **automatically detects** 32-bit ARM architecture with Python 3.13 and downloads pre-built wheels from [GitHub Releases](https://github.com/torlando-tech/ble-reticulum/releases/tag/armv6l-wheels-v1).
|
|
|
|
**Time savings:** ~20 minutes on Pi Zero W (avoids compiling C extensions)
|
|
|
|
### Available Wheels
|
|
|
|
| Package | Version | Python | Architecture | Size |
|
|
|---------|---------|--------|--------------|------|
|
|
| dbus_fast | 2.44.5 | 3.13 | ARMv6l | 874KB |
|
|
|
|
### Manual Installation
|
|
|
|
If you need to install wheels manually (e.g., in a custom Python environment):
|
|
|
|
```bash
|
|
# Download the wheel
|
|
wget https://github.com/torlando-tech/ble-reticulum/releases/download/armv6l-wheels-v1/dbus_fast-2.44.5-cp313-cp313-linux_armv6l.whl
|
|
|
|
# Install it
|
|
pip install dbus_fast-2.44.5-cp313-cp313-linux_armv6l.whl
|
|
```
|
|
|
|
### Building Your Own Wheels
|
|
|
|
If you need to build wheels for a different Python version on 32-bit ARM:
|
|
|
|
```bash
|
|
# Install build dependencies
|
|
sudo apt-get install python3-dev libdbus-1-dev pkg-config
|
|
|
|
# Build the wheel
|
|
pip wheel dbus_fast==2.44.5
|
|
|
|
# The wheel will be saved in the current directory
|
|
# You can then share it or install it on other devices
|
|
```
|
|
|
|
### Why Pre-built Wheels?
|
|
|
|
Python packages with C extensions (like `dbus_fast`) must be compiled from source when installing via pip if no compatible wheel is available on PyPI. On low-powered devices like the Pi Zero W:
|
|
|
|
- **Without pre-built wheel:** 15-30 minutes of compilation
|
|
- **With pre-built wheel:** < 10 seconds download and install
|
|
|
|
The automated installer makes this transparent - it "just works" faster on supported platforms.
|
|
|
|
## Contributing
|
|
|
|
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for:
|
|
- Code style guidelines
|
|
- Pull request process
|
|
- Bug report templates
|
|
- Feature request guidelines
|
|
|
|
## Supporting
|
|
[](https://ko-fi.com/B0B51NFT1Z)
|
|
|
|
## License
|
|
|
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
|
|
## Acknowledgments
|
|
|
|
- [Reticulum Network Stack](https://reticulum.network) by Mark Qvist
|
|
- Built using [bleak](https://github.com/hbldh/bleak) for BLE central operations
|
|
- Built using [bluezero](https://github.com/ukBaz/python-bluezero) for GATT server
|
|
|
|
## Links
|
|
|
|
- [Reticulum Network Stack](https://reticulum.network)
|
|
- [Reticulum Documentation](https://markqvist.github.io/Reticulum/manual/)
|
|
- [Reticulum GitHub](https://github.com/markqvist/Reticulum)
|