First of all, happy Pi Day! Using the standard United States date format, today is 3/14/15, which makes it the most significant Pi Day this century.
Anyway, I've spent the past few hours tinkering with the Microsoft Band SDK Preview, and although I think it's unfortunate that there's no SDK for writing apps which actually run on the Band itself, you can still accomplish some pretty cool things with the phone SDK. Unfortunately, this being a preview release, the documentation is a bit sparse, and I had some trouble figuring out how to get things working. Thus, I wanted to share some of what I've learned. If you prefer to dive right into the code, I've got a small C# sample application on GitHub.
Standard disclaimer: although I am a Microsoft employee, everything I am posting here and on GitHub is my own personal work done on my own time. None of it should be considered as officially representative of Microsoft in any respect. While I will do my best to answer any questions I can related to the Band SDK Preview, I cannot provide official support for this or any other Microsoft product.
Let's get started. If you're following
the documentation, the first
few steps describe how to connect to the Band, as one would expect. The sample code provided creates
a connection within a using
block. This ensures that the connection is disposed of as soon as
control leaves the block, which is sensible since maintaing a connection to the Band, especially
when subscribed to multiple sensors, can severely impact its battery life. However, this approach
can also be a bit misleading, because if you do want to collect data from the Band over some
interval of time, you will only be able to do so during the lifetime of the IBandClient
returned
by ConnectAsync
. As soon as the IBandClient
is disposed of, your connection will be closed and
you will no longer receive sensor data.
Since you'll typically only be connected to one Band at a time, the easiest way to solve this
problem is to store the IBandClient
as a static member variable of your Application
class.
(Note: I don't guarantee that this is the optimal solution. I am by no means an expert here.) This
also makes it easy to access your Band client from different pages within your application, and to
unsubscribe from sensor data when you no longer need it so that you won't drain the Band's
battery.
[Updated March 17, 2015: In my original post, I was missing a call to Dispose()
before setting
the IBandClient
member to null
. As Phil pointed out in the comments, this is a bad practice,
because garbage collection of the IBandClient
may not happen immediately, in which case the
connection to the Band would be left open for some time. During that time, other applications would
be prevented from connecting to the Band. I have corrected this error in my code below, and in my
project on GitHub. If you are only connecting to the Band for a short time, you can use a using
block instead, as discussed above, which will ensure that Dispose()
is called for you. Thanks to
Phil for the correction.]
// App.xaml.cs
using Microsoft.Band;
namespace BandDemo
{
public sealed partial class App : Application
{
public static IBandClient BandClient
{
get; set;
}
// ...
private async void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
if(App.BandClient != null)
{
// Unsubscribe from sensor data so that we don't drain the Band's battery when
// the app is not in use.
await App.BandClient.SensorManager.Accelerometer.StopReadingsAsync();
await App.BandClient.SensorManager.Gyroscope.StopReadingsAsync();
await App.BandClient.SensorManager.HeartRate.StopReadingsAsync();
// Call Dispose to close the connect to the Band
App.BandClient.Dispose();
// We are done with this client, so assign null to the member variable so the
// IBandClient can be garbage collected
App.BandClient = null;
}
deferral.Complete();
}
}
}
// MainPage.xaml.cs
using Microsoft.Band;
using Microsoft.Band.Sensors;
namespace BandDemo
{
public sealed partial class MainPage : Page
{
// ...
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
IBandInfo[] pairedBands = await BandClientManager.Instance.GetBandsAsync();
try
{
App.BandClient = await BandClientManager.Instance.ConnectAsync(pairedBands[0]);
// Do work after successful connect
System.Diagnostics.Debug.WriteLine("Connected!");
}
catch(Exception)
{
System.Diagnostics.Debug.WriteLine("Connection failed!");
}
}
}
}
The next thing you'll notice in the documentation is that it recommends that you query the device for available reporting intervals for each sensor. It's fairly easy to miss that setting the reporting interval is completely optional; you can feel free to skip this step unless you really need fine-grained control over how frequently you get updates.
Finally, the documentation describes how to subscribe to sensor data by adding your callback to a
sensor's ReadingChanged
property. When your callback is executed, you might want to update your
app's UI to indicate the new reading (indeed, the documentation itself suggests this). However, this
is another gotcha because in Windows (Phone) Store apps, "all UI components share the same [UI]
thread," (reference), and
methods on this thread cannot be directly invoked by background threads. If you try to do so, an
exception will be raised. This will of course be no surprise to those of you already familiar with
Store apps (or even with .NET circa 2006), but for the rest of us, it can be a bit confusing.
Luckily, the solution is simple: you just need to use Dispatcher.RunAsync
to do your UI updates.
So, putting it all together, here's how you subscribe to sensor data and update the UI when it
changes:
App.BandClient.SensorManager.Accelerometer.ReadingChanged += async (sender, args) =>
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
AccelerometerXValue.Text = args.SensorReading.AccelerationX.ToString("###00.00");
AccelerometerYValue.Text = args.SensorReading.AccelerationY.ToString("###00.00");
AccelerometerZValue.Text = args.SensorReading.AccelerationZ.ToString("###00.00");
System.Diagnostics.Debug.WriteLine("Got accelerometer event!");
});
};
await App.BandClient.SensorManager.Accelerometer.StartReadingsAsync();
That's it for now! If you have any questions, leave a comment and I'll do my best to answer them.