Showing posts with label Microsoft Band. Show all posts
Showing posts with label Microsoft Band. Show all posts

30 September 2015

A Microsoft Band client to read temperatures and control a fan on a Raspberry PI2

Part 6 of Reading temperatures & controlling a fan with a RP2, Azure Service Bus and a Microsoft Band

Intro
In the final post of this series I will show how the Microsoft Band client in this project, that is used to actually read temperatures and control the fan, is created and operated. The client has a tile and a two-page UI, looks like this and can be seen in action in this video from the first blog post:

To recap - the function of the Band client is as follows

  • WhenI tap the tile on the Band, the UI is opened and shows the latest data, that is:
    • The temperature as last measure by the Raspberry PI2
    • A button I can use to toggle the fan. The text on the button reflects the current status of the fan (i.e. it shows "Stop fan" when it's running, and "Start fan" when it is not
    • Date and time of the last received data.

The classes involved
As you can see in the demo solution, there are three classes, and one interface, all living in the TemperatureReader.ClientApp's Models namespace - that have something to do with the Band:

  • BandOperator
  • BandUiController
  • BandUiDefinitions
  • IBandOperator

I showed a little bit of it in the previous post that described the Windows 10 UWP app itself, but to recap: the app is put together using dependency injection, so every class gets everything (well, almost everything) it needs passed via the constructor, and only knows what it gets via an interface (not a concrete class). In addition, most communication goes via events.

Start it up
If you look in the MainViewModel's CreateInstance method, you will see the BandOperator first spring into life:

public static MainViewModel CreateNew()
{
  var fanStatusPoster = new FanSwitchQueueClient(QueueMode.Send);
  fanStatusPoster.Start();
  var listener = new TemperatureListener();
  var errorLogger = SimpleIoc.Default.GetInstance<IErrorLogger>();
  var messageDisplayer = SimpleIoc.Default.GetInstance<IMessageDisplayer>();
  var bandOperator = new BandOperator(fanStatusPoster);
  return (new MainViewModel(
    listener, bandOperator, 
    messageDisplayer, errorLogger));
}

I grayed out the stuff that is not so important here, but you see the BandOperator uses the FanSwitchQueueClient (see explanation in this post) to defer sending data on Azure Service bus to, and then it's passed to the MainViewModel. Apparently some other mechanism is used to deliver temperature data from the Azure Service bus, and that code is found in the Start method of MainViewModel:

private async Task Start()
{
  IsBusy = true;
  await Task.Delay(1);
  _listener.OnTemperatureDataReceived += Listener_OnTemperatureDataReceived;
  _listener.OnTemperatureDataReceived += _bandOperator.HandleNewTemperature;
  await _listener.Start();
  await StartBackgroundSession();
  await _bandOperator.Start();
  await _bandOperator.SendVibrate();
  IsBusy = false;
}

You can see the listener - being a ITemperatureListener, see also this post - simply passes the event to the BandOperator, starts it, and makes the Band vibrate. So you see - the actual code that operates the Band is very loosely coupled to the rest of the app. But what you also see - the Band does not listen to the data coming form the Azure Service Bus, nor does it send data. That app does that interaction, and the Band - in turn - interacts with the app.

Some definitions first
What is important to understand is that the UI lives on the Band, an remains there. Changing it, and responding to events, is essentially a process that runs on your phone, not on the Band, and you are interacting with a process that sends data over Bluetooth - but that is mostly abstracted away. Building a Band UI is also vastly different from what you are used to, using XAML. You are basically writing code to build the structure of the UI, then fill it with data using even more code. There is no such things as data binding. All UI elements have to be defined using unique identifiers - no such things as easily recognizable names. It harkens back to ye olden days from even before Visual Basic.

Anyway, there is this separate definition class that contains all the necessary ids:

using System;

namespace TemperatureReader.ClientApp.Models
{
  public static class BandUiDefinitions
  {
    public static readonly Guid TileId = 
      new Guid("567FF10C-E373-4AEC-85B4-EF30EE294174");
    public static readonly Guid Page1Id = 
      new Guid("7E494E17-B498-4610-A6A6-3D0C3AF20226");
    public static readonly Guid Page2Id = 
      new Guid("BB4EB700-A57B-4B8E-983B-72974A98D19E");

    public const short IconId = 1;
    public const short ButtonToggleFanId = 2;
    public const short TextTemperatureId = 3;
    public const short TextTimeId = 4;
    public const short TextDateId = 5;
  }
}

So we have three main UI ids - the tile, and both 'pages', that need to have a GUID. I just generated a few using Visual Studio, what GUID you use does not really matter - they need to be different from each other and refrain from re-using them - even over projects. Then there's five user interface elements.

  • On the first page - the one you see when you tap the tile: the thermometer icon, the button to turn the fan on or off (also used to show the current fan status), and the label that shows the temperature as measured by the Raspberry PI2
  • On the second page two label fields, that show the time and the date of the last received update from the Azure Service Bus as received by the App.

The UI looks like this:

IMG_2288
The tile that you can tap

IMG_2289
The first page, with icon, temperature reading and button. The button now says "Start fan", so apparently the app has already received date from the Raspberry PI2, and it indicated the fan is off. Notice a little part of the second page is already visible on the right, alerting the user there's more to be seen and encouraging him to scroll to the right - a UI pattern in use since the very early days of Windows Phone 7.

IMG_2290

The second page (with date and time of last received data)

The user interface element ids are just just integers, but I make them globally unique - that is, unique in the app. The fact that they are spread over two 'pages' comes from the fact that the Band has a very small display, and you will need to make use of it's multi-page features if you want to show anything but the most trivial data. Fortunately, once you understand how it works, that is not very hard to do.

Building the Band client UI
I have separated the actual operating building and manipulating of the Band UI from the 'business logic' concerning the interaction with events coming from the Azure Service Bus. So we have the BandUiController and the BandOperator. A crude and not completely correct analogy could define the BandUIController as the view, and the BandOperator as a kind-of-viewmodel. I did this because at one point I had a class approaching 300 lines and things got very confusing. So I split it up. I show only a little excerpt of the BanOperator before I start explaining the BandUIController first.

The BandUIController needs access to a IBandClient to be able to work on the Band's UI. You need to retrieve one first. How this works, you can see in the BandOperator's GetNewBandClient method:

var pairedBands = await BandClientManager.Instance.GetBandsAsync();
if (pairedBands != null && pairedBands.Any())
{
  return await BandClientManager.Instance.ConnectAsync(pairedBands.First());
}

And this IBandClient is injected into the BandUIController via the constructor. We have seen this pattern before in this series

public class BandUiController
{
  private readonly IBandClient _bandClient;

  public BandUiController(IBandClient bandClient)
  {
    _bandClient = bandClient;
  }
}

The next important thing to understand is that although both occur from code, defining the Band interface and actually displaying stuff in it are two separate actions. The public interface of the BandUIController actually only has four methods - and one of them is an overload of another:

public async Task<bool> BuildTile();
public async Task RemoveTile();
public async Task SetUiValues(string timeText, string dateText, 
                              string temperature, string buttonText);
public async Task SetUiValues(string temperature, string buttonText);

The first one builds the tile (and the rest of the UI), the second one removes it. The third one sets all the UI elements' value, the second one only that of the elements on the first page - that is used for when you press the button to switch the fan on or off. So let's have a look at BuildTile first.

public async Task<bool> BuildTile()
{
  if (_bandClient != null)
  {
    var cap = await _bandClient.TileManager.GetRemainingTileCapacityAsync();
    if (cap > 0)
    {
      var tile = new BandTile(BandUiDefinitions.TileId)
      {
        Name = "Temperature reader",
        TileIcon = await LoadIcon("ms-appx:///Assets/TileIconLarge.png"),
        SmallIcon = await LoadIcon("ms-appx:///Assets/TileIconSmall.png"),
      };

      foreach (var page in BuildTileUi())
      {
        tile.PageLayouts.Add(page);
      }
      await _bandClient.TileManager.AddTileAsync(tile);
      await _bandClient.TileManager.RemovePagesAsync(BandUiDefinitions.TileId);
      await _bandClient.TileManager.SetPagesAsync(BandUiDefinitions.TileId, 
        BuildIntialTileData());
      return true;
    }
  }
  return false;
}

imageFirst thing you need to do is check remaining tile space capability. There's only room for up to 13 custom tiles, so chances are there's not enough room. If there is no space, this client silently fails. But if there's room, the tile is created with the designated GUID, a big and a small icon. The big icon appears on the tile, the small icon is typically used on notifications, but both can be used otherwise (as we will see later). "Large" is maybe stretching it a little as it's only 46x46 (the small one is 24x24). "LoadIcon" is a little routine that loads the icon and I nicked those from the Band samples. Then the tile UI pages are being built using BuildTileUi, and are added to the tile's PageLayout collection. So far, so good. Then things get a little murky.

  • First we add the tile - with page definitions - to the Band UI. By default, it is added to the very right side of the tile strip - just before the settings gear tile.
  • Then we remove any possible data possibly associated with the pages using RemovePagesAsync. Remember, this is not the structure, just what is being displayed on it. I am not 100% sure this line of code is actually needed, but I just left it while experimenting
  • Then we are adding the default data to display on the tile pages' UI elements using SetPagesAsync

Let's first have a look at BuildTileUi

private IEnumerable<PageLayout> BuildTileUi()
{
  var bandUi = new List<PageLayout>();
  
  var page1Elements = new List<PageElement>
  {
    new Icon {ElementId = BandUiDefinitions.IconId, 
Rect = new PageRect(60,10,24,24)},
new TextBlock {ElementId = BandUiDefinitions.TextTemperatureId, Rect = new PageRect(90, 10, 50, 40)}, new TextButton {ElementId = BandUiDefinitions.ButtonToggleFanId, Rect = new PageRect(10, 50, 220, 40), HorizontalAlignment = HorizontalAlignment.Center} }; var firstPanel = new FilledPanel(page1Elements) {
Rect = new PageRect(0, 0, 240, 150) }; var page2Elements = new List<PageElement> { new TextBlock {ElementId = BandUiDefinitions.TextTimeId, Rect = new PageRect(10, 10, 220, 40)}, new TextBlock {ElementId = BandUiDefinitions.TextDateId, Rect = new PageRect(10, 58, 220, 40)} }; var secondPanel = new FilledPanel(page2Elements) {
Rect = new PageRect(0, 0, 240, 150) }; bandUi.Add(new PageLayout(firstPanel)); bandUi.Add(new PageLayout(secondPanel)); return bandUi; }

Now this may look a bit intimidating, but it's actually not so hard to read.

  • First we create the UI elements of the firstpage - an Icon, a TextBlock, and a TextButton, all with location and size defined by a PageRect, relative to the panel they are going to be in.
  • Then we create the first panel, add the list of the UI elements created in the previous step on it, then define it's size and location by a PageRect as well. I am not exactly sure what the maximum values are for a panel, but 240, 150 works out nice and leaves enough space to the right to make the next page visible
  • Then we create the UI elements of the second panel - two TextBlocks of identical size, the second one right under the first
  • Then we create a second panel with the same size as the first panel
  • Finally, we create a PageLayout  from both panels and add those to the list.

As we could see in the BuildTile method the result of the BuildTileUi method is added the tile's PageLayouts collection:

foreach (var page in BuildTileUi())
{ tile.PageLayouts.Add(page); }

At this point, we have only defined the structure of what is to be displayed on the Band. It still does not display any data.

Showing data on a Band UI
Let's have a look at BuildTile again. It's using this line of code to display data

 await _bandClient.TileManager.SetPagesAsync(BandUiDefinitions.TileId,
BuildIntialTileData());
BuildIntialTileData. that just shows some default strings, in turn calls this method
private List<PageData> BuildTileData(string timeText, string dateText, 
                                     string temperature, string buttonText)
{
  var result = new List<PageData>
  {
    BuildTileDataPage2(timeText, dateText),
    BuildTileDataPage1(temperature, buttonText)
  };
  return result;
}

And then we come to the heart of the matter (as far as displaying data is concerned) - that is, these two little methods:

private PageData BuildTileDataPage1(string temperature, string buttonText)
{
  return 
    new PageData(
      BandUiDefinitions.Page1Id, 0, 
      new IconData(BandUiDefinitions.IconId, 1),
      new TextButtonData(BandUiDefinitions.ButtonToggleFanId, buttonText),
      new TextBlockData(BandUiDefinitions.TextTemperatureId, $": {temperature}"));
}

private PageData BuildTileDataPage2(string timeText, string dateText)
{
  return 
    new PageData(BandUiDefinitions.Page2Id, 1,
      new TextBlockData(BandUiDefinitions.TextTimeId, $"Time: {timeText}"),
      new TextBlockData(BandUiDefinitions.TextDateId, $"Date: {dateText}"));
}

Let's first dissect BuildTileDataPage2 as that is the most simple to understand. This says, basically: for page with Page2Id, which is the 2nd page on this UI (the page numbering  is zero based) set a text on a TextBlock with id BandUiDefinitions.TextTimeId, and set another text for BandUiDefinitions.TextDateId. The third parameter of the PageData constructor is of type params PageElementData[] so you can just go on adding user interface value settings to that constructor without the need of defining a list.

In BuildTileDataPage1 we do something similar - bar that the page index now is 0 in stead of 1, a text on a TextButton needs to be of TextButtonData in stead of TextBlockData. and the first item is an IconData. Notice that it adds an icon with index 1. That is the small icon. Remember this piece of code in BuildTile?

var tile = new BandTile(BandUiDefinitions.TileId)
{
  Name = "Temperature reader",
  TileIcon = await LoadIcon("ms-appx:///Assets/TileIconLarge.png"),
  SmallIcon = await LoadIcon("ms-appx:///Assets/TileIconSmall.png")
};

That was added as second, but of course that's zero based as well. You can also add additional icons to the UI but that's not covered here.

Now there is one important final piece of information that you may not have noticed. In BuildTileData I first add the second pagedata to the list, and then the first. I found it necessary to do it that way, or else the UI appears in reverse order (that is, the page with the date/time is displayed initially, and you have to scroll sideways for the page with the button and the temperature. Sometimes, just sometimes it happens the wrong way around anyway. I have not been able to determine what causes this, but if you add the pagedata in reverse order, it works most times - like in, I saw it go wrong two or three times, and only during heavy development.

The public methods to change the UI values are very simple wrappers around code we have already seen:

public async Task SetUiValues(string timeText, string dateText, 
                              string temperature, string buttonText)
{
  var pageData = BuildTileData(timeText, dateText, temperature, buttonText);
  await _bandClient.TileManager.SetPagesAsync(BandUiDefinitions.TileId, pageData);
}

public async Task SetUiValues(string temperature, string buttonText)
{
  await
    _bandClient.TileManager.SetPagesAsync(BandUiDefinitions.TileId,
       BuildTileDataPage1(temperature, buttonText));
}

The first one refreshes the whole UI, the second only the first page. So that is what is necessary to create a UI and display some data on it. Four UI elements, two pages. Really makes you appreciate XAML, doesn't it? ;)

Handling Band interaction
The BandOperator bascially only has the following functions:

  • When a tile is pressed, show the Band UI with the most recent data received from the Azure Service bus
  • When the toggle button is pressed fire off a command on the Azure Service bus
  • When the fan status change is confirmed by the Raspberry PI2, toggle the text on the button

... and yet, it's almost 250 lines long. A lot of that has to do with problems I encountered when a suspended app was resuming. I have tried to fix that using quite an elaborate method to get and create a Band client (the GetBandClient method) - that and it's helper methods are 60 lines in itself. So I will skip over that, but have a look at it in the demo solution to see how I tried to solve this. I am still not quite satisfied with it, but it seems to work. Most of the times.

Moving to the BandOperator's Start method, you can see how the interaction is set up

public async Task<bool> Start(bool forceFreshClient = false)
{
  var tilePresent = false;
  var bandClient = await GetBandClient(forceFreshClient);
  if (bandClient != null)
  {
    var currentTiles = await bandClient.TileManager.GetTilesAsync();
    var temperatureTile = currentTiles.FirstOrDefault(
      p => p.TileId == BandUiDefinitions.TileId);
    if (temperatureTile == null)
    {
      var buc = new BandUiController(bandClient);
      tilePresent = await buc.BuildTile();
    }
    else
    {
      tilePresent = true;
    }

    if (tilePresent)
    {
      await bandClient.TileManager.StartReadingsAsync();
      bandClient.TileManager.TileOpened += TileManager_TileOpened;
      bandClient.TileManager.TileButtonPressed += TileManager_TileButtonPressed;
    }
  }
  IsRunning = tilePresent;
  return tilePresent;
}

Basically this methods tries to either find an existing tile, and failing that, create a BandUiController to make one. bandClient.TileManager.StartReadingsAsync then activates listening to Band events - and by attaching events to TileOpened and TileButtonPressed the handler methods will be called - if the tile on the Band UI button is pressed, or if a button on the custom UI of the tile is pressed.

private async void TileManager_TileOpened(object sender, 
  BandTileEventArgs<IBandTileOpenedEvent> e)
{
  var bandClient = await GetBandClient();
  if (bandClient != null)
  {
    if (e.TileEvent.TileId == BandUiDefinitions.TileId && _lastTemperatureData != null)
    {
      var buc = new BandUiController(bandClient);
      await buc.SetUiValues(
        _lastTemperatureData.Timestamp.ToLocalTime().ToString("HH:mm:ss"),
        _lastTemperatureData.Timestamp.ToLocalTime().ToString("dd-MM-yyyy"),
         $"{_lastTemperatureData.Temperature}°C",
         GetFanStatusText());
      await bandClient.NotificationManager.VibrateAsync(
         VibrationType.NotificationOneTone);
    }
  }
}

So the funny thing is - this method gets called when a tile is pressed on the Band. Any tile, not necessarily the one just created. So first we have to determine if it was actually our tile that was being pressed, by checking the tile id against the id of our tile. When that is the case, we create a BandUIController and update the UI values with the last received data from the Azure Service bus. And then we send a single vibration, so the Band wearer knows new data was received immediately (without checking the date and time on the 2nd page of our custom UI).

A similar procedure goes for the handling of the fan button press:

private async void TileManager_TileButtonPressed(object sender, 
   BandTileEventArgs<IBandTileButtonPressedEvent> e)
{
  var te = e.TileEvent;
  if (te.TileId == BandUiDefinitions.TileId && 
    te.PageId == BandUiDefinitions.Page1Id && 
    te.ElementId == BandUiDefinitions.ButtonToggleFanId)
  {
    if (!_isProcessing)
    {
      _lastToggleUse = DateTime.UtcNow;
      _isProcessing = true;
      var cmd = new FanSwitchCommand(_lastFanStatus, true);
      Debug.WriteLine($"Sending fan command {cmd.Status}");
      await UpdateFirstPageStatus();
      await _fanStatusPoster.PostData(cmd);
    }
  }
}

First, we need to check if the button was pressed on our custom layout  - in theory, that would have been enough as there is only one button on it, but for good measure you can also check for the page and the element id in that page. What then basically happens is that text of the button is changed to "Processing" and a FanSwitchCommand is sent to the Raspberry PI2.

The changing of the text on the button is done via UpdateFirstPageStatus, that in turn uses GetFanStatusText

private async Task UpdateFirstPageStatus()
{
  var bandClient = await GetBandClient();
  if (bandClient != null)
  {
    var text = GetFanStatusText();
    var buc = new BandUiController(bandClient);
    await buc.SetUiValues($"{_lastTemperatureData.Temperature}°C", text);
  }
}

private string GetFanStatusText()
{
  return _isProcessing ? "Processing" : 
    _lastTemperatureData.FanStatus == FanStatus.On ? "Stop fan" : "Start fan";
}

The logic behind this is as follows:_isProcessing used to prevent the user from pressing the toggle button multiple times in a row. When you press the button, one of the first things that happens is that _isProcessing is set to true, effectively barring you from doing something with the button again. The text on the button changes to "Processing". The BandOperator is now waiting for the Raspberry PI2 to confirm it has actually toggled the fan. But you cannot change the value of one UI element on a Band page - you have to refresh all of them. So I call the BandUiController's SetUiValues overload with both the new button text and the last received temperature.

So how is the loop closed? How does the BandOperator know the Raspberry PI2 has indeed toggled the fan? The answer lies in the HandleNewTemperature method that receives new temperature data from the rest of the app (remember that it was wired up in MainViewModel.Start?)

public async void HandleNewTemperature(object sender, TemperatureData data)
{
  Debug.WriteLine(
    $"New temperature data received {data.Temperature} fanstatus = {data.FanStatus}");
  _lastTemperatureData = data;
  _lastTemperatureData.Timestamp = DateTimeOffset.UtcNow;
  if (_lastFanStatus != _lastTemperatureData.FanStatus && _isProcessing)
  {
    _isProcessing = false;
    _lastFanStatus = _lastTemperatureData.FanStatus;
    await UpdateFirstPageStatus();
  }
  else if (_lastToggleUse.IsSecondsAgo(Settings.FanSwitchQueueTtl) && _isProcessing)
  {
    _isProcessing = false;
    _lastFanStatus = _lastTemperatureData.FanStatus;
    await UpdateFirstPageStatus();
  }
  else if (!_isProcessing)
  {
    _lastFanStatus = _lastTemperatureData.FanStatus;
  }
}

So, the received temperature data does not only contain temperature but also the current status of the fan. But if this method was to accept the fan status right away after we had sent off a command to toggle the fan, the button text would immediately flip back to the old text - because the command had not reached the Raspberry PI2 yet, and it would not have time to react and send a confirmation.

So what we do is - when new temperature data arrives, _isProcessing is true (so the user has recently clicked the toggle button) and the received data indicates a status flip - then the Raspberry PI2 has received the toggle command and has acted upon it. So the button is updated from "Processing" to a value according the new fan status. If there is no status change, but the last toggle button use was longer ago then the message time-to-live of the FanSwitchQueue - we assume the command has never reached the Raspberry PI2, we update the _lastFanStatus to the old status, and update the button accordingly. In any other cases, if the user has not pressed the button recently, we just keep the last fan status. This has not much to do with the Band or the UI itself - it's just dealing with possible delays from message delivery (and possible messages not being received by the other party).

Conclusion
Making a custom Band UI is doable, but you do need to pay a lot of attention to detail to get things right. It's definitely more challenging than creating a UI using Blend, as you basically need to keep seeing the whole UI in your mind - there is no way of visually designing it or even make it visible short of running the app and checking the result on the Band. Debugging is a time and battery consuming activity. Acting upon events and having code interact with remote devices has some particular challenges too. And sometimes things just go wrong - but it is not always clear if those things were caused by me doing things wrong or not understanding the finer details of the Band UI API, the fact that I am using it on Windows 10 mobile (which is still in preview at this moment) or bugs in the various APIs (Band or otherwise) that I use to stitch things together. On the bleeding edge is where you suffer pain - but you have the most fun as well.

And yet, the potential use cases are pretty appealing and  are giving a nice idea of how the (very near) future of device coding with Windows CoreIoT looks like. And it has practical appliances too. Recently I was on a holiday in Neustadt an der Weinstraße (where amongst others this blog post was written so the sample location was not entirely random :) ) I had this contraption running at home - but I had put in in my study at home and had connected it to a powerful spot light in stead of a fan. I had my Lumia 1520 and Band with me - and although being physically in Germany, I was able to turn on a light at home (and get confirmation it was actually on or off) by clicking a button on my Band. Thus hopefully convincing potential burglars the house's resident geek was at his computer and the house was occupied. Not that there's much worth stealing anyway, but it's not fun to get home and find broken windows and stuff. If it had any effect - I don't know, but our house was left alone during our absence.

Well, this marks the end of a rather epic and quite voluminous blog post series.I encourage you one final time to download and check the demo solution - and build kick *ss stuff yourself with CoreIoT and/or the Band. Even Doctor Who is into wearable technology these days, and so should we all be :D

12 August 2015

Gotcha: custom Microsoft Band page shows only data in last added UI element

Today I finally nailed a very odd issue in the Microsoft Band SDK - or actually, my understanding thereof. For my current research project, I am building a custom UI on my Microsoft Band. I should contain two panels: one with a text, an icon and a button, and a second panel containing two texts. Details of how to setup up something like this, can be found in the band samples.

Building a Band UI is not for the faint hearted, it includes building the UI from code without any kind of designer. Filling those UI elements with a value then is a separate step, which contains quite a big gotcha that you cannot derive from the samples.

My Band UI creation code is like this:

private IEnumerable<PageLayout> BuildTileUi()
{
  var bandUi = new List<PageLayout>();
  var page1Elements = new List<PageElement>
  {
	new Icon {ElementId = IconId, Rect = new PageRect(60,10,24,24)},
	new TextBlock  {ElementId = TextTemperatureId, Rect = new PageRect(90, 10, 50, 40)},
	new TextButton {ElementId = ButtonToggleFanId, Rect = new PageRect(10, 50, 220, 40), 
	    HorizontalAlignment = HorizontalAlignment.Center}
  };
  var firstPanel = new FilledPanel(page1Elements) { Rect = new PageRect(0, 0, 240, 150) };

  var page2Elements = new List<PageElement>
  {
	new TextBlock {ElementId = TextTimeId, Rect = new PageRect(10, 10, 220, 40)},
	new TextBlock {ElementId = TextDateId, Rect = new PageRect(10, 58, 220, 40)}
  };
  var secondPanel = new FilledPanel(page2Elements) { Rect = new PageRect(0, 0, 240, 150) };

  bandUi.Add(new PageLayout(firstPanel));
  bandUi.Add(new PageLayout(secondPanel));

  return bandUi;
}

That is quite a handful, right? Then for setting values to those UI element, I had this little piece of code:

private List<PageData> BuildTileData1(string timeText,
                                      string dateText,
                                      string temperature,
                                      string buttonText)
{
  var result = new List<PageData>
  {
    new PageData(Page2Id, 1,
      new TextBlockData(TextTimeId, timeText)),
    new PageData(Page2Id, 1,
      new TextBlockData(TextDateId, dateText)),
    new PageData(Page1Id, 0, new IconData(IconId, 1)),
    new PageData(Page1Id, 0,
      new TextBlockData(TextTemperatureId, temperature)),
    new PageData(Page1Id, 0, new TextButtonData(ButtonToggleFanId, buttonText))
  };

  return result;
}

And this is set to my tile using bandClient.TileManager.SetPagesAsync. Which gave me a quite puzzling result: only the last element on each page actually had a value. So that means only the datetext and the button actually showed text. As the sample in the Band SDK samples only uses one UI element, I was like, repeat adding PageData elements, right?

Wrong!

It turns out you can only add values to a PageData element once per SetPagesAsync call. So what you need to do, actually, is:

private List<PageData> BuildTileData(string timeText, 
                                     string dateText, 
                                     string temperature, 
                                     string buttonText)
{
  var result = new List<PageData>
  {
    new PageData(Page2Id, 1,
      new TextBlockData(TextTimeId, timeText),
      new TextBlockData(TextDateId, dateText)),
    new PageData(Page1Id, 0, 
      new IconData(IconId, 1), 
      new TextButtonData(ButtonToggleFanId, buttonText), 
      new TextBlockData(TextTemperatureId, temperature)),
  };

  return result;
}

See the difference? Only add 2 PageData elements, one for every Page, not one for every page element data object. Maybe elementary, but I overlooked it. For quite some time. The constructors of PageData give a bit of a hint:

public PageData(Guid pageId, int pageLayoutIndex, IEnumerable<PageElementData> values);
public PageData(Guid pageId, int pageLayoutIndex, params PageElementData[] values);

as the third parameter is not a page element data object but a list of it. The thing is, I created the UI on demand (when I tapped the tile) and then it worked as expected - but not the seconds time around.

It took me quite some time to figure this one out, so I thought it best to blog about it right away. Maybe I am giving help here, maybe I am just documenting my own stupidity, but nevertheless ;)

I skip the demo sample this time - if you run into this problem, you are quite far with the Band UI already, and the demo I am working on contains this code anyway and will - hopefully - arrive online soon anyway. Attentive readers will have some inkling of what that demo exactly comprises.

Happy Band coding!

25 February 2015

Displaying Microsoft Band heart rate via Raspberry PI2 using Mono, C#, OWIN and a Windows Phone

Intro – what? WHY?

As regular readers of my blog might have seen, I got myself a Raspberri PI 2 and a Sunfounders sensor kit, and was happily dabbling a little with it. And then suddenly, out of the blue, Microsoft dropped a development kit for the Microsoft Band – and Gerard Verbrugge (Microsoft DX Netherlands) challenged me to make something with it. So I thought to combine a number of gadgets: my Microsoft Band, my Windows Phone and  the Raspberry PI plus some of the stuff from the sensor kit.

I created an application that shows your heart rate with a blinking led connected to the Raspberry PI, with a connected buzzer making sound as well. The LED blinks at the heart rate as reported by the Band, and using the  buzzer, the PI also emits and audible tick-sound as the LED blinks. At low heart rate the LED blinks blue, at medium rate green, and at high rate red:

Raspberry PI2 controlled by Microsoft band at heart rate < 80
Raspberry PI2 controlled by Microsoft band at heart rate > 80

If you crank up the sound, you can hear the ticking of the buzzer at the heartbeat. I think this is one of the coolest stuff I ever made. So, Gerard, challenge accepted. And, IMHO, achievement unlocked ;)

Hardware used

  • 1 Microsoft Band
  • 1 Windows Phone running Windows Phone 8.1
  • 1 Raspberry PI 2
  • 1 4-pins RGB LED from the SunFounders kit
  • 1 3-pins Active buzzer from the Sunfounders kit
  • Breadboard, GPIO cable (connecting PI to breadboard), miscellaneous connecting wires

Don’t give up reading if you don’t have a Band – that part can be simulated and the technique to control hardware peripherals from a WebAPI endpoint is useful on it’s own.

Global setup

How this thing works is, in essence, pretty simple:

  • On the Raspberry PI2 runs a OWIN server hosting
    • a little WebAPI endpoint that accepts heart rate data
    • an endless running task making the LED blink in the right color and cadence, again using Daniel Riches’ wrapper around Gordon Henderson’s wiringPI C library. Once again, with some adaptions.
  • A Windows Phone 8.1 application listening to the paired Microsoft Band’s heartrRate sensor’s events, and posting those to the RaspBerry PI2’s OWIN server upon receiving those.

The demo solution contains 5 projects:

  • A Universal app (with only the Windows Phone project actually used)
  • An project holding the OWIN server
  • A PCL holding two simple classes: an object to post data with to the OWIN server, and a Settings object with some hard coded settings shared over several projects
  • A test project posting a single simulated hearth rate update to the server
  • The WiringPi project – the wrapper around the C library that I used before.

A little PCL

I started a with a little PCL library WebLinker.Pcl that contains only two classes: a simple payload class that I can post from my Windows Phone to my OWIN server:

namespace WebBlinker
{
  public class Pulse
  {
    public int HeartRate { get; set; }
  }
}

and a little class to host some settings

namespace WebBlinker
{
  public static class Settings
  {
    public const string ServiceUrl = "http://192.168.0.188:5001/api/singleblink";
    public const string BaseListenUrl = "http://*:5001/";
  }
}

Rather primitive, indeed. But I want the class that I post as JSON and deserialize from JSON to be shared over client and server, and I don't like magic strings (especially not repeated magic strings) so I tend to gather those in settings classes.

The OWIN server

OWIN is a self hosted web server inside a console app – more or less Microsoft’s answer to nodejs and the like. It’s pretty simple to get started: make a console app, then add the Microsoft.AspNet.WebApi.OwinSelfHost nuget package and that brings everything you need – it’s even simpler than I described in this post. Oh, and add a reference to the PCL while you are at it.

The server contains four parts:

  • A “Program” class with a static void Main – like every console app – kicking and keeping the OWIN server alive
  • A “Startup” class containing the configuration of the OWIN server
  • A “BlinkController” WebAPI controller hosting two methods (one for test, one for the real thing)
  • A “Blinker” class that hosts a thread that does the actual blinking.

The Program class is, well, not very complicated:

using System;
using System.Threading;
using Microsoft.Owin.Hosting;

namespace WebBlinker
{
  class Program
  {
    static void Main(string[] args)
    {
      // Start OWIN host and keep it running
      using (WebApp.Start<Startup>(url: Settings.BaseListenUrl))
      {
        while (true)
        {
          Console.WriteLine("Server still alive...");
          Thread.Sleep(60000);
        }
      }
    }
  }
}

The startup class does the actual configuration and is simple but noteworthy:

using System.Web.Http;
using Owin;

namespace WebBlinker
{
  public class Startup
  {
    public void Configuration(IAppBuilder appBuilder)
    {
      var config = new HttpConfiguration();

      //Use attribute routes!
      config.MapHttpAttributeRoutes();
      appBuilder.UseWebApi(config);
      Blinker.GetBlinker().Start();
    }
  }
}

Although in general I am a fan of Microsoft technology, there are some tiny parts I hate with a vengeance. One of those things is ASP.NET MVC / WebAPI default routing. So whenever I get the chance, I go for attribute based routing. I’d rather like to have the minor chore of having to define all my routes explicitly and know they will be there, in stead of having to deal with inferred routes that might just disappear because someone else adds a route that ‘steals’ or invalidates mine. Just a pet peeve. So I did not include the default routing – I just added a call to MapHttpAttributeRoutes. Now I can control my routes pretty easily in my BlinkController by adding attributes:

using System;
using System.Web.Http;

namespace WebBlinker
{
  public class BlinkController : ApiController
  {
    [HttpPost]
    [Route("api/singleblink")]
    public void PostBlink([FromBody] Pulse pulse)
    {
      Console.WriteLine( "Got update: {0}", pulse.HeartRate);
      Blinker.GetBlinker().HeartRate = pulse.HeartRate;
    }

    [HttpGet]
    [Route("api/helloworld")]
    public string HelloWorld()
    {
      Console.WriteLine("Hello world called");
      return "Hello world!";
    }
  }
}

So now I have an explicitly defined route “api/singleblink” where I can post a Pulse object containing a heart rate to, as well as a simple “api/helloworld” method whose URL I can enter in a browser to see if the WebAPI is up and running at all.

In the “PostBlink” method our Blinker’s HeartRate property is updated. And we come to the heart of the matter – pun intended – as far as the server is concerned. What is important to understand is that the Microsoft Band does not post an update per heartbeat. It posts at a quite regular interval what your current heartbeat is. So in order to let the LED blink at the rate of a heartbeat, we actually have to calculate how often the LED needs to flash. As a matter of fact – the LED does not show your actual heartbeat. It just blinks at the same rate. More or less.

The basic setup of the Blinker is:

using System;
using System.Threading;
using System.Threading.Tasks;
using WiringPi;

