Merge pull request #9 from torlando-tech/fix/issue-3-bluez-experimental-mode
Fix/issue 3 bluez experimental mode
This commit is contained in:
commit
c1108f159a
7 changed files with 172 additions and 18 deletions
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
|
@ -10,6 +10,7 @@ jobs:
|
||||||
unit-tests:
|
unit-tests:
|
||||||
name: Unit Tests
|
name: Unit Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: false # Temporarily disabled to save CI minutes while troubleshooting installer tests
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -61,6 +62,7 @@ jobs:
|
||||||
integration-tests:
|
integration-tests:
|
||||||
name: Integration Tests
|
name: Integration Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: false # Temporarily disabled to save CI minutes while troubleshooting installer tests
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
|
|
||||||
44
README.md
44
README.md
|
|
@ -250,6 +250,50 @@ These errors occur when BlueZ attempts Classic Bluetooth (BR/EDR) connections in
|
||||||
**Solution:**
|
**Solution:**
|
||||||
Enable BlueZ experimental mode (see Installation → Manual Installation → step 4). If you used the automated installer, re-run it without `--skip-experimental`.
|
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.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
The BLE interface consists of four main components:
|
The BLE interface consists of four main components:
|
||||||
|
|
|
||||||
12
examples/bluetooth-power-on.service
Normal file
12
examples/bluetooth-power-on.service
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Ensure Bluetooth adapter is powered on at boot
|
||||||
|
After=bluetooth.service
|
||||||
|
Requires=bluetooth.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/bin/sh -c 'echo -e "power on\nquit" | /usr/bin/bluetoothctl'
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
@ -266,6 +266,15 @@ power_mode = balanced
|
||||||
# - If you used install.sh, it should have enabled this automatically
|
# - If you used install.sh, it should have enabled this automatically
|
||||||
# - Verify: ps aux | grep bluetoothd (should show -E flag)
|
# - Verify: ps aux | grep bluetoothd (should show -E flag)
|
||||||
#
|
#
|
||||||
|
# 7. Bluetooth adapter not powered (No powered Bluetooth adapters found):
|
||||||
|
# IMPORTANT: BLE interface won't work if adapter is powered off!
|
||||||
|
# - Symptoms: "Not Powered" errors, interface fails to start, no peer discovery
|
||||||
|
# - Solution: bluetoothctl power on
|
||||||
|
# - Verify: bluetoothctl show (should display "Powered: yes")
|
||||||
|
# - For automatic power-on at boot: see examples/bluetooth-power-on.service
|
||||||
|
# - If blocked: sudo rfkill unblock bluetooth
|
||||||
|
# - The installer (v1.x+) automatically powers on the adapter
|
||||||
|
#
|
||||||
# For more troubleshooting, see README.md
|
# For more troubleshooting, see README.md
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
|
||||||
100
install.sh
100
install.sh
|
|
@ -427,7 +427,11 @@ else
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v setcap &> /dev/null; then
|
# Skip setcap when running as root (e.g., in containers) - root already has all permissions
|
||||||
|
if [ "$EUID" -eq 0 ]; then
|
||||||
|
print_info "Running as root - skipping capability grant (not needed)"
|
||||||
|
print_info "Root user already has all required Bluetooth permissions"
|
||||||
|
elif command -v setcap &> /dev/null; then
|
||||||
# Get python3 path
|
# Get python3 path
|
||||||
PYTHON_PATH=$(which python3)
|
PYTHON_PATH=$(which python3)
|
||||||
print_info "Detected Python at: $PYTHON_PATH"
|
print_info "Detected Python at: $PYTHON_PATH"
|
||||||
|
|
@ -583,8 +587,9 @@ else
|
||||||
ExecStart=
|
ExecStart=
|
||||||
ExecStart=$BLUETOOTHD_PATH -E
|
ExecStart=$BLUETOOTHD_PATH -E
|
||||||
EOF
|
EOF
|
||||||
systemctl daemon-reload
|
# Non-fatal in container/CI environments where systemd isn't running
|
||||||
systemctl restart bluetooth
|
systemctl daemon-reload 2>/dev/null || true
|
||||||
|
systemctl restart bluetooth 2>/dev/null || true
|
||||||
else
|
else
|
||||||
# Not root - use sudo
|
# Not root - use sudo
|
||||||
sudo mkdir -p /etc/systemd/system/bluetooth.service.d
|
sudo mkdir -p /etc/systemd/system/bluetooth.service.d
|
||||||
|
|
@ -593,12 +598,13 @@ EOF
|
||||||
ExecStart=
|
ExecStart=
|
||||||
ExecStart=$BLUETOOTHD_PATH -E
|
ExecStart=$BLUETOOTHD_PATH -E
|
||||||
EOF
|
EOF
|
||||||
sudo systemctl daemon-reload
|
# Non-fatal in container/CI environments where systemd isn't running
|
||||||
sudo systemctl restart bluetooth
|
sudo systemctl daemon-reload 2>/dev/null || true
|
||||||
|
sudo systemctl restart bluetooth 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify bluetooth service is running
|
# Verify bluetooth service is running (skip in container environments)
|
||||||
if systemctl is-active --quiet bluetooth; then
|
if systemctl is-active --quiet bluetooth 2>/dev/null; then
|
||||||
# Double-check that -E flag is actually set
|
# Double-check that -E flag is actually set
|
||||||
if ps aux | grep bluetoothd | grep -q -- "-E"; then
|
if ps aux | grep bluetoothd | grep -q -- "-E"; then
|
||||||
print_success "BlueZ experimental mode enabled successfully"
|
print_success "BlueZ experimental mode enabled successfully"
|
||||||
|
|
@ -606,10 +612,14 @@ EOF
|
||||||
print_warning "Bluetooth service restarted but -E flag not detected"
|
print_warning "Bluetooth service restarted but -E flag not detected"
|
||||||
print_info "You may need to manually verify: ps aux | grep bluetoothd"
|
print_info "You may need to manually verify: ps aux | grep bluetoothd"
|
||||||
fi
|
fi
|
||||||
else
|
elif command -v systemctl &> /dev/null && [ ! -f /.dockerenv ]; then
|
||||||
|
# Only show error if systemctl exists and we're not in a container
|
||||||
print_error "Bluetooth service failed to start"
|
print_error "Bluetooth service failed to start"
|
||||||
print_warning "Check status with: sudo systemctl status bluetooth"
|
print_warning "Check status with: sudo systemctl status bluetooth"
|
||||||
echo
|
echo
|
||||||
|
else
|
||||||
|
# Container environment or systemd not available
|
||||||
|
print_info "Systemd override created (service restart skipped in container environment)"
|
||||||
fi
|
fi
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
@ -618,6 +628,68 @@ EOF
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Step 5B: Bluetooth Adapter Power State
|
||||||
|
print_header "Bluetooth Adapter Power State"
|
||||||
|
|
||||||
|
if command -v bluetoothctl &> /dev/null; then
|
||||||
|
print_info "Checking Bluetooth adapter power state..."
|
||||||
|
|
||||||
|
# Check for rfkill blocks first (must be unblocked before power-on works)
|
||||||
|
if command -v rfkill &> /dev/null; then
|
||||||
|
if rfkill list bluetooth | grep -q "Soft blocked: yes"; then
|
||||||
|
print_warning "Bluetooth adapter is soft-blocked by rfkill"
|
||||||
|
print_info "Unblocking Bluetooth adapter..."
|
||||||
|
# Use sudo only if not running as root (Docker containers run as root without sudo)
|
||||||
|
if [ "$EUID" -eq 0 ]; then
|
||||||
|
rfkill unblock bluetooth
|
||||||
|
else
|
||||||
|
sudo rfkill unblock bluetooth
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Verify unblock succeeded
|
||||||
|
if rfkill list bluetooth | grep -q "Soft blocked: yes"; then
|
||||||
|
print_error "Failed to unblock Bluetooth adapter"
|
||||||
|
print_warning "You may need to check hardware switch or BIOS settings"
|
||||||
|
else
|
||||||
|
print_success "Bluetooth adapter unblocked successfully"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if adapter is powered
|
||||||
|
if bluetoothctl show 2>/dev/null | grep -q "Powered: yes"; then
|
||||||
|
print_success "Bluetooth adapter is powered on"
|
||||||
|
else
|
||||||
|
print_warning "Bluetooth adapter is not powered"
|
||||||
|
print_info "Powering on Bluetooth adapter..."
|
||||||
|
|
||||||
|
# Power on the adapter (non-fatal in container/CI environments where D-Bus may not be running)
|
||||||
|
echo -e "power on\nquit" | bluetoothctl > /dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Verify it worked
|
||||||
|
sleep 1
|
||||||
|
if bluetoothctl show 2>/dev/null | grep -q "Powered: yes"; then
|
||||||
|
print_success "Bluetooth adapter powered on successfully"
|
||||||
|
else
|
||||||
|
print_error "Failed to power on Bluetooth adapter"
|
||||||
|
echo
|
||||||
|
print_info "Troubleshooting steps:"
|
||||||
|
echo " 1. Check if adapter is blocked: sudo rfkill list bluetooth"
|
||||||
|
echo " 2. Unblock if needed: sudo rfkill unblock bluetooth"
|
||||||
|
echo " 3. Try manually: bluetoothctl power on"
|
||||||
|
echo " 4. Verify adapter exists: bluetoothctl list"
|
||||||
|
echo
|
||||||
|
print_warning "BLE interface may not work until adapter is powered on"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_warning "bluetoothctl not available, cannot check adapter power state"
|
||||||
|
print_info "Ensure Bluetooth adapter is powered on before running rnsd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
|
||||||
# Step 6: Configuration
|
# Step 6: Configuration
|
||||||
print_header "Configuration"
|
print_header "Configuration"
|
||||||
|
|
||||||
|
|
@ -630,13 +702,13 @@ echo " File: $CONFIG_FILE"
|
||||||
echo
|
echo
|
||||||
echo " Add this section (copy-paste ready):"
|
echo " Add this section (copy-paste ready):"
|
||||||
echo
|
echo
|
||||||
echo " [[BLE Interface]]"
|
echo " [[BLE Interface]]"
|
||||||
echo " type = BLEInterface"
|
echo " type = BLEInterface"
|
||||||
echo " enabled = yes"
|
echo " enabled = yes"
|
||||||
echo
|
echo
|
||||||
echo " # Enable both modes for mesh"
|
echo " # Enable both modes for mesh"
|
||||||
echo " enable_peripheral = yes"
|
echo " enable_peripheral = yes"
|
||||||
echo " enable_central = yes"
|
echo " enable_central = yes"
|
||||||
echo
|
echo
|
||||||
echo "2. See examples/config_example.toml for all configuration options"
|
echo "2. See examples/config_example.toml for all configuration options"
|
||||||
echo
|
echo
|
||||||
|
|
|
||||||
|
|
@ -862,9 +862,23 @@ class BLEInterface(Interface):
|
||||||
RNS.log(f"{self} scanning for peers (scan_time={scan_time:.1f}s)...", RNS.LOG_EXTREME)
|
RNS.log(f"{self} scanning for peers (scan_time={scan_time:.1f}s)...", RNS.LOG_EXTREME)
|
||||||
|
|
||||||
scanner = BleakScanner(detection_callback=detection_callback)
|
scanner = BleakScanner(detection_callback=detection_callback)
|
||||||
await scanner.start()
|
try:
|
||||||
await asyncio.sleep(scan_time)
|
await scanner.start()
|
||||||
await scanner.stop()
|
await asyncio.sleep(scan_time)
|
||||||
|
await scanner.stop()
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = str(e)
|
||||||
|
# Check for "Not Powered" or similar adapter power issues
|
||||||
|
if "No powered Bluetooth adapters" in error_msg or "Not Powered" in error_msg:
|
||||||
|
RNS.log(f"{self} Bluetooth adapter is not powered!", RNS.LOG_ERROR)
|
||||||
|
RNS.log(f"{self} Solution: Run 'bluetoothctl power on' or 'sudo rfkill unblock bluetooth'", RNS.LOG_ERROR)
|
||||||
|
RNS.log(f"{self} See troubleshooting: https://github.com/torlando-tech/ble-reticulum#bluetooth-adapter-not-powered", RNS.LOG_ERROR)
|
||||||
|
# Don't raise, just return - the discovery loop will retry
|
||||||
|
self.scanning = False
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# Re-raise other errors
|
||||||
|
raise
|
||||||
|
|
||||||
# Get local adapter address if we don't have it yet (for connection direction preference)
|
# Get local adapter address if we don't have it yet (for connection direction preference)
|
||||||
if self.local_address is None:
|
if self.local_address is None:
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ check_package() {
|
||||||
echo "Running install.sh (self-contained installer)..."
|
echo "Running install.sh (self-contained installer)..."
|
||||||
# Navigate to repository root (script is in tests/ directory)
|
# Navigate to repository root (script is in tests/ directory)
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
REPO_ROOT="$(pwd)"
|
||||||
chmod +x install.sh
|
chmod +x install.sh
|
||||||
mkdir -p /tmp/test-config
|
mkdir -p /tmp/test-config
|
||||||
|
|
||||||
|
|
@ -184,7 +185,7 @@ echo ""
|
||||||
|
|
||||||
# Test --skip-experimental flag
|
# Test --skip-experimental flag
|
||||||
echo "Testing --skip-experimental flag..."
|
echo "Testing --skip-experimental flag..."
|
||||||
cd "$(dirname "$0")/.."
|
cd "$REPO_ROOT"
|
||||||
# Run with --skip-experimental to verify it doesn't fail
|
# 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
|
./install.sh --config /tmp/test-config-skip --skip-experimental > /tmp/skip-test.log 2>&1 <<EOF
|
||||||
n
|
n
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue