A quick and dirty library for BlueZ's D-Bus APIs. Primary focus is Bluetooth Low Energy.
Uses Tmds.DBus to access D-Bus. Tmds.DBus.Tool was used to generate the D-Bus object interfaces. D-Bus is the preferred interface for Bluetooth in userspace. The Doing Bluetooth Low Energy on Linux presentation says "Use D-Bus API (documentation in doc/) whenever possible".
- Linux
- A recent release of BlueZ. This package was tested with BlueZ 5.53 with the
-E
flag. You can check which version you're using withbluetoothd -v
.
dotnet add package vestervang.DotNetBlueZ
To use all the features of this library you'll have to enable experimental features.
run systemctl status bluetooth
this will give in information about the bluetooth process.
there should be a line saying something like this:
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled)
The /lib/systemd/system/bluetooth.service
is the path to your service file, we will have to edit this.
Run sudo vim /lib/systemd/system/bluetooth.service
Add -E
at the end of the ExecStart
line.
Now reboot and it should be enabled
C# events are available for several properties. Events are useful for properly handling disconnects and reconnects.
using vestervang.DotNetBlueZ;
...
IAdapter1 adapter = (await BlueZManager.GetAdaptersAsync()).FirstOrDefault();
or get a particular adapter:
IAdapter1 adapter = await BlueZManager.GetAdapterAsync(adapterName: "hci0");
adapter.DeviceFound += adapter_DeviceFoundAsync;
await adapter.StartDiscoveryAsync();
...
await adapter.StopDiscoveryAsync();
adapter.DeviceFound
(above) will be called immediately for existing devices, and as new devices show up during scanning; eventArgs.IsStateChange
can be used to distinguish between existing and new devices. Alternatively you can can use GetDevicesAsync
:
IReadOnlyList<Device> devices = await adapter.GetDevicesAsync();
device.Connected += device_ConnectedAsync;
device.Disconnected += device_DisconnectedAsync;
device.ServicesResolved += device_ServicesResolvedAsync;
await device.ConnectAsync();
Alternatively you can wait for "Connected" and "ServicesResolved" to equal true:
TimeSpan timeout = TimeSpan.FromSeconds(15);
await device.ConnectAsync();
await device.WaitForPropertyValueAsync("Connected", value: true, timeout);
await device.WaitForPropertyValueAsync("ServicesResolved", value: true, timeout);
Prerequisite: You must be connected to a device and services must be resolved. You may need to pair with the device in order to use some services.
Example using GATT Device Information Service UUIDs.
string serviceUUID = "0000180a-0000-1000-8000-00805f9b34fb";
string characteristicUUID = "00002a24-0000-1000-8000-00805f9b34fb";
IGattService1 service = await device.GetServiceAsync(serviceUUID);
IGattCharacteristic1 characteristic = await service.GetCharacteristicAsync(characteristicUUID);
byte[] value = await characteristic.ReadValueAsync(timeout);
string modelName = Encoding.UTF8.GetString(value);
characteristic.Value += characteristic_Value;
...
private static async Task characteristic_Value(GattCharacteristic characteristic, GattCharacteristicValueEventArgs e)
{
try
{
Console.WriteLine($"Characteristic value (hex): {BitConverter.ToString(e.Value)}");
Console.WriteLine($"Characteristic value (UTF-8): \"{Encoding.UTF8.GetString(e.Value)}\"");
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
}
}
It may be necessary to pair with a device for a GATT service to be visible or for reading GATT characteristics to work. To pair, one option is to run bluetoothctl
(or sudo bluetoothctl
)
and then run default agent
and agent on
within bluetoothctl
. Watch bluetoothctl
for pairing requests.
Running the following code with an unpaired device
var dict = new Dictionary<string, object>();
dict.Add("Address", DeviceToConnectTo);
dict.Add("AddressType", "random");
await adapter.ConnectDeviceAsync(dict);
might result in the following error:
org.freedesktop.DBus.Error.UnknownMethod: Method "ConnectDevice" with signature "a{sv}" on interface "org.bluez.Adapter1" doesn't exist
Connecting directly to a device that was obtained by discovery might succeed without pairing.
See Ubuntu's Introduction to Pairing.
See Contributing.