feat: enable BlueZ experimental mode by default to fix BLE connection issues

Fixes #3

BlueZ experimental mode is required for proper BLE connectivity. Without it,
BlueZ attempts Classic Bluetooth (BR/EDR) connections instead of BLE (LE)
connections, causing connection errors like "br-connection-profile-unavailable"
and immediate disconnections after pairing.

Changes:
- install.sh: Automatically enables BlueZ experimental mode during installation
  - Detects BlueZ version (requires >= 5.49)
  - Creates systemd override to add -E flag to bluetoothd
  - Checks if already enabled to avoid duplicate configuration
  - Shows strong warning if user skips with --skip-experimental flag
- Added --skip-experimental flag to opt-out (not recommended)
- Updated help text to document new flag
- tests/test_installer.sh: Added tests for experimental mode configuration
- README.md: Documented BlueZ experimental mode in installation sections
  - Added to automated installation description
  - Added as required step in manual installation
  - Added troubleshooting section for BR/EDR connection errors
- examples/config_example.toml: Added troubleshooting entry for BR/EDR errors

The installer now:
1. Detects BlueZ version >= 5.49 (required for experimental mode)
2. Checks if already enabled (graceful skip)
3. Enables experimental mode by default unless --skip-experimental is used
4. Shows prominent warning if skipped (may cause BLE to break)
5. Handles edge cases (no systemd, old BlueZ, container environments)

This addresses the root cause reported in issue #3 where devices were
connecting then immediately disconnecting with BR/EDR profile errors.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
torlando-tech 2025-10-28 23:25:52 -04:00
commit 0e00fbf2d6
4 changed files with 252 additions and 7 deletions

View file

