This blog presents a Universal Windows App that connects to an embedded Bluetooth device over the Bluetooth Serial Profile using a genetic Bluetooth USB dongle. This app will run on both a Windows 10 IoT-Core devices such as a Raspberry PI2 and a Windows 10 Desktop. The blog covers in detail the key issues with pairing including where a passkey is required with a RPI2 as well as a Bluetooth primer.Update 1.2: One bug fix (was writing send text twice) and and one code improvement (menu returns DeviceInformation so no need to requery for it.)
Bluetooth supports peer to peer networking over a short range. Unlike a Wi-Fi network, Bluetooth connectivity is one to one. Two devices can connect and interact. For example, two devices may connect in a symmetric manner using the serial profile and pas raw data between them. Generally though, Bluetooth connectivity is asymmetric with one end generating a specific data format to be consumed at the other end. For example a mobile phone can implement the A2DP (Advanced Audio Distribution Profile) Profile and stream audio to a head set that implements the HSP (Headset) Profile OBEX (Object Exchange) is a symmetric connection between two devices as they exchange data between them.
The Microsoft IOT-Core Bluetooth Sample project uses the GATT (Generic Attribute Profile Profile). This provides profile discovery and description services for the Bluetooth Low Energy protocol.With this protocol simple low power devices can be connected at one end generating data to be consumed by a complex processor. For example, the simple end can be sensors in say, a health or sporing context, connected to a mobile phone or telemetry unit. In the sample, the Windows IoT Core device is connected to a GATT device that has 6 sensors:.
Contactless IR temperature sensor
Humidity Sensor
Gyroscope
Accelerometer
Magnetometer
Barometric pressure Sensor
This blog is only concerned with the more generic SPP (Serial Port Profile) which is based upon the RFCOMM profile.
Bluetooth is implemented as a driver and protocol stack. Each driver implements a protocol interface going upwards and makes use one or more protocol interfaces from drivers one or more levels below it. At the bottom is the Bluetooth hardware/radios. In the stack as below (an earlier version: circa Bluetooth version 1.1) SDP is the service discovery and RFCOMM is the basis of many profiles as it is a serial interface. Many profiles directly dig deeper than RFCOMM though. Bluetooth serial is SPP (Serial Port Profile) making direct use of RFCOMM.
An early version of the Bluetooth Driver Stack.
Bluetooth is currently version 4.x which includes many additional protocol, profiles and drivers. For example, the stack above does not have the BLE and GATT (V4.0) profiles as used the Microsoft IoT-Core Bluetooth sample. See the full Windows Bluetooth Stack for the later version of the stack. For this activity which only involves the SPP profile, which has been around since version 1.0, the stack above shows enough detail. Indeed the generic USB dongle used with the RPI2 is ten years old! Bluetooth Serial is an original, simple but basic Bluetooth profile.
For two Bluetooth devices to connect they have to be “paired”. Generally you set one end in discovery mode and the other end initiates the paring by selecting the first device from a list generated by the pairing utility. Simple pairing doesn’t require a passkey and the pairing will proceed upon selection. If a passkey is required, it may be fixed in which case its in the documentation (often 0000 or 000000 or 1234) or in-situa generated. If fixed there will be a popup on the initiator into which you enter the passkey. With some combinations of devices, a passkey appears on both and you just confirm on one that they match. For some combinations, such as a Bluetooth keyboard, the initator generates a passkey that you enter on the keyboard.
The default passkey for the Sparkfun Bluetooth Mate and the Freetronics Arduino Bluetooth shield is 1234.
Another option is where the MAC address of the target or device name is known, you might be able to programmatically or with a utility preconfigure the source device to be paired with the target. For example the btpair utility in Bluetooth Command Line Tools facilitates doing this on a Windows desktop. I have used it successfully to so do on a Windows 10 desktop.
btpair examples 1. Pair your computer and device Mac Address 00:01:02:03:FF:FF PIN code 1234 : btpair -b"00:01:02:03:FF:FF6" -p1234 2. Unpair all remembered devices: btpair -u
Some Bluetooth serial devices can be configured to be paired with a utility independent of the host device to which they are serially connected. Once configured, you have pairing between any hosts without further pairing configuration as the pairing is endemic to the Bluetooth modules. No Bluetooth stack is required on the host/s. For example (blast from the past) the EmbeddedBlue 500 (eb500) as used with the Parallax Basic Stamp boards and robots were so configurable. These have a native serial UART TTL level pins. Two such modules can be indelibly paired.
eb500 Bluetooth Serial Adapter
For Windows 10 IoT-Core you can pair two devices using the web portal ( http://<device ip address>:8080 administrator p@ssw0rd).
Unfortunately earlier versions of the OS (including the build at the the time of Windows 10 RTM, build 10256) did not support Passkey pairing with the web portal.
The IoT-Core Web Portal with some paired devices and one waiting to pair.
I can now report that latest build of the Windows 10 IoT-Core the web portal does support Passkey pairing.
On initiating the pairing this dialog shows.
Because the other end requires a passkey, this dialog shows. (This was not available in previous builds.) ..Yeah!
The Iot-Core Bluetooth sample project, the one using GATT, covers how to use an on-board utility (IoTBluetoothPairing.exe) to pair with or without a passkey, via an SSH shell or PowerShell: http://ms-iot.github.io/content/en-US/win10/samples/BLEGatt1.htm
A pairing activity with IoTBluetoothPairing utility
Before getting into the code for the Universal Windows Bluetooth Serial App, lets deal with the test part first. I normally use a loopback to test a serial functionality. If I’m only testing a a microprocessor’s UART then I would simply connect Tx and and Rx and perform some writes followed by reads. On an Arduino device this is pins 0 and 1. On the RPI 2, as discussed in my previous blog Win 10 IoT Core- Raspberry PI 2 Native Serial is Live! , its pins 8 and 10 for the RPI 2 .. Yes, the UART is now available with IoT-Core on the RPI2. For The BeagleBone Black its pins J4 and J5 We were using this loopback configuration (“hammering it”) when investigating the interrupt issue with Compact 2013 (see this blog: Windows Embedded Compact 2013- Interrupt Issue Solved.
Where the serial port to be tested has a complete RS232 interface (ie a DB9 socket)I use a DB plug with the RxTx cross over wires connected plus the typical handshaking crossovers. eg
DB-9 RS-232 Loopback Connection (TIA-574)Connect Pins:
2 to 3 (Rx to Tx)
1 to 4 to 6 (DCD to DTR to DSR)
7 to 8 to 9 (RTS to CTS to RI)
C2102 USB-Serial with Loopback Adapter (DB9)
If the serial transport is to be tested rather than just the port, then a loop back app is implemented on the other end of the pipe. As a test bed for the UW Bluetooth Serial Test app in this blog, the other end of the (Bluetooth) pipe is an Arduino (Uno) board. The Bluetooth adapter is connected to pins 0 and 1 on the Arduino board.See the following for information on setting this up. (You could also use the Freetronics (or similar) Bluetooth shield.):
Arduino Uno (Freetronics EtherTen) with Sparkfun Bluetooth Mate Gold as used as a Bluetooth Serial endpoint for RPI2 Bluetooth Serial testing.
The following is the sketch for the Arduino device (this project is in the repository):
// A serial loop back app // Past setup // Initiated by connected system // Note can be used with Bluetooth where the RxTx of the BT device // are connected to the TxRx of the Arduino board. // Need to disconnect BT adapter for programming though. void setup() { // My adapter is set to this BAUD 115200 // This is the Arduino to BT adapter port rate // Set when the BT adapter is in command mode. // Its NOT the BT rate which you can't set. Serial.begin(115200); delay(333); while (!Serial) ; delay(1000); Serial.println("The quick brown fox jumps over the lazy dog!"); }
The comments above clarify the conceptual misunderstanding that in setting the BAUD rate of a Bluetooth serial adapter, you are only setting the rate for the rate for the physical or USB serial port to the adapter; not the Bluetooth bit rate which is fixed.
And the loop code (simple eh!):
void loop() { char ch = Serial.read(); while ( 255 == (byte) ch) { ch = Serial.read(); } Serial.print(ch); }
This code just reads the serial in and ignores char(255) which means no input. When it gets a sent character it echoes it back.
As previously stated, I use an very old Bluetooth (V1.1) USB dongle plugged into a USB Host port on the RPI2. It shows as a Generic Bluetooth device. The dongle is of the CSR Chipset. I have been able to connect to:
With some devices, the connectivity did not work until the RPI2 was rebooted.
I’ll add more to this list later
My Lumia 930 phone would not pair with the RPI2 as, I guess, it has no Serial Profile nor HID Profile .. the RPI2 doesn’t support any Audio.
As per http://ms-iot.github.io/content/en-US/win10/Bluetooth.htm …
Windows IoT Core currently supports Bluetooth 4.0.
Windows IoT Core currently supports the following Bluetooth profiles:
Bluetooth Basic Rate (BR): HID
Bluetooth Basic Rate (BR): RFCOMM.
Bluetooth Low Energy (LE): GATT
This app was based upon three sources, but needed further development. The sources are:
The code in the previous blog is a UW IoT-Core app, running on a RPI2 that uses USB-Serial collect sensor information hosted on an Arduino device and also has a protocol built upon the serial interface to interact with a 2 line LCD display hosted on the Arduino device. The final outcome (later) of this activity will be using Bluetooth instead of USB-Serial
This app is a simple Bluetooth Serial Universal Windows (Windows 10) test app that enumerates (as a list) all devices Bluetooth paired to the device where the app is running. One is selected by double clicking which initiates connection. Text to send is entered in a Textbox and sent when a button is pressed. The app automatically receives any text sent to it ( and displays it). Hence when the endpoint (as above) simply echoes the the text its received. Tthe sent text should reappear in the reception box on the UW app. Of course, when the app is run Bluetooth must be enabled!
The app is created using in Visual Studio 2015 using the the a Universal Windows app template: New Project Visual C#->Windows->Blank App (Universal Windows)
The UI is implemented in XAML:
The Universal Windows App UI
You may prefer to layout the UI using Relative StackPanels or use Grid rows and columns etc.
The ConnectDevices is a ListBox with a Binding Source set to PairedDevices(see later) and an ItemTemplate consisting of a TextBlock bound to the Name property of PairedDevices (see later).
<ListBox x:Name="ConnectDevices" ItemsSource="{Binding Source={StaticResource PairedDevices}}" Margin="10" DoubleTapped="ConnectDevices_DoubleTapped" Width="Auto" Height="Auto"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
The app will need some specific capabilities for Bluetooth and Serial added to Package.appxmanifest.Unfortunately the UI you get for this if you double click on that file in Solution Explorer doesn’t support adding these capabilities and so you need to add them manually by right clicking on the file in Solution Explorer and select Open With->XML Editor.
Modify the capabilities section (at the bottom) to:
<Capabilities> <Capability Name="internetClient" /> <DeviceCapability Name="bluetooth.rfcomm"> <Device Id="any"> <Function Type="name:serialPort"/> </Device> </DeviceCapability> </Capabilities>
The internetClient capability will already be there. Its not needed for this app so you may wish to remove it. I leave it in.
At the app start the paired devices are enumerated using:
DeviceInformationCollection DeviceInfoCollection = await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));
This collection is then turned into an ObservableCollection that it can be used in the ListBox:
_pairedDevices = new ObservableCollection<PairedDeviceInfo>();
The DeviceInfoCollection is iterated through creating a PairedDeviceInfo object for each item which is then added to the _pairedDevices collection.
PairedDeviceInfo is a class with properties:
Name <string>
Id <string>
DeviceInfo <DeviceInformation>
The class has an appropriate constructor.
DeviceInformation is Windows.Devices.Enumeration.DeviceInformation
In the listbox of paired devices, the name field is (bound) displayed.
When a device is selected (double clicked) the Device Id is used to retrieve the full DeviceInformation (again!) the endpoint’s DeviceInformation is actually part of the selected menu item.
ToDo: This code should be improved because we already have the DeviceInformation! (??) Fixed Update1.2
This is then used to connect to the endpoint device:
The DeviceInformation is used to get the RFCOMM service. The connection is then established as a StreamSocket using the Endpoint HostName (actually its Bluetooth Mac Address) and the ServiceName string.
_service = await RfcommDeviceService.FromIdAsync(DeviceInfo.Id); _socket = new StreamSocket(); await _socket.ConnectAsync(_service.ConnectionHostName, _service.ConnectionServiceName);
If all is well then we have a connection .. the socket will be meaningful (not null).Of course this is error trapped (try-catch)
Send text and Receive text make use of the socket’s InputStream and OutputStream properties.
Receiving text should be started first as it runs as a separate thread. It runs an async wait to get serial input, handles the input then runs another async serial receive .. all in a loop. This loops continuous until a cancellation is generated.
private async void Listen() { ReadCancellationTokenSource = new CancellationTokenSource(); if (_socket.InputStream != null) { dataReaderObject = new DataReader(_socket.InputStream); // keep reading the serial input while (true) { await ReadAsync(ReadCancellationTokenSource.Token); } } }
DataReader dataReaderObject;private CancellationTokenSource ReadCancellationTokenSource; private async Task ReadAsync(CancellationToken cancellationToken) { uint ReadBufferLength = 1024; // If task cancellation was requested, comply cancellationToken.ThrowIfCancellationRequested(); // Set InputStreamOptions to complete the asynchronous read operation when one or more bytes is available dataReaderObject.InputStreamOptions = InputStreamOptions.Partial; // Create a task object to wait for data on the serialPort.InputStream loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken); // Launch the task and wait UInt32 bytesRead = await loadAsyncTask; if (bytesRead > 0) { string recvdtxt = dataReaderObject.ReadString(bytesRead); } }
Whilst the received text is performed asynchronously as the arrival of text is nondeterministic from the app’s perspective, sent text is actioned from the app’s UI and so is essentially synchronous (although a await is used to free up the UI).
private async void Send(string msg) { Task<UInt32> storeAsyncTask; DataWriter dataWriteObject = new DataWriter(_socket.OutputStream); dataWriteObject.WriteString(msg); // Launch an async task to complete the write operation storeAsyncTask = dataWriteObject.StoreAsync().AsTask(); UInt32 bytesWritten = await storeAsyncTask; }
The command buttons are enabled and disabled depending upon the state of the app. For example Send and Start Recv buttons are disabled when the app isn’t connected but become enabled when the app is connected, etc.
Enjoy.
Please leave comments
Hi David, i am getting list of paired devices in http://minwinpc:8080/bluetooth.htm, but not able to get devices in app, already added device capabilities in config file, DeviceInformationCollection DeviceInfoCollection = await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort)); var numDevices = DeviceInfoCollection.Count(); here i get numDevices = 0 please help me.