Blog

  • 32feet.NET on .NET 5.0 and Above

    Version 4.0.22 of InTheHand.BluetoothLE (and 4.0.25 of InTheHand.Net.Bluetooth for Rfcomm) marks an important milestone. It adds a .NET 5.0 specific build which is important because the approach used to access WinRT APIs has changed since .NET Core 3.x and so the library was not compatible with later versions. Now it will run on Windows 10 on .NET 5.0 and 6.0.

    This release also adds some new functionality which goes beyond the WebBluetooth API on which it is based. You can now read the negotiated MTU (Maximum Transmission Unit) for the current connection. Importantly it also requests a higher than default value on Android. The reasoning behind this is that generally platforms use a value in the region of 512 bytes but Android starts with a default value of 20 bytes. Prior to this update you had to write platform specific code to change it, but now we request 512 bytes on connection and whatever value is negotiated (it may still be lower than this depending on the radio device and remote peripheral) is readable from the Mtu property.

  • Bluetooth and Windows 11

    It’s still early days for Windows 11 but pre-release API documentation is available on Microsoft Docs and it gives some hints of new functionality for developers:-

    BluetoothLEDevice gains functionality to request preferred connection parameters. These include minimum and maximum connection interval, latency and link timeout. If these seem a bit complex to set individually there are three static properties which give you a choice of optimizing for throughput, power consumption or a balance in the middle. It also has the ability to query the Bluetooth PHY (physical layer) used which (if both devices support Bluetooth 5.0) can be either Coded (smaller data but longer range), 2M (larger data) or 1M (same as Bluetooth 4.x). It doesn’t currently seem to support requesting a preferred PHY.

    I’m looking at how these can be incorporated into 32feet.NET given that there is currently support for PHY on Android only.

  • Bluetooth Virtual Sniffer for Windows

    I only just found out about this but Microsoft released a packet sniffer for Bluetooth on Windows 10 back in February. This is incredibly useful for debugging and is something I’ve been craving for some time. Previously I’ve been able to analyse packets from Android using Wireshark and now we can view activity between Windows and another device.

    I’m desperately hoping this provide some clues to the constant “Unreachable” errors when trying to communicate with an iPhone which Windows Settings acknowledges is currently connected…

    You can download the Bluetooth Test Platform here.

  • New Windows Phone App

    Okay, now I’ve drawn you in with a click-bait headline I need to clarify a little! For some time now Windows 10 has had a tightly integrated app called Phone Link (previously Your Phone) which allows you to interact with your phone from the desktop. However, it doesn’t cater for the 50-odd percent of users who have fruit based phones. I’ve been working on closing this gap by creating an app for Windows which is now available in the Microsoft Store called Thy Phone.

    The name came about because thy is an archaic version of your and the name also happens to rhyme with a popular phone type.

    It uses functionality available in Windows 10 2004 and above to setup a phone device to use Windows as a handsfree device. This allows you to conduct calls through your PC microphone and speakers (or headset) and see call progress and incoming call alerts on your desktop. It also allows you to activate the Bluetooth Audio support so that you can play music, podcasts etc from your phone through your PC.

    Finally when using an iPhone it supports toast notifications and mirrors those notifications directly in the Windows notification center. Notifications which uses actions can also be triggered directly from the desktop Toast.

    I’ll be adding more functionality over time including Messaging support. I completed a proof of concept but I’m working on a new version of InTheHand.Net.Obex to make it easier to do this. That will also open up other functionality such as file transfers. Because these features all use standard Bluetooth profiles the app is not limited to just iPhones, you can even pair and use your Windows Phone or Nokia 3310 – whatever floats your boat!

    English badge
  • What’s in a BluetoothLEDevice.Name

    I’ve noticed an odd behaviour when creating a BluetoothLEDevice from a found device id. I’m not doing anything special, just retrieving paired devices and then trying to access the relevant BluetoothLEDevice. In common with all Windows device searches (whether by Picker or programmatically with DeviceInformation.FindAllAsync) you receive a DeviceInformation which contains the name and unique id (an opaque device path).

    If you search for paired BluetoothLEDevices the returned DeviceInformations contain the valid name and id of the device. When you use BluetoothLEDevice.FromIdAsync() you get back a working BluetoothLEDevice instance but the Name is of the form “Bluetooth nn:nn:nn:nn:nn:nn” which is slightly less than useless. It turns out that Windows isn’t suffering from Amnesia and the actual display name of the device is accessible from BluetoothLEDevice.DeviceInformation.Name. I was scratching my head for a while over the logic of this but at least now I’ve been able to put in a workaround in InTheHand.BluetoothLE 4.0.18. The BluetoothDevice.Name now shows the same name as Windows shows in the UI and so it behaves the same as the other platform implementations!

  • Wrapping up 2020

    I haven’t posted as much as I would have liked this year, because “reasons” but I thought I would put together a short post to wrap up some of the things that have been going on behind the scenes.

    32feet

    Back in January I announced a re-write of 32feet.NET. The library for Bluetooth Classic has been reworked for the NuGet/.NET Standard age. The intention was removing legacy code and take advantage of newer language features to make it easier to support more platforms and concentrate on getting the core functionality across as many .NET platforms as possible. It’ll also make it easier to support .NET 5/6 ahead. I’ve invested a lot of time into the macOS IOBluetooth and IOBluetoothUI bindings too and will be integrating macOS into the 32feet library in 2021.

    I started to work on integrating some code I had written for iOS using the External Accessory API. This allows access to a subset of Bluetooth devices if they implement Apple’s proprietary MFi standard. This will allow devices such as mobile printers to be used with shared code across multiple platforms.

    I would like to start work on Linux support (via BlueZ) too which will be another major platform ticked off.

    This year I also revived original parts of 32feet.NET in separate packages so IrDA and Object Exchange functionality are available separately. IrDA is end-of-life so I won’t be investing in this area but I wanted to ensure it was made available for desktop Windows in a modern package if required. OBEX has the basic ObexWebRequest with a few minor improvements but it’s something I intend to revisit with a more powerful API soon.

    Bluetooth Low Energy

    Alongside the Bluetooth Classic updates I also created a new library for Bluetooth Low Energy. This currently supports Windows 10 (.NET Framework, .NET Core and UWP), Android, iOS, tvOS, watchOS, macOS. It would be nice to add Linux and possibly Tizen to this list!

    The API follows the existing WebBluetooth standard closely with some additional features relevant to apps. For example not only can you display a picker to the user to choose a device but you can also programmatically discover to generate your own UI. There are some platform specific additions too such as the ability to request a particular physical layer (PHY) which facilitates longer range communication (Currently Android only). Work has also gone into round-tripping support for service UUIDs – the ability to specify by name or short and long UUIDs.

    MediaElement

    The Xamarin Forms MediaElement now has a new home in the Xamarin Community Toolkit. It’s already received a number of fixes and improvements. In October I added support for databinding to the Position property. Rather than having to write your own timer and query the position you can just bind your UI to the property which gets regularly updated as the media plays.

    Happy New Year

    I hope you have a happy holiday season and wish you the best for 2021.

  • Bluetooth Virtual COM Ports

    I was thinking about a comment on a Gist I wrote some time ago and thought I’d written a blog post on the topic, but when I couldn’t find any sign of it I decided to start from scratch as I’d have probably needed to update it anyway.
    The Bluetooth stack in Windows has supported virtual COM ports since the beginning (Windows XP SP2 if you can remember that far back!). It’s important to understand the difference between these and any Serial Port Bluetooth device. The Virtual COM port functionality exists purely for interop with older software – you can make a Bluetooth device appear to the system as a wired Serial device and open a COM port and talk to the device as if it was plugged into your PC.
    If you’re writing modern software which talks to devices then you would use a Bluetooth API – either 32feet.NET or the platform specific Bluetooth API directly. If not you can create a virtual COM port. Throughout Windows 10’s lifetime the Bluetooth options have been slowly moving to the modern Settings experience but the old Control Panel still exists and is used for some additional functionality – Virtual Serial ports are still in there.
    The port can be either incoming – you have a listening service which other devices can connect to; or outgoing where you connect to a specific remote device. From Settings > Devices select “More Bluetooth options” from the righthand menu.

    2020-11-22 (1)

    From the resulting “classic” control panel applet select the “COM ports” tab to see configured ports or to set one up.

    2020-11-22

    When I reworked the 32feet.NET library I decided not to include the COM port functionality as it’s only relevant to Windows and is for legacy code only. However I put together the required code to enumerate all the Bluetooth virtual COM ports on the system in a Gist which you can find here:-

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Text;
    namespace BluetoothDiagnostics
    {
    public sealed class BluetoothComPort
    {
    /// <summary>
    /// Returns a collection of all the Bluetooth virtual-COM ports on the system.
    /// </summary>
    /// <returns></returns>
    public static IReadOnlyList<BluetoothComPort> FindAll()
    {
    List<BluetoothComPort> ports = new List<BluetoothComPort>();
    IntPtr handle = NativeMethods.SetupDiGetClassDevs(ref NativeMethods.GUID_DEVCLASS_PORTS, null, IntPtr.Zero, NativeMethods.DIGCF.PRESENT);
    if (handle != IntPtr.Zero)
    {
    try
    {
    NativeMethods.SP_DEVINFO_DATA dat = new NativeMethods.SP_DEVINFO_DATA();
    dat.cbSize = Marshal.SizeOf(dat);
    uint i = 0;
    while (NativeMethods.SetupDiEnumDeviceInfo(handle, i++, ref dat))
    {
    string remoteServiceName = string.Empty;
    StringBuilder sbid = new StringBuilder(256);
    int size;
    NativeMethods.SetupDiGetDeviceInstanceId(handle, ref dat, sbid, sbid.Capacity, out size);
    Debug.WriteLine(sbid);
    long addr = GetBluetoothAddressFromDevicePath(sbid.ToString());
    // only valid if an outgoing Bluetooth port
    if (addr != long.MinValue && addr != 0)
    {
    IntPtr hkey = NativeMethods.SetupDiOpenDevRegKey(handle, ref dat, NativeMethods.DICS.GLOBAL, 0, NativeMethods.DIREG.DEV, 1);
    var key = Microsoft.Win32.RegistryKey.FromHandle(new Microsoft.Win32.SafeHandles.SafeRegistryHandle(hkey, true));
    object name = key.GetValue("PortName");
    var pkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\BTHPORT\\Parameters\\Devices\\" + addr.ToString("x12"));
    if (pkey != null)
    {
    foreach (string nm in pkey.GetSubKeyNames())
    {
    if (nm.StartsWith("ServicesFor"))
    {
    var skey = pkey.OpenSubKey(nm);
    string s = sbid.ToString();
    //bluetooth service uuid from device id string
    int ifirst = s.IndexOf("{");
    string uuid = s.Substring(ifirst, s.IndexOf("}") – ifirst+1);
    var ukey = skey.OpenSubKey(uuid);
    // instance id from device id string
    string iid = s.Substring(s.LastIndexOf("_")+1);
    var instKey = ukey.OpenSubKey(iid);
    // registry key contains service name as a byte array
    object o = instKey.GetValue("PriLangServiceName");
    if(o != null)
    {
    byte[] chars = o as byte[];
    remoteServiceName = Encoding.UTF8.GetString(chars);
    remoteServiceName = remoteServiceName.Substring(0, remoteServiceName.IndexOf('\0'));
    }
    }
    }
    }
    ports.Add(new BluetoothComPort(sbid.ToString(), addr, name.ToString(), remoteServiceName));
    key.Dispose();
    }
    }
    }
    finally
    {
    NativeMethods.SetupDiDestroyDeviceInfoList(handle);
    }
    }
    return ports;
    }
    private string _deviceId;
    private long _bluetoothAddress;
    private string _portName;
    private string _remoteServiceName;
    internal BluetoothComPort(string deviceId, long bluetoothAddress, string portName, string remoteServiceName)
    {
    _deviceId = deviceId;
    _bluetoothAddress = bluetoothAddress;
    _portName = portName;
    _remoteServiceName = remoteServiceName;
    }
    public string DeviceId
    {
    get
    {
    return _deviceId;
    }
    }
    public long BluetoothAddress
    {
    get
    {
    return _bluetoothAddress;
    }
    }
    public string PortName
    {
    get
    {
    return _portName;
    }
    }
    public string RemoteServiceName
    {
    get
    {
    return _remoteServiceName;
    }
    }
    private static long GetBluetoothAddressFromDevicePath(string path)
    {
    if (path.StartsWith("BTHENUM"))
    {
    int start = path.LastIndexOf('&');
    int end = path.LastIndexOf('_');
    string addressString = path.Substring(start + 1, (end – start) – 1);
    // may return zero if it is an incoming port (we're not interested in these)
    return long.Parse(addressString, System.Globalization.NumberStyles.HexNumber);
    }
    // not a bluetooth port
    return long.MinValue;
    }
    private static class NativeMethods
    {
    // The SetupDiGetClassDevs function returns a handle to a device information set that contains requested device information elements for a local machine.
    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    internal static extern IntPtr SetupDiGetClassDevs(
    ref Guid classGuid,
    [MarshalAs(UnmanagedType.LPTStr)] string enumerator,
    IntPtr hwndParent,
    DIGCF flags);
    // The SetupDiEnumDeviceInfo function returns a SP_DEVINFO_DATA structure that specifies a device information element in a device information set.
    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool SetupDiEnumDeviceInfo(
    IntPtr deviceInfoSet,
    uint memberIndex,
    ref SP_DEVINFO_DATA deviceInfoData);
    // The SetupDiDestroyDeviceInfoList function deletes a device information set and frees all associated memory.
    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet);
    // The SetupDiGetDeviceInstanceId function retrieves the device instance ID that is associated with a device information element.
    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool SetupDiGetDeviceInstanceId(
    IntPtr deviceInfoSet,
    ref SP_DEVINFO_DATA deviceInfoData,
    System.Text.StringBuilder deviceInstanceId,
    int deviceInstanceIdSize,
    out int requiredSize);
    //static Guid GUID_DEVCLASS_BLUETOOTH = new Guid("{E0CBF06C-CD8B-4647-BB8A-263B43F0F974}");
    internal static Guid GUID_DEVCLASS_PORTS = new Guid("{4d36e978-e325-11ce-bfc1-08002be10318}");
    [DllImport("setupapi.dll", SetLastError = true)]
    internal static extern IntPtr SetupDiOpenDevRegKey(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData,
    DICS Scope, int HwProfile, DIREG KeyType, int samDesired);
    [Flags]
    internal enum DICS
    {
    GLOBAL = 0x00000001, // make change in all hardware profiles
    CONFIGSPECIFIC = 0x00000002, // make change in specified profile only
    }
    internal enum DIREG
    {
    DEV = 0x00000001, // Open/Create/Delete device key
    DRV = 0x00000002, // Open/Create/Delete driver key
    }
    // changes to follow.
    // SETUPAPI.H
    [Flags()]
    internal enum DIGCF
    {
    PRESENT = 0x00000002, // Return only devices that are currently present in a system.
    ALLCLASSES = 0x00000004, // Return a list of installed devices for all device setup classes or all device interface classes.
    PROFILE = 0x00000008, // Return only devices that are a part of the current hardware profile.
    }
    [StructLayout(LayoutKind.Sequential)]
    internal struct SP_DEVINFO_DATA
    {
    internal int cbSize; // = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
    internal Guid ClassGuid;
    internal uint DevInst;
    internal IntPtr Reserved;
    }
    }
    }
    }

    I’ve just updated it as I noticed an issue with the null termination of the port names so it is now correctly trimmed. The class is very simple to use – the following will write out all the configured ports to the debug console:-

    foreach (var p in BluetoothDiagnostics.BluetoothComPort.FindAll()) 
    {
       System.Diagnostics.Debug.WriteLine($"{p.PortName} {p.DeviceId} {p.BluetoothAddress:X6}"); 
    } 

    This will match the contents of the Control Panel shown above.

  • Asyncify a method and event response

    It’s common to come across a pattern where you call a synchronous method to kick off an activity then await an event which gives you the result (or an error). In the process of building 32feet‘s Bluetooth LE library this became a familiar sight both for Android and iOS APIs.

    The goal was to create a single async method which starts the process and completes when the response (good or bad) is received. The API model for the resulting cross-platform API is based on Web Bluetooth but with some extensions. The Web Bluetooth API is JavaScript based and each operation returns a promise – the .NET approach is to use a Task which you can await or ContinueWith.

    In an early implementation I had used a WaitHandle and had a thread blocking on this but this is not a clean async approach. I needed a solution which would handle the method and event handler and use a standard Task. Luckily there is the TaskCompletionSource type which can facilitate this. It wraps a Task and allows you to set a result (or exception) which we’ll be setting inside the event handler. Therefore each resulting method follows basically the pattern below:-

    TaskCompletionSource<IReadOnlyList<GattDescriptor>> tcs = new TaskCompletionSource<IReadOnlyList<GattDescriptor>>();
    CBPeripheral peripheral = Service.Device;
    
    void handler(object sender, CBCharacteristicEventArgs args)
    {
       peripheral.DiscoveredDescriptor -= handler;
    
       if (args.Error != null)
       {
          tcs.SetException(new Exception(args.Error.ToString()));
          return;
       }
    
       List<GattDescriptor> descriptors = new List<GattDescriptor>();
    
       foreach (CBDescriptor cbdescriptor in _characteristic.Descriptors)
       {
          descriptors.Add(new GattDescriptor(this, cbdescriptor));
       }
    
       tcs.SetResult(descriptors.AsReadOnly());
    }
    
    peripheral.DiscoveredDescriptor += handler;
    peripheral.DiscoverDescriptors(_characteristic);
    
    return tcs.Task;
    
    • Create a TaskCompletionSource with the required return type
    • Get the native object we’ll be starting the operation on
    • Create a handler for the event. Inside the handler we
      • Remove the event handler
      • Check the response – set either positive or negative result to TaskCompletionSource.
    • Add the event handler
    • Call the method to start the process
    • Return the TaskCompletionSource Task

    I hope this provides a handy pattern to follow if you’re trying to use similar APIs, if not I’m sure it will at least jog my memory. I’ll post again soon about the full Bluetooth library in more depth.

  • Changes coming in Xamarin Android BluetoothSocket

    While debugging an app I came across this new message in the Output window:-

    "Not wrapping exception of type Java.IO.IOException from method `Read`. This will change in a future release."

    When working with an RFComm service on Android you use a BluetoothSocket which in turn owns two Streams – one for Input and one for Output. Currently when there is a failure such as a broken connection the Read or Write operation will throw a Java.IO.IOException even though these streams are exposed as regular .NET Streams. It looks like Microsoft plan to change this behaviour and I presume this will be to wrap the exception in the equivalent System.IO.IOException. This makes a lot of sense and will improve consistency with other .NET flavours but it is something you need to be aware of as it will require code changes in your exception handling.

  • WPF Activity in Xamarin Forms

    WPF Activity in Xamarin Forms

    I’ve recently been working on extending an existing product written in Xamarin Forms to Windows desktop and this has meant learning a lot more about the WPF implementation. Originally Xamarin Forms was created for mobile platforms such as Android and iOS (and Windows Phone – more on that later) but has since expanded to desktop platforms such as macOS, GTK and WPF on Windows (UWP originally straddled mobile and desktop). WPF is a more recent addition to Xamarin Forms and so understandably the implementation is less mature. It does follow the native look and feel closely but I soon found quite a jarring omission.

    Our app uses the ActivityIndicator to represent indeterminate activity. On Android et al. this displays a rotating circular spinner but on WPF it was using an indeterminate progress bar squashed into a rectangular space which looked odd. This is understandable as there is no native control in WPF. I then had a flash of deja vu when thinking about something I wrote some time ago for Windows Phone!

    Windows Phone 8.1 introduced the WinRT application model (from Windows 8) alongside the existing Silverlight approach but there were some complications and some types of apps could only be written in Silverlight. I created a library called Charming which added some of the Windows 8 style APIs to the Silverlight app model. This was later expanded and became Pontoon. One of the APIs was a Silverlight ProgressRing control which looked just like the Windows 8 equivalent. The progress ring still regularly appears in the Windows shell and is implemented in UWP. Since Silverlight is a subset of WPF it was straightforward to port it into the Xamarin Forms WPF platform library and modify the renderer for ActivityIndicator. Voila! A nice native looking ActivityIndicator for Windows desktop apps. This has now been merged into the Xamarin Forms code and should appear in a preview release soon alongside the MediaElement that also got merged recently. Until it hits a stable release you can add it via InTheHand.Forms too.