Nico's digital footprint

I grew up in the nineties, that makes me awesome by default

WP8 as bluetooth Start button

by Nico

If you’ve ever used applications like the Office Remote that seem to take over your computer you’ll know that it can provide quite the “magic” experience. As with all magic technologies this one is quite simple as well. All we need is a computer with Bluetooth that runs a small app with a Bluetooth listener and a Windows Phone app that connects to that listener app via Bluetooth. For this demo app I’m going to make the Windows Phone app act like the Start button on a Windows machine.

Simulating the Windows key

The listener application is a small WPF app that has only one button. That button will act like the Start button. To interact with the operating system and simulate a keyboard press we’ll need to import user32.dll and hook into its keybd_event. We’ll want to send ctrl + esc to Windows (acts the same as the Windows key)

Code Snippet
  1. //source: http://www.codeproject.com/Questions/46731/Open-Start-Menu
  2. private static void ShowStartMenu()
  3. {
  4.     // key down event:
  5.     const byte keyControl = 0x11;
  6.     const byte keyEscape = 0x1B;
  7.     keybd_event(keyControl, 0, 0, UIntPtr.Zero);
  8.     keybd_event(keyEscape, 0, 0, UIntPtr.Zero);
  9.  
  10.     // key up event:
  11.     const uint keyeventfKeyup = 0x02;
  12.     keybd_event(keyControl, 0, keyeventfKeyup, UIntPtr.Zero);
  13.     keybd_event(keyEscape, 0, keyeventfKeyup, UIntPtr.Zero);
  14. }
  15.  
  16. [DllImport("user32.dll")]
  17. static extern void keybd_event(byte bVk, byte bScan, uint dwFlags,
  18.    UIntPtr dwExtraInfo);

In the event handler of the button we call ShowStartMenu() and behold: a wild software Windows key appears!

Building the Bluetooth listener

Adding Bluetooth support to a .NET application isn’t the easiest thing to do. Luckily for us there are libraries out there that take out the hassle. For this project I used Peter Foot’s 32feet.NET library available through NuGet

Install-Package 32feet.NET

A small note on this package as found on the 32feet.NET product page

“Bluetooth support requires a device with the Microsoft or Widcomm Bluetooth stack”

Luckily for me, my device uses the Microsoft BT stack, so I’m good to go. Now on to the code, we’ll need to start a BT listener and once connected read data from a networkstream. If we receive a certain message we’ll execute the ShowStartMenu() method.

First some fields

Code Snippet
  1. private BluetoothListener _listener;
  2.  
  3. //unique service identifier
  4. private readonly Guid _service = new Guid("{7A51FDC2-FDDF-4c9b-AFFC-98BCD91BF93B}");
  5. private bool _connected;

BluetoothListener is a class we get from 32feet.NET. The GUID is a reference to the service this app is providing, our Windows Phone app will need the same GUID to be able to connect to this service. And the third field is a boolean to keep track of having a connected client or not.

Code Snippet
  1. private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
  2. {
  3.     BluetoothRadio br = BluetoothRadio.PrimaryRadio;
  4.  
  5.     if (br == null)
  6.     {
  7.         MessageBox.Show("No supported Bluetooth radio/stack found.");
  8.     }
  9.     else if (br.Mode != RadioMode.Discoverable)
  10.     {
  11.         MessageBoxResult rslt = MessageBox.Show("Make BluetoothRadio Discoverable?",
  12.             "Bluetooth Remote Listener", MessageBoxButton.YesNo);
  13.  
  14.         if (rslt == MessageBoxResult.Yes)
  15.         {
  16.             br.Mode = RadioMode.Discoverable;
  17.         }
  18.     }
  19.  
  20.     _listener = new BluetoothListener(_service);
  21.     _listener.Start();
  22.  
  23.     System.Threading.Thread t = new System.Threading.Thread(ListenLoop);
  24.     t.Start();
  25. }

In the OnLoaded event from the MainWindow we’ll first check if there’s a BT radio found that works on the MS or Widcomm stack. If that’s found, and made discoverable, we’ll start the listener with the service GUID specified in the field. Once the listener is started we fire up a second thread that will contain a loop to connect a client and read data from the stream.

Code Snippet
  1. private void ListenLoop()
  2. {
  3.     byte[] buffer = new byte[1024];
  4.  
  5.     while (true)
  6.     {
  7.         BluetoothClient bc;
  8.         Stream ns;
  9.  
  10.         try
  11.         {
  12.             bc = _listener.AcceptBluetoothClient();
  13.             ns = bc.GetStream();
  14.             _connected = true;
  15.         }
  16.         catch
  17.         {
  18.             break;
  19.         }
  20.  
  21.         //keep connection open
  22.         while (_connected)
  23.         {
  24.             try
  25.             {
  26.                 int received = ns.Read(buffer, 0, buffer.Length);
  27.  
  28.                 if (received > 0)
  29.                 {
  30.                     string data = Encoding.UTF8.GetString(buffer);
  31.  
  32.                     string command = data.Substring(0, data.IndexOf("\0", StringComparison.Ordinal));
  33.  
  34.                     if (command == "Start")
  35.                     {
  36.                         ShowStartMenu();
  37.                     }
  38.                 }
  39.                 else
  40.                 {
  41.                     //connection lost
  42.                     _connected = false;
  43.                 }
  44.             }
  45.             catch
  46.             {
  47.                 _connected = false;
  48.                 break;
  49.             }
  50.         }
  51.  
  52.         try
  53.         {
  54.             bc.Close();
  55.         }
  56.         catch
  57.         {
  58.         }
  59.     }
  60. }

