Sunday, February 16, 2014

BLE for developers in Windows 8.1 Part I

    For who doesn't know Bluetooth Smart/Low Energy devices are devices optimized for low power consumption. Devices can act as an GATT Server, GATT client or both at the same time. In Server mode the device exposes one or more services. Each service has one or more characteristics with properties attached to it (read, write, notify, broadcast,...). The GATT Client connects to the Server and "consumes" its services.

   Windows 8.1 has added support for communicating with Bluetooth devices from the store applications. This opened the development for a lot of accessories including the Bluetooth Low Energy devices. Windows 8.1 supports only GATT client mode. This is the first iteration of the Bluetooth api and, from some points of view, it is not complete and kinda rushed out to be available. Here are some missing features:

  1. The User eXperience when connecting to an Bluetooth device is bad. The common scenario for this kind of device is that you buy the device you see the application for the Windows Store is available, download it and run the app to play with the device. Here comes the "fun" part: form inside the store application you cannot connect to devices that were not previously paired from PC Settings - Devices - Bluetooth. So, for the first connection, when the user launches the application and you don't find any device to connect to, you will have to find a way to explain and convince the user to open Settings ->Change PC Settings->.... Bluetooth and then come back to your application. Also it is not possible to launch the  Bluetooth screen directly like in Windows Phone case.
  2. For the BLE devices the whole idea of the application is to consume less but have a connection between the device (server) and the application (client). Too bad that is there is no to make teh application run in the background or receive notifications from the devices when the application is suspended. This missing feature "kills" a little bit the BLE principles and without it the protocol from the app side is not better than the classical SPP. The application should be able to connect, receive notifications in the background, be aware when the device disconnects and to be able to reconnect when available again.
  3. Without being able to connect to devices that were not previously paired beacon scenarios are not possible.
  4. Missing api's to discover the services and the characteristics of each service for any BLE device. 
     Beside that let's have a first look at how the new apis work. I have bought several BLE devices: The Texas Instruments CC2541 SensorTAG, Estimote tag,  Kensington Proximo TAG. (I actually bought the devices in inverse order: Kensington, Estimote TI ).
       If you want to start testing/demo the BLE api I think the SensorTAG  is the best choice. The Texas Instruments chipset CC2540/1 is one of the most used chipset in BLE devices and the price of this kit is around 25$. It includes 6 sensors inside: IR temperature Sensor, Humidity Sensor, Pressure Sensor, Accelerometer, Gyroscope, Magnetometer so you can simulate various scenarios.
     The first step would be to understand if your system supports BLE devices. For this you will have to open Device Manager-> Bluetooth and see if the "Microsoft Bluetooth LE Enumerator" is present. If it is your system can connect to Bluetooth Smart devices. 

  The second step would be to pair with the device (actually you could skip the first step and if you don't see your device in the list see if your system supports BLE). To make the device visible for pairing just push the button on the left side of the device.

    If the device you are looking for is present pair with it (used 0000 as pin). The system configures all the GATT services internally.  Here is how your windows Bluetooth devices look internally before and after the pair:

    You can actually see that we have new GATT services added to our system.

    When using the GATT api you will actually have to know the service you want to connect to and also the characteristics that the service exposes. There are several ways to do that:

  1. The most obvious one you have the documentation of the device and see all the services and their characteristics. Here is the pdf of the TI Development KIT
  2. You can actually get the Service UUID's from the Device Manager:

and then you could hope it is one of the standard BLE profiles so you could have the characteristics for that service. In the picture above the UUID of the GATT service is f000aa40-0451-4000-b000-000000000000 and it doesn't fit any standard profile. The standard profiles fit this pattern: 0000XXXX-0000-1000-8000-00805f9b34fb where XXXX is the id of the profile (for example 0x1800 is the generic access profile). There are some GATT Services that are not showed in the Device List even if present and, as far as I tested,  these are the Generic Access 0x1800 and  Generic Attribute 0x1801 and Device Information 0x180A. You can  always try to add these services to the list of services you want to connect to as it will enable you to have more information on the device you are connecting to.

    So here is the list of the GATT services exposed by the SensorTAG:
Thermometer "f000aa00-0451-4000-b000-000000000000"
Accelerometer "f000aa10-0451-4000-b000-000000000000"
Humidity "f000aa20-0451-4000-b000-000000000000"
Magnetometer "f000aa30-0451-4000-b000-000000000000"
Barometer "f000aa40-0451-4000-b000-000000000000"
Gyroscope "f000aa50-0451-4000-b000-000000000000"
Key Service "0000ffe0-0000-1000-8000-00805f9b34fb"
+ standard and hidden in device manager:
Generic Access: 00001800-0000-1000-8000-00805f9b34fb
Generic Attribute: 00001801-0000-1000-8000-00805f9b34fb
Device Information: 0000180A-0000-1000-8000-00805f9b34fb

     Now that we know what services our device exposes we can start creating the Store Application and add the necessary capabilities which will enable us to connect to the device. Start Visual Studio and create an Windows Store application then edit the Package.appxmanifest file using View Code option. Add the following capabilities:

   <Capability Name="internetClient" />  
   <m2:DeviceCapability Name="bluetooth.genericAttributeProfile">  
    <m2:Device Id="any">  
     <m2:Function Type="serviceId:f000aa00-0451-4000-b000-000000000000"/>  
     <m2:Function Type="serviceId:F000AA10-0451-4000-B000-000000000000"/>  
     <m2:Function Type="serviceId:F000AA20-0451-4000-B000-000000000000"/>  
     <m2:Function Type="serviceId:F000AA30-0451-4000-B000-000000000000"/>  
     <m2:Function Type="serviceId:F000AA40-0451-4000-B000-000000000000"/>  
     <m2:Function Type="serviceId:F000AA50-0451-4000-B000-000000000000"/>  
     <m2:Function Type="serviceId:0000ffe0-0000-1000-8000-00805f9b34fb"/>  
     <m2:Function Type="serviceId:00001800-0000-1000-8000-00805f9b34fb"/>  
     <m2:Function Type="serviceId:00001801-0000-1000-8000-00805f9b34fb"/>  
     <m2:Function Type="serviceId:0000180A-0000-1000-8000-00805f9b34fb"/>  

  When interacting with the device you will have to find the devices that expose the services we want to connect to and then connect to the desired service. For this we will call Enumeration.DeviceInformation.FindAllAsync passing an AQS string to filter the devices. Let's say we want to connect to the devices that expose the Generic Access Service as it is an standard service. We can actually generate the AQS in 3 different ways for this service:

  1. Using the the standard implemented GattServiceUuids GetDeviceSelectorFromUuid(GattServiceUuids.GenericAccess)
  2. Generating an standard UUID from an shortID GetDeviceSelectorFromShortId(0x1800) -what it actually does is to add  "-0000-1000-8000-00805f9b34fb" to the id
  3. When the service is not a standard one you will have to use GetDeviceSelectorFromUuid(new Guid(serviceGuid)) and pass the custom service guid in our case serviceGuid="00001800-0000-1000-8000-00805f9b34fb"
   The code below shows how to read the device name using the Generic Access Service of the Sensor Tag. Here are the standard characteristics of the generic access service link.

       //Find the devices that expose the service  
       var devices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(GattDeviceService.GetDeviceSelectorFromUuid(GattServiceUuids.GenericAccess));  
       if (devices.Count==0)  
       //Connect to the service  
       var service = await GattDeviceService.FromIdAsync(devices[0].Id);  
       if (service == null)  
       //Obtain the characteristic we want to interact with  
       var characteristic = service.GetCharacteristics(GattCharacteristic.ConvertShortIdToUuid(0x2A00))[0];  
       //Read the value  
       var deviceNameBytes=(await characteristic.ReadValueAsync()).Value.ToArray();  
       //Convert to string  
       var deviceName=Encoding.UTF8.GetString(deviceNameBytes,0,deviceNameBytes.Length);  

   Now let's see how to interact with an non standard service and a characteristic that notifies when changing. I've chosen the accelerometer service of the SensorTAG:

       //Find the devices that expose the service  
       var devices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(GattDeviceService.GetDeviceSelectorFromUuid(new Guid("F000AA10-0451-4000-B000-000000000000")));  
       if (devices.Count==0)  
       //Connect to the service  
       var accService = await GattDeviceService.FromIdAsync(devices[0].Id);  
       if (accService == null)  
       //Get the accelerometer data characteristic  
       var accData = accService.GetCharacteristics(new Guid("F000AA11-0451-4000-B000-000000000000"))[0];  
       //Subcribe value changed  
       accData.ValueChanged += accData_ValueChanged;  
       //Set configuration to notify  
       await accData.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);  
       //Get the accelerometer configuration characteristic  
       var accConfig = accService.GetCharacteristics(new Guid("F000AA12-0451-4000-B000-000000000000"))[0];  
       //Write 1 to start accelerometer sensor  
       await accConfig.WriteValueAsync((new byte[]{1}).AsBuffer());  

In this case we connect to the service, we subscribe to the data characteristic notifications (we also ensure that the characteristic is in notify mode), and then we start the accelerometer sensor. Once the sensor starts it will notify us every 1000 ms. (default value that can be changed).

 async void accData_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)  
       var values = (await sender.ReadValueAsync()).Value.ToArray();  
       var x = values[0];  
       var y = values[1];  
       var z = values[2];  

We can do the same with all the other services exposed by the device.

Till next post NAMASTE!

No comments:

Post a Comment