@ -43,7 +43,15 @@ The script will:
2. ✓ Install system dependencies (BlueZ, dbus)
3. ✓ Install Python packages in the correct environment
4. ✓ Copy BLE interface files to `~/.reticulum/interfaces/` (or custom config directory if specified)
5. ✓ Optionally set up Bluetooth permissions
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
```
### Option B: Manual Installation
@ -100,7 +108,35 @@ mkdir -p ~/.reticulum/interfaces
cp src/RNS/Interfaces/BLE*.py ~/.reticulum/interfaces/
```
#### 4. Grant Bluetooth Permissions
#### 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
@ -199,9 +235,21 @@ python ble_minimal_test.py test
- Set `enable_peripheral = no` to disable peripheral mode
### Permission denied errors
- Grant capabilities to Python (see Installation → Manual Installation → step 4)
- 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`.
## Architecture
The BLE interface consists of four main components:

View file

@ -253,6 +253,19 @@ power_mode = balanced
# - Restart Bluetooth service (Linux: sudo systemctl restart bluetooth)
# - Check device_name is not too long (max ~20 characters)
#
# 6. BR/EDR connection errors (br-connection-profile-unavailable, ProfileUnavailable):
# IMPORTANT: This is the most common BLE connection issue!
# - Symptoms: Devices connect then immediately disconnect, "ConnectDevice() unavailable" in logs
# - Cause: BlueZ attempting Classic Bluetooth (BR/EDR) instead of BLE (LE) connections
# - Solution: Enable BlueZ experimental mode (required for proper BLE connectivity)
# Linux: sudo systemctl edit bluetooth
# Add: [Service]
# ExecStart=
# ExecStart=/usr/lib/bluetooth/bluetoothd -E
# Then: sudo systemctl daemon-reload && sudo systemctl restart bluetooth
# - If you used install.sh, it should have enabled this automatically
# - Verify: ps aux | grep bluetoothd (should show -E flag)
#
# For more troubleshooting, see README.md
# ============================================================================

View file

@ -51,19 +51,26 @@ pip_install() {
# Parse command line arguments
CUSTOM_CONFIG_DIR=""
SKIP_BLUEZ_EXPERIMENTAL=false
while [[ $# -gt 0 ]]; do
case $1 in
--config)
CUSTOM_CONFIG_DIR="$2"
shift 2
;;
--skip-experimental)
SKIP_BLUEZ_EXPERIMENTAL=true
shift
;;
-h|--help)
echo "Usage: $0 [--config CONFIG_DIR]"
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --config CONFIG_DIR Install to custom Reticulum config directory"
echo " (default: ~/.reticulum)"
echo " -h, --help Show this help message"
echo " --config CONFIG_DIR Install to custom Reticulum config directory"
echo " (default: ~/.reticulum)"
echo " --skip-experimental Skip enabling BlueZ experimental mode"
echo " WARNING: May cause BLE connection failures"
echo " -h, --help Show this help message"
exit 0
;;
*)
@ -351,6 +358,139 @@ fi
echo
# Step 5A: BlueZ Experimental Mode
print_header "BlueZ Experimental Mode"
# Check if bluetoothctl is available
if ! command -v bluetoothctl &> /dev/null; then
print_warning "bluetoothctl not found - BlueZ may not be installed"
print_info "BLE interface requires BlueZ for Bluetooth functionality"
echo
elif ! command -v systemctl &> /dev/null; then
print_warning "systemctl not found - cannot configure BlueZ experimental mode"
print_info "This system may not use systemd, or this may be a container environment"
echo
else
# Detect BlueZ version
BLUEZ_VERSION=$(bluetoothctl --version 2>/dev/null | grep -oP '\d+\.\d+' | head -1)
if [ -z "$BLUEZ_VERSION" ]; then
print_warning "Could not detect BlueZ version"
echo
else
print_info "Detected BlueZ version: $BLUEZ_VERSION"
# Parse version to check if >= 5.49
VERSION_MAJOR=$(echo "$BLUEZ_VERSION" | cut -d. -f1)
VERSION_MINOR=$(echo "$BLUEZ_VERSION" | cut -d. -f2)
if [ "$VERSION_MAJOR" -lt 5 ] || ([ "$VERSION_MAJOR" -eq 5 ] && [ "$VERSION_MINOR" -lt 49 ]); then
print_warning "BlueZ version $BLUEZ_VERSION does not support experimental mode (requires >= 5.49)"
print_info "BLE interface will work with standard connection methods"
print_info "Consider upgrading BlueZ for full BLE compatibility"
echo
else
# Check if experimental mode is already enabled
if systemctl status bluetooth 2>/dev/null | grep -q -- "-E\|--experimental"; then
print_success "BlueZ experimental mode already enabled"
echo
elif [ "$SKIP_BLUEZ_EXPERIMENTAL" = true ]; then
# User explicitly skipped experimental mode - show strong warning
echo
print_error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_error "WARNING: Skipping BlueZ experimental mode"
print_error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo
echo -e "${RED}BLE connections may fail with errors like:${NC}"
echo " • br-connection-profile-unavailable"
echo " • ProfileUnavailable"
echo " • Immediate disconnections after pairing"
echo
echo -e "${RED}Your BLE interface may attempt Classic Bluetooth (BR/EDR)${NC}"
echo -e "${RED}connections instead of BLE (LE) connections.${NC}"
echo
echo -e "${YELLOW}This is NOT RECOMMENDED unless you have a specific reason.${NC}"
echo
echo "To enable experimental mode later:"
echo " 1. sudo systemctl edit bluetooth"
echo " 2. Add these lines:"
echo " [Service]"
echo " ExecStart="
echo " ExecStart=/usr/lib/bluetooth/bluetoothd -E"
echo " 3. sudo systemctl daemon-reload"
echo " 4. sudo systemctl restart bluetooth"
echo
print_error "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo
else
# Enable experimental mode by default
print_info "Enabling BlueZ experimental mode (required for proper BLE connectivity)"
print_info "This enables LE-specific connection methods (ConnectDevice API)"
echo
# Find bluetoothd path
BLUETOOTHD_PATH=""
for path in /usr/lib/bluetooth/bluetoothd /usr/libexec/bluetooth/bluetoothd; do
if [ -f "$path" ]; then
BLUETOOTHD_PATH="$path"
break
fi
done
if [ -z "$BLUETOOTHD_PATH" ]; then
print_error "Could not find bluetoothd binary"
print_warning "Tried: /usr/lib/bluetooth/bluetoothd, /usr/libexec/bluetooth/bluetoothd"
echo
else
print_info "Using bluetoothd at: $BLUETOOTHD_PATH"
# Create systemd override
print_info "Creating systemd override for bluetooth service..."
# Use sudo only if not running as root
if [ "$EUID" -eq 0 ]; then
# Running as root - no sudo needed
mkdir -p /etc/systemd/system/bluetooth.service.d
cat > /etc/systemd/system/bluetooth.service.d/override.conf <<EOF
[Service]
ExecStart=
ExecStart=$BLUETOOTHD_PATH -E
EOF
systemctl daemon-reload
systemctl restart bluetooth
else
# Not root - use sudo
sudo mkdir -p /etc/systemd/system/bluetooth.service.d
sudo tee /etc/systemd/system/bluetooth.service.d/override.conf > /dev/null <<EOF
[Service]
ExecStart=
ExecStart=$BLUETOOTHD_PATH -E
EOF
sudo systemctl daemon-reload
sudo systemctl restart bluetooth
fi
# Verify bluetooth service is running
if systemctl is-active --quiet bluetooth; then
# Double-check that -E flag is actually set
if ps aux | grep bluetoothd | grep -q -- "-E"; then
print_success "BlueZ experimental mode enabled successfully"
else
print_warning "Bluetooth service restarted but -E flag not detected"
print_info "You may need to manually verify: ps aux | grep bluetoothd"
fi
else
print_error "Bluetooth service failed to start"
print_warning "Check status with: sudo systemctl status bluetooth"
echo
fi
echo
fi
fi
fi
fi
fi
# Step 6: Configuration
print_header "Configuration"

View file

@ -52,6 +52,7 @@ chmod +x install.sh
mkdir -p /tmp/test-config
# Run non-interactively (answer 'n' to bluetooth permissions prompt)
# Note: BlueZ experimental mode will be enabled by default (no prompt)
./install.sh --config /tmp/test-config <<EOF
n
EOF
@ -153,6 +154,49 @@ echo "Testing BLE interface import..."
cd /tmp/test-config/interfaces
python3 -c "import sys; sys.path.insert(0, '.'); from BLEInterface import BLEInterface; print(' ✓ BLEInterface imported successfully')" || { echo "FAIL: Cannot import BLEInterface"; exit 1; }
echo ""
# Check BlueZ experimental mode configuration
echo "Checking BlueZ experimental mode..."
if command -v systemctl &> /dev/null && command -v bluetoothctl &> /dev/null; then
# systemctl is available - check if experimental mode was configured
if [ -f /etc/systemd/system/bluetooth.service.d/override.conf ]; then
echo " ✓ Systemd override file created"
if grep -q -- "-E" /etc/systemd/system/bluetooth.service.d/override.conf; then
echo " ✓ Experimental mode flag (-E) configured"
else
echo " ⚠ WARNING: Override file exists but -E flag not found"
fi
else
# No override file - may have been already enabled or not supported
if systemctl status bluetooth 2>/dev/null | grep -q -- "-E\|--experimental"; then
echo " ✓ Experimental mode already enabled (not via installer)"
else
echo " ⚠ WARNING: Experimental mode not configured"
fi
fi
else
# systemctl or bluetoothctl not available (container environment)
echo " Systemd/BlueZ not available (container environment - OK)"
fi
echo ""
# Test --skip-experimental flag
echo "Testing --skip-experimental flag..."
cd "$(dirname "$0")/.."
# Run with --skip-experimental to verify it doesn't fail
./install.sh --config /tmp/test-config-skip --skip-experimental > /tmp/skip-test.log 2>&1 <<EOF
n
EOF
# Check that warning was shown
if grep -q "WARNING: Skipping BlueZ experimental mode" /tmp/skip-test.log; then
echo " ✓ --skip-experimental flag works (warning displayed)"
else
echo " ⚠ WARNING: --skip-experimental flag may not be working correctly"
fi
echo ""
echo "=== SUCCESS: All tests passed ==="
echo ""