BluetoothGD
Cross-platform Bluetooth Classic pairing and connection for Godot 4.x.
(Godot 4.4 and 4.7 tested)
Built as a GDExtension, it lets you discover, pair, and manage Bluetooth devices (including gamepads) directly from GDScript — ideal for in-game controller pairing flows without sending players to OS settings.
Includes a reference demo UI (example/scan_demo.tscn) for quick testing and integration.
Demo available on the GitHub repository.
Supported Platforms
- Windows 10/11 (x86_64)
- Linux (x86_64)
Features
- Native device discovery using platform Bluetooth APIs
- Pair / unpair, connect / disconnect devices
- Real-time paired and connected state tracking
- Signal-based API — easy to bind to UI
- Non-blocking worker thread backend
- Reference demo with device list, scan toggle, pair/unpair, and event log
- Full GDScript API with detailed error reporting
Known Limitations
- On Windows,
disconnect_device()cannot force-disconnect Bluetooth HID gamepads (power off the controller or use OS settings). - On Linux, HID gamepads may auto-reconnect when powered on.
Requirements
- Godot 4.4+
- CMake 3.17+
- Platform-specific build tools (Visual Studio on Windows,
libdbus-1-dev+ BlueZ on Linux)
Quick Start
1. Enable the addon
Enable the plugin in Project → Project Settings → Plugins.
2. Add a BluetoothManager node
Add a BluetoothManager node to your scene as a child or autoload. Keep your UI on a separate Control node — do not attach your UI script to BluetoothManager itself.
YourScene (Control)
├── Bluetooth # BluetoothManager node
└── ... your UI ...
Alternatively, register it as an autoload named Bluetooth pointing at a scene that contains only a BluetoothManager node.
3. Connect signals and call methods
Wire up the API from your UI script:
extends Control
@onready var _bluetooth: BluetoothManager = $Bluetooth
func _ready() -> void:
_bluetooth.bluetooth_ready.connect(_on_bluetooth_ready)
_bluetooth.scan_started.connect(_on_scan_started)
_bluetooth.scan_stopped.connect(_on_scan_stopped)
_bluetooth.device_found.connect(_on_device_found)
_bluetooth.pairing_succeeded.connect(_on_pairing_succeeded)
_bluetooth.pairing_failed.connect(_on_pairing_failed)
_bluetooth.pairing_pin_requested.connect(_on_pairing_pin_requested)
_bluetooth.error_occurred.connect(_on_error_occurred)
func _on_bluetooth_ready() -> void:
# Backend is initialized — safe to scan and query paired devices.
var paired: Array = _bluetooth.get_paired_devices()
print("Bluetooth ready on %s" % _bluetooth.get_platform_name())
func _on_start_scan_pressed() -> void:
_bluetooth.start_scan({
"named_only": false,
"gamepads_only": true,
})
func _on_stop_scan_pressed() -> void:
_bluetooth.stop_scan()
func _on_scan_started() -> void:
print("Scan started")
func _on_scan_stopped() -> void:
print("Scan stopped")
func _on_device_found(device_info: Dictionary) -> void:
# device_info keys: address, name, paired, connected, device_class, device_id, rssi
print("Found: %s (%s)" % [device_info.get("name", "Unknown"), device_info.get("address", "")])
func _on_pair_pressed(address: String) -> void:
_bluetooth.pair_device(address)
func _on_unpair_pressed(address: String) -> void:
_bluetooth.unpair_device(address)
func _on_pairing_pin_requested(address: String) -> void:
_bluetooth.confirm_pairing("0000") # or reject_pairing() / cancel_pairing()
func _on_pairing_succeeded(address: String) -> void:
print("Paired: %s" % address)
func _on_pairing_failed(address: String, error: String, error_code: int) -> void:
print("Pairing failed: %s (%s)" % [error, _bluetooth.get_error_code_name(error_code)])
func _on_error_occurred(operation: String, message: String, error_code: int) -> void:
print("Error [%s]: %s" % [operation, message])
Important: Wait for the bluetooth_ready signal before calling start_scan(). Scan and pairing results are delivered asynchronously via signals on the main thread.
4. Run the included example
Open and run addons/bluetooth_gd/example/scan_demo.tscn to see scanning, device listing, and pair/unpair in action.
Changelog for version v0.2
No changelog provided for this version.