namespace WebBlinker
{
  public class Blinker
  {
    private DateTime lastReceivedUpdate = DateTime.MinValue;
    private int heartRate;
    private Task task;

    private Blinker()
    {
      if (Init.WiringPiSetup() != -1)
      {
        GPIO.pinMode(BluePin, (int)GPIO.GPIOpinmode.Output);
        GPIO.pinMode(GreenPin, (int)GPIO.GPIOpinmode.Output);
        GPIO.pinMode(RedPin, (int)GPIO.GPIOpinmode.Output);
        GPIO.pinMode(SoundPin, (int)GPIO.GPIOpinmode.Output);
      }
    }

    public void Start()
    {
      if (task == null)
      {
        cancellationTokenSource = new CancellationTokenSource();
        task = new Task(() => ShowHeartRateBlinking(cancellationTokenSource.Token), 
           cancellationTokenSource.Token);
        task.Start();
      }
    }

    public void Stop()
    {
      if (cancellationTokenSource != null)
      {
        cancellationTokenSource.Cancel();
        task = null;
      }
    }

    private CancellationTokenSource cancellationTokenSource = null;


    private static Blinker blinker;
    public static Blinker GetBlinker()
    {
      return blinker ?? (blinker = new Blinker());
    }

    public const int RedPin = 27;
    public const int GreenPin = 28;
    public const int BluePin = 29;
    public const int SoundPin = 0;
  }
}

On creation, it initializes the pins we are going to use for output (these are GPIO16, GPIO20 and GPIO21 for RGB, and GPIO17 for sound). See picture for connection details. To make things easier for myself, I actually selected a blue wire for the blue pin, a red for the red ping, and green for the green pin ;):WP_20150225_21_02_33_Pro__highres

I am using a different API than before, for this one is – amongst other reasons reasons - a lot simpler to use and does not hinge on C threading. With a caveat I will come to a little later. Important to see is that’s a singleton – there’s only a private constructor and a static method GetBlinker() to create it. Normally you would not get away with this in a web environment, but as OWIN is not hosted in IIS, we actually can use statics here. Scalable? No. But who cares on a mini device like this. Usable? Yes!

So the Startup class initializes and starts the Blinker. That fires of a cancellable task. The Blinker actually has some more code, amongst other the implementation of said task:

private async Task ShowHeartRateBlinking(CancellationToken cancellationToken)
{
  while (!cancellationToken.IsCancellationRequested)
  {
    if (DateTime.Now - lastReceivedUpdate < TimeSpan.FromSeconds(5))
    {
      DoBlink();
      await Task.Delay(60000/HeartRate, cancellationToken);
    }
    else
    {
      await Task.Delay(10000, cancellationToken);
    }
  }
}

public int HeartRate
{
  get { return heartRate; }
  set
  {
    if (value >= 0 && value <= 200)
    {
      lastReceivedUpdate = DateTime.Now;
      Console.WriteLine("Got updated: {0}", value);
      heartRate = value;
    }
  }
}

private async Task DoBlink()
{
  var pin = GetPin();
  GPIO.digitalWrite(pin, 1);
  GPIO.digitalWrite(SoundPin, 1);
  await Task.Delay(50);
  GPIO.digitalWrite(pin, 0);
  GPIO.digitalWrite(SoundPin, 0);
}

private int GetPin()
{
  if( HeartRate < 80) return BluePin;
  return HeartRate < 130 ? GreenPin : RedPin;
}

