AndroidappleBluetoothC#DevelopmentXamarin

Bluetooth Low Energy UART Service with Xamarin Forms

uart-ble-xamairn-forms-logo

A little bit of background.

Bluetooth is a wireless technology standard used for exchanging data between fixed and mobile devices over short distances using short-wavelength UHF radio waves in the industrial, scientific and medical radio bands, from 2.402 GHz to 2.480 GHz, and building personal area networks. It was originally conceived as a wireless alternative to RS-232 data cables (reference: https://en.wikipedia.org/wiki/Bluetooth).

In Bluetooth Classic, a Serial Port Profile (SPP) is one of the traditional Bluetooth profiles (https://www.bluetooth.com/specifications/profiles-overview/) and it is an adopted profile defined by the Bluetooth Special Interest Group (SIG) used to emulate a serial port connection over a Bluetooth wireless.

For Bluetooth Low Energy (BLE), an adopted profile for implementing Serial Port Profile (SPP) over BLE is not defined, thus emulation of a serial port must be implemented as a vendor-specific custom profile.

At this point, I will not go into details of BLE, but you can find a lot of documentation on the web and a quick-start information on my blog post https://www.jenx.si/2019/08/09/bluetooth-gatt-server/.

For now, I will only emphasis that when you start building apps on top of BLE, you will need to know what Generic Attribute Profile (GATT) is and how to deal with this protocol.

How to start?

When I start with any Bluetooth LE project, I always observe which GATT Profiles/Service/Characteristics are exposed by GATT Server. For that I use super-awesome Android tool nRF Connect for Mobile (or for iOS here) from Nordic Semiconductor.

Putting my case-study in the context.

When we talk about Bluetooth communication there is almost always some hardware included in the development loop.

In my case, on one of my projects I worked with OBD2 Bluetooth dongle where UART/Serial communication was enabled over BLE.

First of all, I paired with my BLE device and observe GATT entities. I had four GATT services available on my BLE dongle, i.e.:

gatt-uart-services

For me, the interesting GATT services were the two marked in upper image.

First two are Generic Access and Device Information GATT services, providing general information about the device. You can check about these two services here: https://www.bluetooth.com/specifications/gatt/services/.

At first, I thought that first one (out of interesting ones) was used for UART communication. I knew that my BLE dongle used MicroChip hardware – and I found good documentation how Microchip use UART over BLE: Accessing the Transparent UART Service using Android BLE.

After a bit of investigation, I found out that GATT service with UUID 0xFFF0 (e.g. GUID 0000FFF0-0000-1000-8000-00805f9b34fb) is the right GATT service for me, and it’s used for UART communication with my BLE dongle.

Now, I have all the information needed, let’s start coding!

Let’s do some code.

In this blog post, I will present how to implement BLE UART with Xamarin Forms. I will show Android and iOS version by sharing almost 100% of the source code.

I used my https://github.com/josipx/Jenx.Bluetooth.GattServer source repository as a starting point. This Github repository is accompanying source code for my blog post https://www.jenx.si/2019/08/09/bluetooth-gatt-server.

After checkout, I removed all server side (GATT Server) projects. Next, I replaced outdated Plugin.Permissions NuGet with permission module from Xamarin.Essentials 1.5.3.2. Furthermore, I updated projects to the latest (as of this writing) NuGets, namely: Xamarin Forms 4.8.0.1269, BLE.Plugin 2.1.1 and for basic application cosmetics Xamarin.Forms.Visual.Material 4.8.0.1269.

Finally, I slightly changed namespaces and polished some code to reflect and to address my current domain.

App flow is as simple as possible:

  1. User starts the application.
  2. User hits “Scan” button where BLE permissions are checked and scan for BLE devices is performed.
  3. List of found BLE devices is displayed.
  4. User selects BLE device to connect to.
  5. On successful connection, apps redirects on UART data-exchange page.

You can check complete source code here https://github.com/josipx/Jenx.Bluetooth.UartOverGatt, here I will only present important and interesting parts of my application.

My mobile app consists basically from two pages, all code is approx. 100 lines of C# code-behind per page, and some basic XAML for view-page. Therefore, I will simply dump my code here, and comment out interesting parts.

My GATT Identifiers/constants are defined as:

Initial – main page page is where I can check permissions and perform BLE scanning. Xamarin.Essentials permissions module is used for handling permissions. Adding permissions to Android and iOS can be tricky, but for more details you can check application Android Manifest or iOS counterpart info.plist.

MainPage

XAML
Code behind

BluetoothDataPage

XAML
Code behind

At this point, I must express my disclaimer :): in this blog post – minimal proof-of-concept example is presented – in real production-ready application all this functionality would be organized in more structural way (e.g. as Views, Model, View-Models, Services, etc…).

My mobile apps in action

Android
iOS

OK, look & feel of the app is not exactly the same between Android and iOS, but UART functionality is working perfectly on both platforms – and for this example this is all that matters.

Conclusion

In this blog post I wanted to show how easy is to use Xamarin Forms and make Android and iOS app to talk to Bluetooth LE device. Furthermore, I presented simple Bluetooth UART example over BLE.

Many thanks to Plugin.BLE plugin which provides awesome Xamarin Forms Bluetooth layer.

In one of the next blog posts, I will use this investigation and show how to talk to cars over OBD2 protocol.

Until then, happy coding.

34 thoughts on “Bluetooth Low Energy UART Service with Xamarin Forms

  1. Hi, while using sendCharacteristics.WriteAsync in BluetoothDataPage.cs , its directly going to the catch part and showing msg as “Error sending comand to UART” , Can you help me out with this.
    Thanks and Regards,
    Abhimanyu Panda
    Developer, TCS
    India

  2. Thanks so much! When searching for the git reference, I mistakenly found the first one. When I built and ran it, it looked pretty much like the example in the blog so I thought I had the right code. I was able to run the correct project and connect to my HM18 by tweaking the GUIDs.

    However, my goal is to create a Xamarin BLE server. My project has a client that connects to 3 other devices (HM18, ESP32 feather and, NRF feather BLE). But my client doesn’t connect my Android Xamarin app. So I was looking for other examples to try and rule things out. If you know of a good android for Xamarin based example server, appreciate the pointer. But thanks for the quick reply.

    1. I did not do any Android (Xamarin Forms) BLE GATT servers. But, at first shoot I would say:
      – BluetoothGattServer Android API exist, so it’s possible to wrap this API and bring it to the XF common C# xamarin layer, or
      – check if some Java based GATT server library (aar) exist and import functionality via bindings to the XF layer.

      Joze

  3. Getting this when trying to build the solution as is from the repo. Any thoughts?

    Severity Code Description Project File Line Suppression State
    Error drawable-v24_materialactivityindicatorbackground.xml.flat: error: failed to open file. “drawable-v24_materialactivityindicatorbackground.xml.flat: error: failed to open file.”. Jenx.Bluetooth.UartOverGatt.Client.Android

      1. Thank you. I ended up porting the code to my project instead and everything built fine after some namespace changes. Thank you for the examples. It really helped and I’m communicating with a ble device now.

  4. @Joze I tried your code. able to scan devices but when I tried to connect getting GattCallback error 133,
    only trying for android.

    1. @rmpQue: sometime Android just pops this GATT 133 error. For start, try (not so nice) walkaround:

      for (var r = 0; r < 3; r++) // retry 3times, else fail
      {
      	try
      	{
      		await _bluetoothAdapter.ConnectToDeviceAsync(bleDevice, new ConnectParameters(false, true));
      	}
      	catch // expand this for specific exceptions
      	{
      		//
      	}
      
      	if (bleDevice.State == DeviceState.Connected)
      	{                        
      		break;
      	}
      }
      
      				
  5. @Joze Thanks for the quick response, I tried same, but maybe I’m trying to connect to the wrong devices.
    I’m trying to connect my headphone, mobiles both have a BLE

  6. @Joze Now trying actual BLE module, now able to connect but before entering the Passkey its showing connect and when read services then no one service getting

    I’m doing like that,
    scanning devices
    Connecting to the device (showing the connected, device.State)
    Trying to read Services but getting zero counts for services

    I tried code for readService as bellow,

    var allServices = await device.GetServicesAsync();
    var serviceByUUID = await device.GetServiceAsync(SERVICE_UUID);
    var serviceByDeviceId = await device.GetServiceAsync(device.Id);

    Please correct me where I’m wrong

    1. @rmpQue: Did you try some 3Rd party tool (like Nordic -https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en&gl=US) and check if you can find/discover selected GATT services/characteristics? If this works, then checks Android manifest/iOS pfile, permissions and BLE GATT service/characteristic properties if you can access these services/characteristics…

  7. @Joze, I tried your suggested Nordic app as well as SmartDiscover, but no luck.
    Steps
    1. Scanning the devices
    2. Click on the device to establish the connection
    3. It showing connected only for the few ms (not sure its connected or not but device.State showing connected)
    4. passkey popup is open
    5. once the popup comes the connection lost and once we enter the passkey nothing happening…

    So, do we have control of the passkey popup? (I think it’s from system or BLE)

    code as bellow,
    private async void btnConnect_Clicked(object sender, EventArgs e)
    {
    if(device == null || string.IsNullOrEmpty(device.Id.ToString()))
    {
    await DisplayAlert(“Unexpected”, “Please first select the device, and then retry.”, “OK”);
    return;
    }

    for (var r = 0; r < 3; r++)
    {
    try
    {
    await adapter.ConnectToDeviceAsync(device, new ConnectParameters(false, true));
    }
    catch(Exception ex)
    {
    status.Text = "Not Connected";
    status.TextColor = Color.Red;
    await DisplayAlert("Generic Notice", ex.Message.ToString(), "OK");
    }

    if (device.State == DeviceState.Connected)
    {
    status.Text = "Connected";
    status.TextColor = Color.Green;
    await DisplayAlert("Now Connected", $"Now connected to BLE device: {device.Name}", "OK");
    break;
    }
    else
    {
    status.Text = "Not Connected";
    status.TextColor = Color.Red;
    }
    }
    }

    code steps
    1. await adapter.ConnectToDeviceAsync(device, new ConnectParameters(false, true)); executed
    2. then device.State is aslo showing connected (device.State == DeviceState.Connecte) its true

    1. @rmpQue

      Some additional 🙂 ideas:

      1) I guess your device needs paring/bonding before any communication.

      2) On Xamarin Forms for android you can use “BroadcastReceiver” for custom paring, and setting pins if automatic “android system” does not work for you. iOS for security puposes does not allow this.

      3) Try pairing with your device via Android Bluetooth panel, so you will have paired device before doing your BLE GATT data excange.

  8. @Joze Thank you very much, using the BroadcastReceiver, able to auto-connect to the device.

    yes, you are correct, my device, without pairing/bonding we can not communicate.

    facing another issue, want to send a query (Modbus ASCII packet) to read data from the device, but in Plugin.BLE there is no such read function to pass the query to the device, right?

    Means, only Characteristic.ReadAsync(); available, but want to read data bypassing the Modbus Packet, so how we can do that?

    1. @rmpQue: When we are talking about BlueTooth Light (BLE), then we are talking about GATT/GAP communication protocols -> Services/Characteristics/Attributes. (Check BlueToot specifications on this) .
      On top of this communication layer, you can create custom messaging protocol. Modbus is one of those protocols. If you want to communicate with ModBus packages you just need to convert these packages to bytearray and sand it via predefined Services/Characteristics on BLE layer.
      Plugin.BLE is strictly cross-platform Xamarin Forms abstraction layer on top of BLE protocols.

      one extra note here: when sending large byterrays over BLE pay attention also on MTU (max trans unit) size. But this is another story….

  9. “If you want to communicate with ModBus packages you just need to convert these packages to bytearray and sand it via predefined Services/Characteristics on BLE layer.”

    Read data from Device :
    suppose I converted packets into byteArray, so which function we can use to send that byteArray?
    want something like that, var response = await device.ReadAsync(“query”);
    query maybe be a byteArray or Modbus packet

    So, how we can do using the plugin.BLE?

    For Write data to Device:
    the function is maybe like that, var status = await Characteristic.WriteAsync(Data);
    So, this function is already available.

      1. As I stated before, Plugin.Ble is simply Xamarin forms cross platform abstraction layer on top of iOS and Android BLE APIs.

        So, If you check Bluetooth light documentation you will see that GATT Characteristics (base entities for BLE communication) have many permissions: Read, Write, Notify (https://www.bluetooth.org/docman/handlers/DownloadDoc.ashx?doc_id=478726)

        These two methods (Read, Write and async variants) simple read or write on selected characteristics.

        If you have notification mechanism enabled on BLE, you can use also:

        notificationCharacteristic.ValueUpdated += DataReceived;

        and register callback to accept notification messages from BLE device.

        Anyway,
        1) you must know which Services/Characteristics use your device
        2) You convert your message (I guess, Modbus ASCII in your case) into array of bytes
        3) write (or read or register for notifications) from characteristics.

  10. I just wanted to say thanks for this, yours is the only working example I could find for reading information from a BT device. Explained well, works perfectly and it’s saved me an awful lot of time.

  11. First I want to say many thanks for sharing this. It helped me a lot. I want to ask a question maybe you could help. Every time the app runs you have to scan for devices and then connect. I need the app to save the preferred device and load it automatically every time it starts. I understand the device is type IDevice which is an interface.. I tryied several methods of saving an instance of the selectedItem so I can load it again with no luck. Or maybe create a new variable of type IDevice and set the name and address of my bluetooth device somehow? I also tried that but the properties of IDevice are read only.. Any Ideas? Thank you very much in advance.

    1. 1) You can store initial IDEvice Guid and on the next run search/filter out your device from IAdapter.DiscoveredDevices and/or ConnectedDevices.
      2) Scanning for devices is very energy consuming so use it as less as possible…
      3) “IDevice” props are more or less read only (advertise BLE name, rssi, DeviceState…).

  12. The device I have does not have a READ characteristic. It supports NOTIFY, WRITE, WRITE NO RESPONSE. I use the same characteristic for both read and write. The device ONLY exposes one of these characteristics. When I try to get data from it I get an catch error Error initializing UART GATT service. Can I read from a NOTIFY characteristic?

  13. Hi Joze,

    First, thanks for your code, I am working with an ESP32 and got communicating my Android cellphone with it. The ESP32 is in server mode.

    What I’d like to do now is to start my app when the cellphone detects the ESP32. Did you do anything like that? I’ve been reading and it seems I should code a service to run in the background, but I’m not sure if it’s the best approach. As I am an microcontrollers engineer and not app developer, I’m looking something as simple as possible. What do you think? Wich is the approach you woul look for?

    Thanks in advance

  14. @Joze, Ive followed this an have my app connecting to ble. I can send commands (via Arduino serial monitor) to turn the light on and off, i receive the commands from the serial monitor in the app but i cannot send any commands from the app. Can you help please?

  15. Ive followed this and it finds and connects to my ble device. I am able to send commands via Arduino serial monitor which will turn a light on. I can see those commands in the app so they are been received via Bluetooth. The problem i have is i cant send any commands via the app to me ble device. Can you help please. thankyou

  16. Dear Joze, thanks a lot for your example!
    I have problem with testing on real hardware Android 11 (API 30) BLE UART (nRF52).
    When scanning, the application does not see BLE devices, but I see them from the NRF connect.
    Can you suggest where to look for the problem?

  17. Joze,
    As everyone else has said, thank you so much for this tutorial. I have searched for weeks and came up with nothing until I found yours. Best one by far!

    I created a project using “Jenx.Bluetooth.UartOverGatt.Client” as the name just so it would match your tutorial line for line. The project works great! It does everything it is supposed to and sends/receives data from my Arduino device.

    I create a new project with a name like “MyProject” and I use your code like I did before, but change the namespaces and everything from “Jenx.Bluetooth.UartOverGatt.Client” to “MyProject” but for some reason it is no longer able to find the UART GATT Service. It finds the device, but when I try to initialize I get that error message saying it can’t be found. Everything from the jenx project that worked and the MyProject are exactly the same, except the name, but for some reason it cannot connect. Any ideas on why? Thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.