Plugin.BluetoothClassic
This is a plug-in that supports transmitting/receiving data with use of the SPP (Serial Port Profile) through a bluetooth classic protocol in the Xamarin.Forms/Xamarin.Android applications.
Install / Use
/learn @rostislav-nikitin/Plugin.BluetoothClassicREADME

Plugin.BluetoothClassic for Xamarin
This is a plug-in that supports transmitting/receiving data with the use of the SPP (Serial Port Profile) through a bluetooth classic protocol in the next types of the Xamarin projects:
- Xamarin.Forms
- Xamarin.Android
How to install it?
One of they ways to install it is to use a NuGet package manager:
PM> Install-Package Plugin.BluetoothClassic -Version 1.1.5
If you're building a Xamarin.Forms application then you need to install this package into the both: Xamarin.Forms and Xamarin.Android projects.
More information about this NuGet package is accessbile by this link: https://www.nuget.org/packages/Plugin.BluetoothClassic/
How to use it?
Use DependencyService.Resolve<IBluetoothAdapter> to get instance of the device default bluetooth adapter.
IBluetoothAdapter
This is a device default bluetooth adapter wrapper.
Members
bool Enabledto check is default bluetooth adapter enabledIEnumerable<BluetoothDeviceModel> BondedDevicesto show list of the bonded remote devicesvoid Enable()to enable a default bluetooth adaptervoid Disable()to disable a default bluetooth adapterIBluetoothConnection CreateConnection(BluetoothDeviceModel bluetoothDeviceModel)to create a new connection to the bonded remote deviceIBluetoothManagedConnection CreateManagedConnection(BluetoothDeviceModel bluetoothDeviceModel)to create a new managed connection to the bonded remote device
As you see, there are two different supported connection types: the IBluetoothConnection, and the IBluetoothManagedConnection.
Let's start with the IBluetoothConnection one.
IBluetoothConnection: IDisposable
You can use it for the short-time to transmit/recive transactions. Sometimes you need to get some small piece of data and close the connection. For example, suppose you need to get 5 bytes of the data every 30 minutes. You need to get it from a temperature sensor connected to the MCU wich also connected to the bluetooth module.
In this case, the IBluetoothConnection is your choice.
It's life-time is usually a period of the transmitting/reciving data and usually it is wrapped into the using(...){..} statement to automaticlly dispose it after it's job done.
Members
Task ConnectAsync()to connect to the remote bluetooth device asynchronouslyTask TransmitAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)to transmit data to the remote bluetooth device asynchronouslyTask TransmitAsync(byte[] buffer, int offset, int count)to transmit data to the remote bluetooth device asynchronouslyTask TransmitAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)to transmit data to the remote bluetooth device asynchronouslybool DataAvailable { get; }to check is any data available to reciveTask<int> ReciveAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)to recive data from the remote bluetooth device asynchronouslyTask<int> ReciveAsync(byte[] buffer, int offset, int count)to recive data from the remote bluetooth device asynchronouslyTask<int> ReciveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)to recive data from the remote bluetooth device asynchronously
Example
public async void Transmit(BluetoothDeviceModel device, byte value)
{
const int BufferSize = 1;
const int OffsetDefault = 0;
if (device != null)
{
var _bluetoothAdapter = DependencyService.Resolve<IBluetoothAdapter>();
using (var connection = _bluetoothAdapter.CreateConnection(device))
{
if(await connection.RetryConnectAsync(retriesCount: 5))
{
byte[] buffer = new byte[BufferSize] { value };
try
{
if (!await connection.RetryTransmitAsync(
buffer, OffsetDefault, buffer.Length))
{
await DisplayAlert("Error", "Can not transmit data.", "Close");
}
}
catch(Exception exception)
{
await DisplayAlert("Error", exception.Message, "Close");
}
}
else
{
await DisplayAlert("Error", "Can not to connect.", "Close");
}
}
}
}
IBluetoothManagedConnection: IDisposable
You can use it for the long-term bluetooth connections. For example, in order to make your device stay connected to some other remote bluetooth device for minutes/hours for a continuous data exchange.
This type of the connection contains internal connection manager. It cares about reconnecting if connection was lost.
It uses transmit queue. When some code calls void Transmit(...) method it simply puts the data into this queue. In fact, data will be transmitted with the transmitter thread when the connection will be available.
Also depends on settings, this type of connection listens to input stream for the data.
It's life-time usually is equal to the life-time of the application. If it have to be created on the application starts and disposed on the application shutdowns.
Members
ConnectionState ConnectionStateto check a connection statevoid Connect()to connect to the remote devicevoid IDisposable.Dispose()to disconnect from the remote device and free unmanaged resourcesvoid Transmit(Memory<byte> buffer)to add a buffer to the transmit queue (buffers are transmitting to the remote device sequentially while remote device connected. Otherwise data will be stored until it will be reconnected)void Transmit(byte[] buffer, int offset, int count)add a buffer to the transmit queue (buffers are transmitting to the remote device sequentially while remote device connected. Otherwise data will be stored until it will be reconnected)event StateChanged OnStateChangedto subscribe/unsubscribe on any connection state changesevent Transmitted OnTransmittedto subscribe/unsubscribe on a data transmitted to the connected remote deviceevent Recived OnRecivedto subscribe/unsubscribe for/from reciving a data from the connected remote deviceevent Error OnErrorto subscribe/unsubscribe on any connection errors
Example
(From the examples/Digit example)
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class SelectBluetoothRemoteDevicePage : ContentPage
{
private readonly IBluetoothAdapter _bluetoothAdapter;
public SelectBluetoothRemoteDevicePage()
{
_bluetoothAdapter = DependencyService.Resolve<IBluetoothAdapter>();
InitializeComponent();
}
...
private async Task<bool> TryConnect(BluetoothDeviceModel bluetoothDeviceModel)
{
const bool Connected = true;
const bool NotConnected = false;
var connection = _bluetoothAdapter.CreateManagedConnection(bluetoothDeviceModel);
try
{
connection.Connect();
App.CurrentBluetoothConnection = connection;
return Connected;
}
catch (BluetoothConnectionException exception)
{
await DisplayAlert("Connection error",
$"Can not connect to the device: {bluetoothDeviceModel.Name}" +
$"({bluetoothDeviceModel.Address}).\n" +
$"Exception: \"{exception.Message}\"\n" +
"Please, try another one.",
"Close");
return NotConnected;
}
catch (Exception exception)
{
await DisplayAlert("Generic error", exception.Message, "Close");
return NotConnected;
}
...
}
}
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DigitPage : ContentPage
{
public DigitPage()
{
InitializeComponent();
DigitViewModel model = (DigitViewModel)BindingContext;
model.PropertyChanged += Model_PropertyChanged;
if (App.CurrentBluetoothConnection != null)
{
App.CurrentBluetoothConnection.OnStateChanged +=
CurrentBluetoothConnection_OnStateChanged;
App.CurrentBluetoothConnection.OnRecived +=
CurrentBluetoothConnection_OnRecived;
App.CurrentBluetoothConnection.OnError +=
CurrentBluetoothConnection_OnError;
}
}
...
private void TransmitCurrentDigit()
{
DigitViewModel model = (DigitViewModel)BindingContext;
if (model != null && !model.Reciving)
{
App.CurrentBluetoothConnection.Transmit(new Memory<byte>(new byte[] { model.Digit }));
}
}
...
AndroidManifest.xml
Don't forget to add the following lines to the your {AplicationName}.Android/Properties/AndroidManifest.xml file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1" android:versionName="1.0"
package="com.companyname.plugin.bluetoothclassic">
...
<uses-feature android:name="android.hardware.bluetooth" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
...
</manifest>
Examples
The examples folder contains working example(s) that demonstrates how to use this package in practice.
- Retry example demonstractes how to work with a
IBluetoothConnection - Digit
Related Skills
openhue
344.1kControl Philips Hue lights and scenes via the OpenHue CLI.
sag
344.1kElevenLabs text-to-speech with mac-style say UX.
weather
344.1kGet current weather and forecasts via wttr.in or Open-Meteo
tweakcc
1.5kCustomize Claude Code's system prompts, create custom toolsets, input pattern highlighters, themes/thinking verbs/spinners, customize input box & user message styling, support AGENTS.md, unlock private/unreleased features, and much more. Supports both native/npm installs on all platforms.