The ShowHeartRateBlinking actually is an endless loop, that runs while no cancellation is given (or until the OWIN server is simply killed). If the last update was less than 5 seconds ago, it will blink the LED and give a sound pulse, wait for 60000ms divided by the beats per minute – and goes into the next blink. If it does not get updates after 5 seconds, it goes into a 10-seconds polling mode.

The HeartRate property is set by the BlinkController when new data from the Band is posted to the OWIN server’s endpoint, and it’s setter also keeps track of the last update time. The DoBlink method actually does the blinking and the activating of the buzzer (at pin 0). The GetPin method, by means of extra fun, determines what color the LED will flash based upon the posted heart rate: blue for below 80, green for below 130, and red for 130 and up.

In the demo solution there is actually a test project with a test method that posts a single update to the OWIN server. That should at least get you a “Got updated” line on the screen when you run the server. If you run the method multiple times, the LED should start to blink at 75bmp and the buzzer gives a ticking sound.

Adapting the WiringPi wrapper

I already warned about a caveat concerning the API I am using now. Unfortunately, if you compile and run the OWIN server in this stage on the Raspberry PI2, you will get an error. Apparently the WiringPi wrapper is a bit outdated or things have changed for the Raspberry PI2. In the WrapperClass.cs file there’s a class GPIO with on top the following part:

public class GPIO
{
  [DllImport("libwiringPi.so", EntryPoint = "pinModeGpio")] 
  public static extern void pinMode(int pin, int mode);

  [DllImport("libwiringPi.so", EntryPoint = "digitalWriteGpio")]
  public static extern void digitalWrite(int pin, int value);

Studying the original C code, I found that the entry points should be just “pinMode” and “digitalWrite”, not “pinModeGpio” and “digitalWriteGpio”. That was easily fixed:

public class GPIO
{
  [DllImport("libwiringPi.so", EntryPoint = "pinMode")] 
  public static extern void pinMode(int pin, int mode);

  [DllImport("libwiringPi.so", EntryPoint = "digitalWrite")]
  public static extern void digitalWrite(int pin, int value);

If you compile the OWIN server now, and copy the .EXE and all DLLs in the bin/debug directory to your Raspberry PI2, you should now be able to run the server (sudo mono WebBlinker.exe – since settings.BaseListenerUrl is set to “http://*:5001/” it listens to port 5001 on all hostnames assigned to your Raspberry PI, and this means you have to start the webserver as root). It should print “Server still alive…” every 60 seconds on the command line, as well as any heart beat postings coming by.

The Windows Phone app

I hope my Windows Phone friends will forgive me for skimping a little on this, because I really made quite a horrible and above all ugly app. I first installed the Microsoft ASP.NET WebAPI 2.2 Client libraries in it. Then I created this model to actually listen to the Band’s heart rate events and post them to the OWIN server:

using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using Microsoft.Band;
using Microsoft.Band.Sensors;
using WebBlinker;

namespace BlinkerPhone
{
  public class BlinkerModel
  {
    public async void SendSimulatedPulse(int value)
    {
      var pulse = new Pulse { HeartRate = value };
      using (var client = new HttpClient())
      {
        await client.PostAsJsonAsync(new Uri(Settings.ServiceUrl), pulse);
      }
    }

    private IBandClient bandClient;

    public async void StartListening()
    {
      var pairedBands = await BandClientManager.Instance.GetBandsAsync();
      if (pairedBands.Any())
      {
        var band = pairedBands.First();
        bandClient = await BandClientManager.Instance.ConnectAsync(band);
        var sensor = bandClient.SensorManager.HeartRate;
        sensor.ReadingChanged += SensorReadingChanged;
        await sensor.StartReadingsAsync();
      }
    }

    public async void StopListening()
    {
      var sensor = bandClient.SensorManager.HeartRate;
      sensor.ReadingChanged -= SensorReadingChanged;
      await sensor.StopReadingsAsync();
      bandClient.Dispose();
    }

    async void SensorReadingChanged(object sender, 
      BandSensorReadingEventArgs<IBandHeartRateReading> e)
    {
      try
      {
        if (e.SensorReading != null)
        {
          var pulse = new Pulse { HeartRate = e.SensorReading.HeartRate };
          using (var client = new HttpClient())
          {
            await client.PostAsJsonAsync(new Uri(Settings.ServiceUrl), pulse);
            Debug.WriteLine("Sending update, pulserate = {0}", pulse.HeartRate);
          }
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine("Error reading/sending data: {0}", ex);
      }
    }
  }
}

wp_ss_20150225_0001This is not exactly rocket science, and it’s mostly 1:1 nicked from the heart rate sensor sample in the Windows Phone Band SDK samples anyway. If there’s a heart rate event coming in, a new Pulse object is created and posted to the Raspberry PI2’s OWIN Web API endpoint and the LED starts blinking in the right rate.

To give the user the ability to start, stop and send test data I created this awesome UI with buttons to start and stop the phone sending band data to the OWIN server, as well as a button to send a simulated heart rate as typed in the textbox. Quite the pinnacle of minimalistic design I’d say, don’t you think? ;)

Conclusion

With a little imagination, out-of-the-box thinking and of course some kick *ss hardware and awesome SDKs it’s not that hard these days to let three devices dance together and make a single system – all C#, mostly Microsoft technology and mostly standard knowledge. The hardest part is now writing the C# code that runs on Mono, as it’s impossible to debug a running instance from Visual Studio. We are so spoiled by Visual Studio and the awesome tools it offers, it’s sometimes very hard to have to do without. Let’s hope Windows 10 for devices makes life as good for device developers as for all other Windows Platform developers.

For those who missed the link in between – a complete solution can be downloaded here.