This is the code for the loop that runs in the second thread. First we declare a buffer of 1KB, more than enough for this sample. Next we start an infinite loop, in that loop we declare a BluetoothClient and a Stream. Once a client connects through the AcceptBluetoothClient method we fetch a stream and set the connected flag to true. As long as we’re connected we read data from the stream into the buffer, once we receive actual data we’ll decode it from a byte array (the buffer) into a string (the original message). In this sample the message will be 5 bytes but the buffer can hold 1024 bytes. It will fill up the remaining bytes with “\0” so we’ll need to chop that of with a Substring. If the received string is “Start” we’ll flip to the Start screen. If we read from the stream but the number of received bytes is zero, we’ve lost the connection to the client.

Fire up the WPF app, make sure the button flips you to the Start screen and let’s get started on the WP8 app!

Windows Phone Client

The Windows Phone app consists of two pages. The MainPage is for selecting a Bluetooth peer to connect to and the ConnectedPage has the same button as the WPF application. The app uses MVVM Light, everything happens in the MainViewModel. Both the MainPage and ConnectedPage use MainViewModel as their datacontext. For navigation I’ve added the always helpful Cimbalino NuGet package and registered their NavigationService in SimpleIoc through the ViewModelLocator

Code Snippet
  1. public ViewModelLocator()
  2. {
  3.     ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
  4.  
  5.     if (ViewModelBase.IsInDesignModeStatic)
  6.     {
  7.         // Create design time view services and models
  8.     }
  9.     else
  10.     {
  11.         // Create run time view services and models
  12.         SimpleIoc.Default.Register<INavigationService, NavigationService>();
  13.     }
  14.  
  15.     SimpleIoc.Default.Register<MainViewModel>();
  16. }

In the MainViewModel I have declared three properties

Code Snippet
  1. private ObservableCollection<PeerInformation> _peers;
  2. private PeerInformation _selectedPeer;
  3. private ICommand _goToStartCommand;
  4.  
  5. public ICommand GoToStartCommand
  6. {
  7.     get { return _goToStartCommand ?? (_goToStartCommand = new RelayCommand(SendCommand)); }
  8. }
  9.  
  10. public ObservableCollection<PeerInformation> Peers
  11. {
  12.     get { return _peers; }
  13.     set
  14.     {
  15.         if (_peers == value) return;
  16.  
  17.         _peers = value;
  18.         RaisePropertyChanged(() => Peers);
  19.     }
  20. }
  21.  
  22. public PeerInformation SelectedPeer
  23. {
  24.     get { return _selectedPeer; }
  25.     set
  26.     {
  27.         if (_selectedPeer == value) return;
  28.  
  29.         _selectedPeer = value;
  30.         RaisePropertyChanged(() => SelectedPeer);
  31.  
  32.         ConnectToDevice();
  33.     }
  34. }

The PeerInformation class is something we get from the Windows Phone Bluetooth API. We have an observablecollection that will hold all peers that are known for your Windows Phone device, we also have a property for the selected peer and a command that will fire a message into the NetworkStream.

We’ll also need some fields

Code Snippet
  1. private StreamSocket _socket;
  2. private DataWriter _dataWriter;
  3.  
  4. private readonly INavigationService _navigationService;

The socket will hold the connection between the phone app and the WPF app. The DataWriter can write data into the stream. The NavigationService is the one from the Cimbalino toolkit.

Here’s the constructor for the MainViewModel

Code Snippet
  1. public MainViewModel(INavigationService navigationService)
  2. {
  3.     if (IsInDesignModeStatic) return;
  4.  
  5.     _navigationService = navigationService;
  6.  
  7.     if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Emulator)
  8.     {
  9.         MessageBox.Show("Bluetooth not available in the emulator");
  10.     }
  11.     else
  12.     {
  13.         RefreshPairedDevicesList();
  14.     }
  15. }

The NavigationService is injected via SimpleIoc. First we check if the current device is the Windows Phone emulator, the emulator doesn’t support Bluetooth so we need an actual device to test this on.

The RefreshPairedDevicesList looks like this (all using the Windows Phone Bluetooth APIs)

