
<img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/Fantety/GDBLE/release.yml">
<img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/Fantety/GDBLE">
<img alt="GitHub language count" src="https://img.shields.io/github/languages/count/Fantety/GDBLE">
<img alt="GitHub License" src="https://img.shields.io/github/license/Fantety/GDBLE">
A modern Bluetooth Low Energy (BLE) plugin for Godot 4
<a href="https://github.com/Fantety/GDBLE/raw/v0.5.5/README.md">🇨🇳 中文</a> |
<a href="https://github.com/Fantety/GDBLE/raw/v0.5.5/README_EN.md">🇺🇸 English</a>
GDBLE is a Bluetooth Low Energy (BLE) plugin designed for Godot 4, built with Rust and GDExtension. Based on the btleplug library, it provides complete BLE functionality including device scanning, connection, service discovery, characteristic read/write, and notification subscription.
Plugin Page: GDBLE - Godot Asset Library
| Platform | Architecture | Status | Version Required |
|---|---|---|---|
| Windows | x86_64 | ✅ Tested | Windows 10+ |
| macOS | x86_64 | ✅ Supported | macOS 10.15+ |
| macOS | ARM64 (M1/M2) | ✅ Supported | macOS 11+ |
| Linux | x86_64 | ✅ Supported | Ubuntu 20.04+ |
| Android | ARM64 | ⚠️ Not tested | Android 5.0+ (API 21+) |
| Android | x86_64 | ⚠️ Not tested | Android 5.0+ (API 21+) |
⚠️ Note: Android ARMv7 (32-bit) architecture is not supported. Please use ARM64 or x86_64 architectures.
⚠️ Important: Android builds compile successfully but have not been tested on actual devices. Please test thoroughly before production use.
Starting from v0.5.3, GDBLE uses a channel-based event system to ensure thread safety:
mpsc::UnboundedSender from background threadsBluetoothManager processes events on the main thread via process() callbackThis resolves the panic/hang issues caused by calling Godot API from background threads in previous versions (Issue #11, #14).
addons folderyour_project/
├── addons/
│ └── gdble/
│ ├── gdble.gdextension
│ └── gdble.dll (Windows) or libgdble.dylib (macOS)
extends Node
var bluetooth_manager: BluetoothManager
func _ready():
# 1. Create BluetoothManager instance
bluetooth_manager = BluetoothManager.new()
add_child(bluetooth_manager)
# 2. Connect signals
bluetooth_manager.adapter_initialized.connect(_on_adapter_initialized)
bluetooth_manager.device_discovered.connect(_on_device_discovered)
bluetooth_manager.scan_stopped.connect(_on_scan_stopped)
# 3. Initialize Bluetooth adapter
bluetooth_manager.initialize()
func _on_adapter_initialized(success: bool, error: String):
if success:
print("Bluetooth initialized successfully")
# Start scanning for 10 seconds
bluetooth_manager.start_scan(10.0)
else:
print("Bluetooth initialization failed: ", error)
func _on_device_discovered(device_info: Dictionary):
print("Device found: ", device_info.get("name", "Unknown"))
print(" Address: ", device_info.get("address"))
print(" Signal strength: ", device_info.get("rssi"), " dBm")
func _on_scan_stopped():
print("Scan complete")
var devices = bluetooth_manager.get_discovered_devices()
print("Total devices found: ", devices.size())
var connected_device: BleDevice = null
func connect_to_device(address: String):
# Connect to device
connected_device = bluetooth_manager.connect_device(address)
if connected_device:
# Connect device signals
connected_device.connected.connect(_on_device_connected)
connected_device.services_discovered.connect(_on_services_discovered)
connected_device.characteristic_written.connect(_on_characteristic_written)
# Start connection
connected_device.connect_async()
func _on_device_connected():
print("Device connected")
# Discover services
connected_device.discover_services()
func _on_services_discovered(services: Array):
print("Discovered ", services.size(), " services")
# Iterate through services and characteristics
for service in services:
var service_uuid = service.get("uuid")
var characteristics = service.get("characteristics", [])
for characteristic in characteristics:
var char_uuid = characteristic.get("uuid")
var properties = characteristic.get("properties", {})
# If characteristic supports write, write data
if properties.get("write", false):
var data = "Hello BLE".to_utf8_buffer()
connected_device.write_characteristic(service_uuid, char_uuid, data, false)
func _on_characteristic_written(char_uuid: String):
print("Data written successfully: ", char_uuid)
Bluetooth manager responsible for adapter initialization, device scanning, and connection management.
| Method | Parameters | Return | Description |
|---|---|---|---|
initialize() |
None | void | Initialize Bluetooth adapter |
is_initialized() |
None | bool | Check if adapter is initialized |
start_scan(timeout_seconds) |
float | void | Start scanning for devices |
stop_scan() |
None | void | Stop scanning |
get_discovered_devices() |
None | Array[Dictionary] | Get list of discovered devices |
connect_device(address) |
String | BleDevice | Connect to specified device |
disconnect_device(address) |
String | void | Disconnect specified device |
get_device(address) |
String | BleDevice | Get connected device instance |
get_connected_devices() |
None | Array[BleDevice] | Get all connected devices |
set_debug_mode(enabled) |
bool | void | Enable/disable debug mode |
is_debug_mode() |
None | bool | Check debug mode status |
| Signal | Parameters | Description |
|---|---|---|
adapter_initialized |
success: bool, error: String | Adapter initialization complete |
device_discovered |
device_info: Dictionary | New device discovered |
device_updated |
device_info: Dictionary | Device information updated |
scan_started |
None | Scan started |
scan_stopped |
None | Scan stopped |
device_connecting |
address: String | Device connection initiated |
device_connected |
address: String | Device connected successfully |
device_disconnected |
address: String | Device disconnected |
error_occurred |
error_message: String | Error occurred |
Represents a single BLE device, provides connection, service discovery, and data read/write functionality.
| Method | Parameters | Return | Description |
|---|---|---|---|
connect_async() |
None | void | Asynchronously connect to device |
disconnect() |
None | void | Disconnect from device |
is_connected() |
None | bool | Check if connected |
get_address() |
None | String | Get device address |
get_name() |
None | String | Get device name |
discover_services() |
None | void | Discover device services |
get_services() |
None | Array[Dictionary] | Get discovered services list |
read_characteristic(service_uuid, char_uuid) |
String, String | void | Read characteristic value |
write_characteristic(service_uuid, char_uuid, data, with_response) |
String, String, PackedByteArray, bool | void | Write characteristic value |
subscribe_characteristic(service_uuid, char_uuid) |
String, String | void | Subscribe to characteristic notifications |
unsubscribe_characteristic(service_uuid, char_uuid) |
String, String | void | Unsubscribe from notifications |
| Signal | Parameters | Description |
|---|---|---|
connected |
None | Device connected successfully |
disconnected |
None | Device disconnected |
connection_failed |
error: String | Connection failed |
services_discovered |
services: Array | Service discovery complete |
characteristic_read |
char_uuid: String, data: PackedByteArray | Characteristic read complete |
characteristic_written |
char_uuid: String | Characteristic write complete |
characteristic_notified |
char_uuid: String, data: PackedByteArray | Characteristic notification received |
operation_failed |
operation: String, error: String | Operation fail |
$ claude mcp add GDBLE \
-- python -m otcore.mcp_server <graph>