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>
This commit is contained in:
parent
e7ee42ddc5
commit
a0f361a3f4
5 changed files with 121 additions and 3 deletions
44
README.md
44
README.md
|
|
@ -250,6 +250,50 @@ These errors occur when BlueZ attempts Classic Bluetooth (BR/EDR) connections in
|
|||
**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.
|
||||
|
||||
## Architecture
|
||||
|
||||
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
|
||||
# - 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
|
||||
|
||||
# ============================================================================
|
||||
|
|
|
|||
39
install.sh
39
install.sh
|
|
@ -618,6 +618,45 @@ EOF
|
|||
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 if adapter is powered
|
||||
if bluetoothctl show | 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
|
||||
echo -e "power on\nquit" | bluetoothctl > /dev/null 2>&1
|
||||
|
||||
# Verify it worked
|
||||
sleep 1
|
||||
if bluetoothctl show | 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
|
||||
print_header "Configuration"
|
||||
|
||||
|
|
|
|||
|
|
@ -862,9 +862,23 @@ class BLEInterface(Interface):
|
|||
RNS.log(f"{self} scanning for peers (scan_time={scan_time:.1f}s)...", RNS.LOG_EXTREME)
|
||||
|
||||
scanner = BleakScanner(detection_callback=detection_callback)
|
||||
await scanner.start()
|
||||
await asyncio.sleep(scan_time)
|
||||
await scanner.stop()
|
||||
try:
|
||||
await scanner.start()
|
||||
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)
|
||||
if self.local_address is None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue