Plugin.BLE 3.1.0

dotnet add package Plugin.BLE --version 3.1.0
NuGet\Install-Package Plugin.BLE -Version 3.1.0
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="Plugin.BLE" Version="3.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Plugin.BLE --version 3.1.0
#r "nuget: Plugin.BLE, 3.1.0"
#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.
// Install Plugin.BLE as a Cake Addin
#addin nuget:?package=Plugin.BLE&version=3.1.0

// Install Plugin.BLE as a Cake Tool
#tool nuget:?package=Plugin.BLE&version=3.1.0

<img src="icon_small.png" width="71" height="71"/> Bluetooth LE plugin for Xamarin & MAUI

Build status: Build status

Xamarin, MAUI and MvvMCross plugin for accessing the bluetooth functionality. The plugin is loosely based on the BLE implementation of Monkey Robotics.

Important Note: With the term "vanilla" we mean the non-MvvmCross version, i.e. the pure Xamarin or MAUI plugin. You can use it without MvvmCross, if you download the vanilla package.

Support & Limitations

Release Notes

Platform Version Limitations
Xamarin.Android 4.3
Xamarin.iOS 7.0
Xamarin.Mac 10.9 (Mavericks) >= 2.1.0
Xamarin.UWP 1709 - 10.0.16299 >= 2.2.0
MAUI (Android, iOS, Mac, WinUI) >= 3.0.0

Nuget Packages

package stable beta downloads
Plugin.BLE NuGet NuGet Beta Downloads
MvvmCross.Plugin.BLE NuGet MvvMCross NuGet MvvMCross Beta Downloads



// stable
Install-Package Plugin.BLE
// or pre-release
Install-Package Plugin.BLE -Pre


Install-Package MvvmCross.Plugin.BLE
// or
Install-Package MvvmCross.Plugin.BLE -Pre



Add these permissions to AndroidManifest.xml. For Marshmallow and above, please follow Requesting Runtime Permissions in Android Marshmallow and don't forget to prompt the user for the location permission.

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

Android 12 and above may require one or more of the following additional runtime permissions, depending on which features of the library you are using (see the android Bluetooth permissions documentation)

<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

Add this line to your manifest if you want to declare that your app is available to BLE-capable devices only:

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>


On iOS you must add the following keys to your Info.plist


<string>YOUR CUSTOM MESSAGE</string>

<string>YOUR CUSTOM MESSAGE</string>


On MacOS (version 11 and above) you must add the following keys to your Info.plist:

<string>YOUR CUSTOM MESSAGE</string>


Add this line to the Package Manifest (.appxmanifest):

<DeviceCapability Name="bluetooth" />

Sample app

We provide a sample Xamarin.Forms app, that is a basic bluetooth LE scanner. With this app, it's possible to

  • check the BLE status
  • discover devices
  • connect/disconnect
  • discover the services
  • discover the characteristics
  • see characteristic details
  • read/write and register for notifications of a characteristic

Have a look at the code and use it as starting point to learn about the plugin and play around with it.



var ble = CrossBluetoothLE.Current;
var adapter = CrossBluetoothLE.Current.Adapter;


The MvvmCross plugin registers IBluetoothLE and IAdapter as lazy initialized singletons. You can resolve/inject them as any other MvvmCross service. You don't have to resolve/inject both. It depends on your use case.

var ble = Mvx.Resolve<IBluetoothLE>();
var adapter = Mvx.Resolve<IAdapter>();


MyViewModel(IBluetoothLE ble, IAdapter adapter)
    this.ble = ble;
    this.adapter = adapter;

Please make sure you have this code in your LinkerPleaseLink.cs file

public void Include(MvvmCross.Plugins.BLE.Plugin plugin)


Get the bluetooth status
var state = ble.State;

You can also listen for State changes. So you can react if the user turns on/off bluetooth on your smartphone.

ble.StateChanged += (s, e) =>
    Debug.WriteLine($"The bluetooth state changed to {e.NewState}");


Scan for devices
adapter.DeviceDiscovered += (s,a) => deviceList.Add(a.Device);
await adapter.StartScanningForDevicesAsync();
Scan Filtering
var scanFilterOptions = new ScanFilterOptions();
scanFilterOptions.ServiceUuids = new [] {guid1, guid2, etc}; // cross platform filter
scanFilterOptions.ManufacturerDataFilters = new [] { new ManufacturerDataFilter(1), new ManufacturerDataFilter(2) }; // android only filter
scanFilterOptions.DeviceAddresses = new [] {"80:6F:B0:43:8D:3B","80:6F:B0:25:C3:15",etc}; // android only filter
await adapter.StartScanningForDevicesAsync(scanFilterOptions);

Set adapter.ScanTimeout to specify the maximum duration of the scan.


Set adapter.ScanMode to specify scan mode. It must be set before calling StartScanningForDevicesAsync(). Changing it while scanning, will not affect the current scan.

Connect to device

ConnectToDeviceAsync returns a Task that finishes if the device has been connected successful. Otherwise a DeviceConnectionException gets thrown.

    await _adapter.ConnectToDeviceAsync(device);
catch(DeviceConnectionException e)
    // ... could not connect to device
Connect to known Device

ConnectToKnownDeviceAsync can connect to a device with a given GUID. This means that if the device GUID is known, no scan is necessary to connect to a device. This can be very useful for a fast background reconnect. Always use a cancellation token with this method.

  • On iOS it will attempt to connect indefinitely, even if out of range, so the only way to cancel it is with the token.
  • On Android this will throw a GATT ERROR in a couple of seconds if the device is out of range.
    await _adapter.ConnectToKnownDeviceAsync(guid, cancellationToken);
catch(DeviceConnectionException e)
    // ... could not connect to device
Get services
var services = await connectedDevice.GetServicesAsync();

or get a specific service:

var service = await connectedDevice.GetServiceAsync(Guid.Parse("ffe0ecd2-3d16-4f8d-90de-e89e7fc396a5"));
Get characteristics
var characteristics = await service.GetCharacteristicsAsync();

or get a specific characteristic:

var characteristic = await service.GetCharacteristicAsync(Guid.Parse("d8de624e-140f-4a22-8594-e2216b84a5f2"));
Read characteristic
var bytes = await characteristic.ReadAsync();
Write characteristic
await characteristic.WriteAsync(bytes);
Characteristic notifications
characteristic.ValueUpdated += (o, args) =>
    var bytes = args.Characteristic.Value;

await characteristic.StartUpdatesAsync();

Get descriptors
var descriptors = await characteristic.GetDescriptorsAsync();
Read descriptor
var bytes = await descriptor.ReadAsync();
Write descriptor
await descriptor.WriteAsync(bytes);
Get System Devices

Returns all BLE devices connected or bonded (only Android) to the system. In order to use the device in the app you have to first call ConnectAsync.

var systemDevices = adapter.GetSystemConnectedOrPairedDevices();

foreach(var device in systemDevices)
    await _adapter.ConnectToDeviceAsync(device);

Caution! Important remarks / API limitations

The BLE API implementation (especially on Android) has the following limitations:

  • Characteristic/Descriptor Write: make sure you call characteristic.WriteAsync(...) from the main thread, failing to do so will most probably result in a GattWriteError.
  • Sequential calls: Always wait for the previous BLE command to finish before invoking the next. The Android API needs its calls to be serial, otherwise calls that do not wait for the previous ones will fail with some type of GattError. A more explicit example: if you call this in your view lifecycle (onAppearing etc) all these methods return void and 100% don't guarantee that any await bleCommand() called here will be truly awaited by other lifecycle methods.
  • Scan with services filter: On specifically Android 4.3 the scan services filter does not work (due to the underlying android implementation). For android 4.3 you will have to use a workaround and scan without a filter and then manually filter by using the advertisement data (which contains the published service GUIDs).

Best practice


  • Surround Async API calls in try-catch blocks. Most BLE calls can/will throw an exception in certain cases, this is especially true for Android. We will try to update the xml doc to reflect this.
        await _adapter.ConnectToDeviceAsync(device);
    catch(DeviceConnectionException ex)
    catch(Exception ex)
  • Avoid caching of Characteristic or Service instances between connection sessions. This includes saving a reference to them in your class between connection sessions etc. After a device has been disconnected all Service & Characteristic instances become invalid. Always use GetServiceAsync and GetCharacteristicAsync to get a valid instance.

General BLE iOS, Android

  • Scanning: Avoid performing BLE device operations like Connect, Read, Write etc while scanning for devices. Scanning is battery-intensive.
    • Try to stop scanning before performing device operations (connect/read/write/etc).
    • Try to stop scanning as soon as you find the desired device.
    • Never scan on a loop, and set a time limit on your scan.

How to build the nuget package

  1. Build

    Open a console, change to the folder "dotnet-bluetooth-le/.build" and run cake.

  2. pack the nuget

    nuget pack ../Source/Plugin.BLE/Plugin.BLE.csproj

    nuget pack ../Source/MvvmCross.Plugins.BLE/MvvmCross.Plugins.BLE.csproj

Extended topics

How to contribute

We usually do our development work on a branch with the name of the milestone. So please base your pull requests on the currently open development branch.


Apache 2.0

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-android33.0 is compatible.  net7.0-ios was computed.  net7.0-ios16.1 is compatible.  net7.0-maccatalyst was computed.  net7.0-maccatalyst16.1 is compatible.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net7.0-windows10.0.19041 is compatible.  net8.0 was computed.  net8.0-android was computed.  net8.0-android34.0 is compatible.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-ios17.2 is compatible.  net8.0-maccatalyst was computed.  net8.0-maccatalyst17.2 is compatible.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net8.0-windows10.0.19041 is compatible.  net8.0-windows10.0.22000 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 was computed. 
MonoAndroid monoandroid was computed.  monoandroid13.0 is compatible. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Universal Windows Platform uap10.0.17763 is compatible. 
Xamarin.iOS xamarinios was computed.  xamarinios10 is compatible. 
Xamarin.Mac xamarinmac was computed.  xamarinmac20 is compatible. 
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.
  • .NETStandard 2.0

    • No dependencies.
  • MonoAndroid 13.0

    • No dependencies.
  • net7.0-android33.0

    • No dependencies.
  • net7.0-ios16.1

    • No dependencies.
  • net7.0-maccatalyst16.1

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

    • No dependencies.
  • net8.0-android34.0

    • No dependencies.
  • net8.0-ios17.2

    • No dependencies.
  • net8.0-maccatalyst17.2

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

    • No dependencies.
  • net8.0-windows10.0.22000

    • No dependencies.
  • UAP 10.0.17763

    • No dependencies.
  • Xamarin.iOS 1.0

    • No dependencies.
  • Xamarin.Mac 2.0

    • No dependencies.

NuGet packages (14)

Showing the top 5 NuGet packages that depend on Plugin.BLE:

Package Downloads

MVVMCross Plugin to access Bluetooth Low Energy functionality on Android, iOS, macOS, and Windows. Read the full documentation on the projects page.


Xamarin Bluetooth LE (Android/iOS) device support for Buttplug Servers, using Plugin.BLE. (.Net Standard 2.0)


Maui Comm Library for IDTech iOS and Android Devices


C# library for Xamarin to interact with Digi International's XBee radio frequency modules from mobile devices.


Library to work with Moduware modular platform.

GitHub repositories (3)

Showing the top 3 popular GitHub repositories that depend on Plugin.BLE:

Repository Stars
Bluetooth LE plugin for Xamarin/MAUI, supporting Android, iOS, Mac, Windows
.NET MAUI Samples
Mobile App to export data from Mi Body Composition Scale and upload it to Garmin Connect Cloud
Version Downloads Last updated
3.1.0 4,036 5/20/2024
3.1.0-rc.1 857 5/3/2024
3.1.0-beta.3 2,703 3/25/2024
3.1.0-beta.2 3,431 2/12/2024
3.1.0-beta.1 8,827 11/17/2023
3.0.0 72,661 10/8/2023
3.0.0-rc.1 4,282 9/21/2023
3.0.0-beta.6 2,900 9/5/2023
3.0.0-beta.5 10,467 8/5/2023
3.0.0-beta.4 13,647 5/21/2023
3.0.0-beta.3 21,112 4/17/2023
3.0.0-beta.2 17,020 11/24/2022
3.0.0-beta.1 4,278 11/17/2022
2.2.0-pre5 15,355 7/11/2022
2.2.0-pre4 969 6/1/2022
2.2.0-pre2 30,090 9/17/2019
2.2.0-pre1 1,605 8/11/2019
2.1.3 189,726 5/1/2022
2.1.2 175,553 4/22/2021
2.1.1 264,545 8/28/2019
2.1.0 5,721 8/11/2019
2.1.0-pre1 968 8/4/2019
2.0.1 2,734 8/4/2019
2.0.0 3,138 8/3/2019
2.0.0-pre1 17,470 5/21/2018
1.3.0 177,742 7/23/2017
1.3.0-beta3 1,549 5/4/2017
1.3.0-beta2 1,205 4/27/2017
1.3.0-beta1 1,225 4/26/2017
1.3.0-alpha1 1,352 3/10/2017
1.2.3 8,695 4/18/2017
1.2.2 2,392 4/4/2017
1.2.1 3,595 3/8/2017
1.2.0 2,336 2/17/2017
1.2.0-beta4 1,544 12/23/2016
1.2.0-beta3 1,305 12/17/2016
1.2.0-beta2 1,477 11/27/2016
1.2.0-beta1 1,310 11/19/2016
1.1.0 4,058 10/21/2016
1.1.0-beta5 1,637 10/7/2016
1.1.0-beta4 1,500 10/2/2016
1.1.0-beta3 1,479 9/30/2016
1.1.0-beta2 1,722 9/21/2016
1.1.0-beta1 1,634 8/15/2016
1.0.0 4,339 8/7/2016