Code Snippet
  1. private async void RefreshPairedDevicesList()
  2. {
  3.     try
  4.     {
  5.         // Search for all paired devices
  6.         PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";
  7.         var peers = await PeerFinder.FindAllPeersAsync();
  8.  
  9.         Peers = new ObservableCollection<PeerInformation>(peers);
  10.     }
  11.     catch (Exception ex)
  12.     {
  13.         if ((uint)ex.HResult == 0x8007048F)
  14.         {
  15.             if (MessageBox.Show("Bluetooth is off, enable it?",
  16.                 "Bluetooth Off", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
  17.             {
  18.                 ConnectionSettingsTask connectionSettingsTask = new ConnectionSettingsTask
  19.                 {
  20.                     ConnectionSettingsType = ConnectionSettingsType.Bluetooth
  21.                 };
  22.                 connectionSettingsTask.Show();
  23.                 RefreshPairedDevicesList();
  24.             }
  25.         }
  26.         else
  27.         {
  28.             MessageBox.Show(ex.Message);
  29.         }
  30.     }
  31. }

First we’ll look for all paired devices for our phone, those get pushed into the observablecollection. If that fails with result 0x8007048F that means that Bluetooth is disabled on the device, in that case we use the ConnectionSettingsTask to redirect the user to the Bluetooth settings page in WP8.

After the list of peers is build, the user will be able to select one (I’ll discuss the pages in a minute). When a peer is selected we open up the Bluetooth connection

Code Snippet
  1. private async void ConnectToDevice()
  2. {
  3.     if (_socket != null)
  4.     {
  5.         // Disposing the socket with close it and release all resources associated with the socket
  6.         _socket.Dispose();
  7.     }
  8.  
  9.     try
  10.     {
  11.         _socket = new StreamSocket();
  12.  
  13.         //connect to the service with specified GUID
  14.         await _socket.ConnectAsync(SelectedPeer.HostName, "{7A51FDC2-FDDF-4c9b-AFFC-98BCD91BF93B}");
  15.  
  16.         // Connection was successful
  17.         _dataWriter = new DataWriter(_socket.OutputStream);
  18.         _navigationService.NavigateTo("/ConnectedPage.xaml");
  19.     }
  20.     catch (Exception ex)
  21.     {
  22.         MessageBox.Show(ex.Message);
  23.  
  24.         _socket.Dispose();
  25.         _socket = null;
  26.     }
  27. }

The ConnectAsync method takes in the hostname of the peer and the same GUID we used in the WPF application to start the service. Once the connection is made we take the OutputStream and use it to initialize the DataWriter and we navigate to the ConnectedPage.

Let’s have a quick look at the MainPage xaml.

Code Snippet
  1. <phone:LongListSelector ItemsSource="{Binding Peers}">
  2.     <phone:LongListSelector.ItemTemplate>
  3.         <DataTemplate>
  4.             <TextBlock Margin="12, 0, 0, 12"
  5.                        Style="{StaticResource PhoneTextTitle2Style}"
  6.                        Text="{Binding DisplayName}" />
  7.         </DataTemplate>
  8.     </phone:LongListSelector.ItemTemplate>
  9.     <i:Interaction.Behaviors>
  10.         <behaviors:SelectedItemBehavior SelectedItem="{Binding SelectedPeer, Mode=TwoWay}" />
  11.     </i:Interaction.Behaviors>
  12.  
  13. </phone:LongListSelector>

The MainPage consists of a LongListSelector with an ItemsSource bound to the observableCollection on the MainViewModel. as a DataContext we show the displaynames of all available peers.

NOTE: LongListSelector does not support SelectedItem binding. I’ve added a behavior to this project that gives us just that. For more information on the behavior see my blogpost from a while ago.

The ConnectedPage only has a button with a command bound to the GoToStartCommand on MainViewModel. Once that command is fired we send a string “Start” on to the stream.

Code Snippet
  1. private async void SendCommand()
  2. {
  3.     _dataWriter.WriteString("Start");
  4.     await _dataWriter.StoreAsync();
  5.     await _dataWriter.FlushAsync();
  6. }

And that’s it! The message gets written into the DataWriter, stored in the stream and the stream gets flushed. In the WPF app the string gets read into the buffer as a byte array, gets converted back into a string and the Start screen appears!

Wrap-up

In this post I’ve discussed how to connect your Windows Phone device to another bluetooth enabled device and use a service running there.

Happy coding!


Tags:

.Net | Devices | Bluetooth | MVVM Light | WP8 | WPF | XAML | Windows programming

blog comments powered by Disqus
  Log in

About the author

Hi,

My name is Nico, I’m an MVP Windows Platform Development living in Belgium.
I’m currently employed as a .NET consultant at RealDolmen, one of Belgium’s leading IT single source providers.

I'm also founding member and board member of the Belgian Metro App Developer Network, a user group focussed on Windows 8 and Windows Phone development. If you're in Belgium feel free to drop by if we're doing an event. http://www.madn.be

Since June 2012 I'm a proud member of Microsoft's Extended Experts Team Belgium. And in February 2013 I became a member of DZone's Most Valuable Bloggers family.

In 2013 I became a book author and wrote "Windows 8 app projects, XAML & C# edition".

In 2014 I received the MVP award for the very first time.

I hope to get feedback from my readers either through comments, mail (nico_vermeir@hotmail.com), twitter, facebook, …

 

mvp

 

mvp

 

 

MeetLogo

 

MVBLogo

mybook

 

Month List