Today we are delving into the details of a basic and widely used service. We may well come back and revisit the lower layers, but this topic will look at something you are likely to use when doing Bluetooth Classic (not Low Energy) development.
Cutting the Cable
One of the early aims for Bluetooth was to provide a wireless alternative to the humble Serial Port. Physically a serial port is very basic and allows two-way communication with just a few wires. Over the years faster speeds have been introduced but otherwise it’s the same tech which powers lots of legacy devices such as (if you can remember them) wonderful squawking dial up modems!
The Bluetooth protocol stack has a number of layers and the one which is commonly accessible to developers is called RFComm (Radio Frequency Communication) and atop this sits a number of standard services which you can implement. The simplest is Serial Port service which has the short id 0x1101. This defines a basic data connection which behaves like a traditional serial port. You can connect to the serial port service on any device (usually you’ll need to have paired with the device first) and exchange data. The Serial Port service doesn’t define what that data should be – you have to know what the remote device is and what to expect.
Lots of common devices expose just the Serial Port service and will provide their own documentation on protocols etc supported. For example, several Bluetooth printers use Serial Port but you have to know the device specific printing language to correctly print anything. Some may have a binary protocol, others something text based. When we look into some of the telephony profiles, we’ll see that they are often based around the AT commands which were used to talk to phones and modems although these use specific services like Headset and Hands Free and you can use the relevant specifications to understand the commands and responses.
A device can technically advertise multiple services with the same service UUID, but thankfully this is rare. This can make it tricky when requesting a service by UUID because you’ll generally get the channel number of the first service (which hopefully is the desired one). You’d need to do a more complex SDP lookup to see the differences and decide which to use. SDP records include a name so this can be used to differentiate. Some devices have multiple ways of interacting using different services – many barcode scanners support both Serial Port Profile for low-level control and also HID (Human Interface Device) to appear to the device as a standard keyboard. While the channel id should remain fixed for the lifetime of the device (until powered down/rebooted) we can’t assume it will always be the same and so should request by UUID and not channel number.
On desktop PCs there can be quite a lot of old software which is still used long after we expect. It can be too risky to update legacy software to change from a world of COM ports to Bluetooth devices. Therefore, Windows has supported the use of Virtual COM ports. There are only specific scenarios where this makes sense, and you absolutely shouldn’t be considering using them just because you are using Serial Port Profile.
A virtual serial port is an extra layer of software on top of the normal RFComm connection which exposes itself on your PC as a regular COM port. Therefore, when you have a legacy program which uses a COM port for a GPS, measuring scale or any similar device you can tell the software to use this COM port and it will be oblivious to the actual connection method. You can get Bluetooth to Serial adaptors which allow you to connect a legacy RS-232 device and then advertise Serial Port service. Many of us will have one of these in our box of cables and adaptors which we keep “just in case”.
In Windows 10/11 the virtual COM ports are a little more hidden as they are part of the old Control Panel and not in the modern Settings app. Go to Bluetooth & Devices, scroll down to More Bluetooth settings and then select the COM ports tab. You can add an outgoing port – assigning a virtual COM port to a specific remote device, or an incoming port – creating a local serial port service which other devices can connect to.
In the next post we’ll look in a bit more detail at writing code to talk to a device.