/DotNet-BlueZ

A quick and dirty library for BlueZ's D-Bus APIs. Focus is on Bluetooth Low Energy APIs.

Primary LanguageC#Apache License 2.0Apache-2.0

DotNet-BlueZ

A quick and dirty library for BlueZ's D-Bus APIs. Primary focus is Bluetooth Low Energy.

.NET Build, Test, and Publish Nuget Package

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".

Requirements

  • 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 with bluetoothd -v.

Installation

dotnet add package vestervang.DotNetBlueZ

Enabling Experimental features

To use all the features of this library you'll have to enable experimental features.

Ubuntu

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

Events

C# events are available for several properties. Events are useful for properly handling disconnects and reconnects.

Usage

Get a Bluetooth adapter

using vestervang.DotNetBlueZ;
...

IAdapter1 adapter = (await BlueZManager.GetAdaptersAsync()).FirstOrDefault();

or get a particular adapter:

IAdapter1 adapter = await BlueZManager.GetAdapterAsync(adapterName: "hci0");

Scan for Bluetooth devices

adapter.DeviceFound += adapter_DeviceFoundAsync;

await adapter.StartDiscoveryAsync();
...
await adapter.StopDiscoveryAsync();

Get Devices

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();

Connect to a Device

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);

Retrieve a GATT Service and Characteristic

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);

Read a GATT Characteristic value

byte[] value = await characteristic.ReadValueAsync(timeout);

string modelName = Encoding.UTF8.GetString(value);

Subscribe to GATT Characteristic Notifications

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);
  }
}

Tips

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.

Contributing

See Contributing.

Reference