DSoft.System.BluetoothLe 4.0.2609.21

dotnet add package DSoft.System.BluetoothLe --version 4.0.2609.21
                    
NuGet\Install-Package DSoft.System.BluetoothLe -Version 4.0.2609.21
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DSoft.System.BluetoothLe" Version="4.0.2609.21" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DSoft.System.BluetoothLe" Version="4.0.2609.21" />
                    
Directory.Packages.props
<PackageReference Include="DSoft.System.BluetoothLe" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DSoft.System.BluetoothLe --version 4.0.2609.21
                    
#r "nuget: DSoft.System.BluetoothLe, 4.0.2609.21"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DSoft.System.BluetoothLe@4.0.2609.21
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DSoft.System.BluetoothLe&version=4.0.2609.21
                    
Install as a Cake Addin
#tool nuget:?package=DSoft.System.BluetoothLe&version=4.0.2609.21
                    
Install as a Cake Tool

DSoft.System.BluetoothLe

DSoft.System.BluetoothLe is a cross-platform Bluetooth Low Energy library for modern .NET. It provides one API for scanning, connecting, discovering GATT services/characteristics, reading, writing, and receiving characteristic updates across mobile and desktop targets.

The library started as a fork/repackage of Plugin.BLE and has been migrated from Xamarin targets to .NET platform targets.

Supported Targets

Target Minimum OS
net10.0-android Android API 21
net10.0-ios iOS 15.0
net10.0-maccatalyst Mac Catalyst 15.0
net10.0-macos macOS 15.0
net10.0-tvos tvOS 15.0
net10.0-windows10.0.19041.0 Windows 10 1809
net481 Windows 10 1809
net10.0 / netstandard2.0 API surface only; platform Bluetooth calls throw on unsupported platforms

Install

Reference the package from your app project:

<PackageReference Include="DSoft.System.BluetoothLe" Version="4.0.*" />

When working from source, reference the project:

<ProjectReference Include="..\DSoft.System.BluetoothLe\DSoft.System.BluetoothLe.csproj" />

Use the library from the System.BluetoothLe namespace:

using System.BluetoothLe;
using System.BluetoothLe.EventArgs;

Platform Setup

Your app must request the operating-system permissions needed for Bluetooth. The library does not replace runtime permission prompts or app manifest entries.

Android

Add Bluetooth permissions to your Android manifest. For Android 12/API 31 and later, apps normally need BLUETOOTH_SCAN and BLUETOOTH_CONNECT. Older Android versions commonly require BLUETOOTH, BLUETOOTH_ADMIN, and location permission for scanning.

Example manifest entries:

<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />

Request dangerous permissions at runtime before scanning or connecting.

iOS, macOS, Mac Catalyst, tvOS

Add Bluetooth usage descriptions to your app's Info.plist where required by the platform:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to connect to nearby BLE devices.</string>

For iOS background BLE scenarios, also configure the appropriate background modes in your app.

Windows

Windows support uses the Windows Runtime Bluetooth APIs. The .NET for Windows and .NET Framework implementations share the same implementation and require Windows 10 1809 or later with a Bluetooth LE-capable adapter.

Packaged Windows apps should declare the Bluetooth capability in the app manifest. Desktop WPF apps should still handle unavailable Bluetooth hardware/radio states at runtime.

Quick Start

Get the current platform implementation:

var bluetooth = BluetoothLE.Current;

// Await readiness rather than testing IsOn. A radio that has only just been brought up has not settled and
// reports BluetoothState.Unknown, so an immediate IsOn check fails against a perfectly good adapter.
var state = await bluetooth.WaitForStateAsync(BluetoothState.On, cancellationToken);

var adapter = bluetooth.Adapter;

If you would rather report the real state to the user than wait for a particular one, await WaitForAvailabilityAsync(), which completes as soon as the radio settles into any determinate state - including Off and Unauthorized.

Scan for devices:

var adapter = BluetoothLE.Current.Adapter;

adapter.DeviceDiscovered += (sender, args) =>
{
    Console.WriteLine($"Found {args.Device.NameOrId} ({args.Device.Id}) RSSI {args.Device.Rssi}");
};

adapter.ScanTimeout = 10000; // milliseconds
adapter.ScanMode = ScanMode.LowLatency;

await adapter.StartScanningForDevicesAsync(cancellationToken: cancellationToken);

Starting a scan while one is already running throws InvalidOperationException, and a scan the platform refuses to start (radio off, permission denied, unsupported) throws AdapterScanException carrying a ScanFailureReason rather than completing empty after the full timeout.

Scan for devices that advertise a service:

var heartRateService = Guid.Parse("0000180d-0000-1000-8000-00805f9b34fb");

await adapter.StartScanningForDevicesAsync(
    serviceUuids: new[] { heartRateService },
    deviceFilter: device => !string.IsNullOrWhiteSpace(device.Name),
    allowDuplicatesKey: false,
    cancellationToken: cancellationToken);

Connect to a discovered device:

var device = adapter.DiscoveredDevices.FirstOrDefault();

if (device == null)
{
    throw new InvalidOperationException("No BLE device was discovered.");
}

await adapter.ConnectToDeviceAsync(device);

Connect to a known device by id:

var knownDeviceId = Guid.Parse("00000000-0000-0000-0000-000000000000");
var device = await adapter.ConnectToKnownDeviceAsync(knownDeviceId);

Discover services and characteristics:

var services = await device.GetServicesAsync();

foreach (var service in services)
{
    Console.WriteLine($"{service.Name}: {service.Id}");

    var characteristics = await service.GetCharacteristicsAsync();

    foreach (var characteristic in characteristics)
    {
        Console.WriteLine($"  {characteristic.Name}: {characteristic.Id} ({characteristic.Properties})");
    }
}

Read and write a characteristic:

var serviceId = Guid.Parse("0000180d-0000-1000-8000-00805f9b34fb");
var characteristicId = Guid.Parse("00002a37-0000-1000-8000-00805f9b34fb");

var service = await device.GetServiceAsync(serviceId);
var characteristic = await service.GetCharacteristicAsync(characteristicId);

if (characteristic.CanRead)
{
    byte[] value = await characteristic.ReadAsync();
}

if (characteristic.CanWrite)
{
    await characteristic.WriteAsync(new byte[] { 0x01, 0x02, 0x03 });
}

Subscribe to characteristic updates:

characteristic.ValueUpdated += (sender, args) =>
{
    var value = args.Characteristic.Value;
    Console.WriteLine(BitConverter.ToString(value));
};

await characteristic.StartUpdatesAsync();

// Later:
await characteristic.StopUpdatesAsync();

Disconnect:

await adapter.DisconnectDeviceAsync(device);

Useful API Surface

  • BluetoothLE.Current: singleton entry point for the current platform.
  • BluetoothLE.State, IsAvailable, IsOn: current Bluetooth state.
  • BluetoothLE.StateChanged: Bluetooth state notifications.
  • Adapter.StartScanningForDevicesAsync: scan for BLE devices.
  • Adapter.DeviceDiscovered: raised the first time a device is discovered during a scan.
  • Adapter.DeviceAdvertised: raised for matching advertisements.
  • Adapter.ConnectToDeviceAsync: connect to a discovered device.
  • Adapter.ConnectToKnownDeviceAsync: connect directly by known platform device id.
  • Device.GetServicesAsync: discover GATT services.
  • Service.GetCharacteristicsAsync: discover GATT characteristics.
  • Characteristic.ReadAsync, WriteAsync, StartUpdatesAsync, StopUpdatesAsync: interact with characteristic values.
  • Descriptor.ReadAsync, WriteAsync: interact with descriptors.

Notes

  • BLE device identifiers are platform-specific. Persist known device ids only for the same platform/device context.
  • Scanning and connecting require OS permissions and Bluetooth hardware. Always handle BluetoothState.Unavailable and BluetoothState.Off.
  • net10.0 and netstandard2.0 builds keep the shared API available, but platform Bluetooth operations require a supported platform target.
  • Some Android APIs used by the implementation are marked obsolete by newer SDK analyzers (CA1422). Migrating to the API 33 GATT overloads rewrites the notification delivery path, so it is deliberately deferred until it can be tested against real hardware; the behaviour is correct today.
  • Every event this library raises is raised on a native callback thread - the Android GATT callback thread or the CoreBluetooth delegate queue - and is not marshalled to your UI thread. Marshal in your handler.
  • Trace.TraceImplementation receives every diagnostic message the library emits. It writes to System.Diagnostics.Trace by default; assign your own delegate to bridge it to your logging framework, or set it to null to silence the library.

Building From Source

Restore and build the active solution with the .NET 10 SDK:

dotnet restore DSoft.System.BluetoothLe.slnx
dotnet build DSoft.System.BluetoothLe.slnx --no-restore

net10.0-windows10.0.19041.0 and net481 are only built when the host is Windows; on macOS and Linux the remaining seven target frameworks build and the two Windows ones are skipped.

Strong naming

The assembly is strong-named with DSoft.snk, which is committed to this repository on purpose. A strong name is an identity, not a signature: it lets the runtime tell this assembly apart from another of the same name, and it is what net481 binding needs. It is not a guarantee of origin, and publishing the key does not weaken anything that was ever guaranteed. Verify the package through NuGet, not through the strong name.

Relationship To Plugin.BLE

This project keeps the broad shape of Plugin.BLE but uses:

  • Namespace: System.BluetoothLe
  • Entry point: BluetoothLE.Current
  • Multi-targeted partial classes instead of the original base-class layout

Existing Plugin.BLE concepts map closely to this library, but code should be updated to the names above.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-android36.0 is compatible.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-ios26.0 is compatible.  net10.0-maccatalyst was computed.  net10.0-maccatalyst26.0 is compatible.  net10.0-macos was computed.  net10.0-macos26.0 is compatible.  net10.0-tvos was computed.  net10.0-tvos26.0 is compatible.  net10.0-windows was computed.  net10.0-windows10.0.19041 is compatible. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.8.1

  • .NETStandard 2.0

    • No dependencies.
  • net10.0

    • No dependencies.
  • net10.0-android36.0

    • No dependencies.
  • net10.0-ios26.0

    • No dependencies.
  • net10.0-maccatalyst26.0

    • No dependencies.
  • net10.0-macos26.0

    • No dependencies.
  • net10.0-tvos26.0

    • No dependencies.
  • net10.0-windows10.0.19041

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.2609.21 73 9/2/2026
3.0.2607.92 411 7/9/2026
2.0.2110.291 2,893 10/30/2021
2.0.2109.301 586 9/30/2021
2.0.2108.171 600 8/17/2021
2.0.2107.141 618 7/14/2021
2.0.2107.132 601 7/13/2021
2.0.2107.131 582 7/13/2021
2.0.2106.104 708 6/15/2021
2.0.2010.51-prerelease 648 10/5/2020
2.0.2010.21-prerelease 614 10/2/2020
2.0.2009.301-prerelease 545 9/30/2020
2.0.2006.222-prerelease 575 6/22/2020
2.0.2006.191-prerelease 626 6/19/2020
2.0.2006.181-prerelease 602 6/18/2020
2.0.2006.152-prerelease 635 6/15/2020
1.0.2006.52 833 6/5/2020

4.0 - modernisation release. Contains breaking changes.

           This release corrects defects carried over from the Xamarin port and reshapes the public API.

           Fixed: a failed scan start no longer wedges the adapter into a permanent "already scanning" state;
           connection failures are no longer lost as unhandled async void exceptions; CancellationToken is now
           honoured throughout - service discovery, characteristic and descriptor reads and writes, notifications,
           MTU and RSSI - where it was previously accepted and discarded; on Android, a pending read or write is
           now cancelled when the link drops for any reason, a characteristic read is no longer completed by an
           unrelated notification, and a BluetoothGattCallback is no longer allocated for every advertisement
           received; malformed advertisements no longer crash the process from inside a native scan callback;
           built-in tracing, which had never been wired up on any platform, now works.

           Changed deliberately: starting a scan while one is running throws InvalidOperationException instead of
           silently doing nothing, and StopScanningForDevicesAsync now genuinely waits for the scan to stop; a scan
           that cannot start (adapter off, permission denied, unsupported) faults with AdapterScanException and
           raises ScanFailed instead of completing empty after the full timeout; devices arriving in the tail of a
           stopping scan are no longer reported; exceptions thrown by consumer event handlers are logged and
           swallowed instead of terminating the app; task continuations now resume on the thread pool rather than
           inline on the CoreBluetooth or Android GATT callback thread; Characteristic.WriteAsync returns Task and
           throws CharacteristicWriteException instead of returning an unexplained false on two platforms and
           throwing on the third; ConnectToKnownDeviceAsync reuses an already-discovered device instead of minting
           a duplicate (on Android, a duplicate BluetoothGatt); Device.Dispose() no longer starts an unawaited
           disconnect - disconnect explicitly first; on Android, 32-bit service-data advertisement records are no
           longer byte-reversed and incomplete 32-bit UUID lists now are, and a denied BLUETOOTH_SCAN /
           BLUETOOTH_CONNECT permission is reported as BluetoothState.Unauthorized; Device.State on iOS, macOS,
           Mac Catalyst and tvOS reports the new DeviceState.Disconnecting while a disconnect is in progress,
           where it previously reported Disconnected - Android and Windows still move straight to Disconnected,
           so test for Connected rather than for Disconnected if you need portable behaviour.

           Removed: BluetoothLE.UseOldFindCharacteristicMode, Adapter.ConnectedDeviceRegistry (use ConnectedDevices
           / TryGetConnectedDevice), the fork's PlatformNotSupportedException (the BCL type is thrown instead),
           BleCommandQueue, LEStream, the unreachable Android API-18 scan path, and several unused parameterless
           constructors.

           Added: BluetoothLE.WaitForStateAsync - await Bluetooth readiness instead of polling IsOn / IsAvailable,
           which on Apple can bail out on a working adapter that has not yet settled. Every exception the library
           raises deliberately now derives from BleException, so one catch clause covers them all.

           The Windows (net10.0-windows10.0.19041.0) and net481 targets compile cleanly, but were not run: the
           work was done on macOS, where those two targets build only by overriding TargetFrameworks with
           EnableWindowsTargeting. Their behaviour should be smoke-tested on a Windows agent before release.