Windows 10 IoT Core–Network Enabling the Blinking LED Demo with AllJoyn

Following my previous post about taking a Raspberry PI 2 and running Windows IoT Core on it in order to build out an app that allowed a user to make an LED flash by pressing a switch, I wondered what it would take to make this "more useful" by offering it as a service on my local network such that the light could be controlled remotely.

In effect – what does it take to turn my Raspberry PI 2 into an "IoT Light Bulb" ?

As an aside, I know this is silly – it'd be expensive to go down this route rather than to just buy an "IoT LightBulb" but that doesn't mean that I shouldn't have fun playing around a little with the code for it 🙂

Over the years, I've worked with various bits of network technology from sockets to RPC, DCE, DCOM, .NET Remoting, various levels of 'Web Services' and 'Message Queues', WebSockets and good old HTTP and similar.

Some of those technologies came with some form of 'registration' or ‘advertisement’ concept where one piece of software can come onto a network and advertise that it has services to offer to other applications on that network.

I still use this kind of thing today when I use devices in my home that are (still) powered by Universal Plug N Play or DLNA which allow a consuming device to discover a providing device on a network and then make use of some of its services.

There's a new software stack in town when it comes to providing this kind of functionality for the IoT world and that's AllJoyn.

What’s AllJoyn?

I'm just beginning my AllJoyn journey.I watched the //Build 2015 session here

AllJoyn: Building Universal Windows Apps that Discover, Connect, and Interact with Other Devices and Cloud Services Using AllJoyn

and it sparked my interest enough to give it a whirl and see if I could make my "IoT LightBulb" available as a local service on my network.

There's some good documentation on AllJoyn on the website and on the Windows IoT site and I scanned through;

  • Architecture
  • Core Framework – this covers areas such as an app announcing itself to the network, getting connections set up between apps (both point-to-point and multi-point), one app being able to read metadata about another app (called 'introspection'), security and so on.
  • Base Services

There's 2 flavours of AllJoyn – a flavour called 'Thin Core' which is the very essence of the services and which is designed for a micro-controller class of device and provides the ability for an app to do 'bus attachment' and provide/consume services. Then there's 'Standard Core' which is more for the micro-processor class of device and offers more services.

Windows 10 comes with an implementation of these AllJoyn APIs and includes;

  • AllJoyn Standard Client API (this is a traditional C API).
  • A Universal Windows Platform API on top of that C API such that a UWP developer feels at home writing AllJoyn apps without having to hunt down a C API.

Defining My Lightbulb Service Interface

I figured that I'd try and get my "IoT LightBulb" up and running by first designing a simple programmatic interface for it which I can specify as;

void Switch(bool on);

but, from an AllJoyn perspective, I need to model this in AllJoyn's version of 'Interface Definition Language' which seems to be the 'Introspection Data Format' of D-Bus;

http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format

with D-Bus being one of the IPC/RPC mechanisms that I've not made any use of in the past but that's perhaps because I think it's mainly found on Linux.

Based on a quick reading of that link, I came up with this for my interface;

<node name="/com/taulty/lightbulb">
  <interface name="com.taulty.ilightbulb">
    <method name="switch">
	<arg name="on" type="b" direction="in"/>
    </method>
  </interface>
</node>

to be honest, I guessed at the type of "b" but I then located the alljoyncodegen.exe tool mentioned in the session in the Windows 10 IoT bits and ran it on that interface and it didn't complain so I figured that I'd press on.

I ran it with;

capture1

I was a little surprised at how much code got generated for this simple example (it really does remind me of RPC programming!);

capture2

and this gives me code for a C++/CX WinRT component and so I went with it and dropped into Visual Studio and made a new WinRT component project in C++ and added this code to it before compiling it up.

I tried to use the "new project from existing code" option in Visual Studio but it looks like that mechanism hasn't been updated in quite some time as it doesn't seem to know about project types like "WinRT Components" and so I just made a blank WinRT component project;

capture3

and then I manually removed the source files provided by the template;

capture4

before adding in all the source files from the generated code and I can see that my LightbulbService has been turned into this;

namespace com {
	namespace taulty {

		public interface class IlightbulbService
		{
		public:
			// Implement this function to handle calls to the switch method.
			Windows::Foundation::IAsyncOperation<lightbulbSwitchResult^>^ SwitchAsync(
				Windows::Devices::AllJoyn::AllJoynMessageInfo^ info, _In_ bool interface_on);

		};

	}
}

I then needed to remember to tell my pch.cpp file that it was allowed to create the precompiled header via its properties (NB: life is easier if you use Platform:All unlike what I did in the picture below);

capture5

and then I found that I hit this from the preprocessor;

capture6

and so I figured that I could guess what to do about that and added a define to my project (again: choose Platform:All for an easier life than the one shown in the picture here);

capture7

and then I hit some errors about virtual memory limits for pre-compiled headers being hit in the compiler;

Capture8

and so I tweaked those values exactly as the error told me to!

capture9

and then I was left with some failures to convert one type of function pointer to another type errors;

Capture10

which I scratched my head over for a while and it was long enough to do a web search which found me a Windows IoT blog post which turned out to be very similar to what I was doing except that the post relates to consuming an existing service rather than writing a new one;

http://channel9.msdn.com/Blogs/Internet-of-Things-Blog/Step-By-Step-Building-AllJoyn-Universal-Windows-Apps-for-Windows-10-Public-Preview

but that helped me enough to realise that my calling convention needed to be changed to standard call;

Capture11

and then I could finally compile! and I had a WinRT component.

At this point, I had a cup of tea and a biscuit before…

Implementing My LightBulb Service as an AllJoyn Producer

I added a regular, blank UWP project written in C# and referenced my lightbulb WinRT component from that project and then immediately hit build errors;

Capture12

These are the typical WinRT component errors where the file I'm producing is going to be called lightbuld.winmd because of the way that I named the project but the root namespace is com.taulty (this was generated from the interface.xml file by the alljoyncodegen.exe tool).

NB: if I'd been following the IoT blog post, I realised at this point that I would have avoided this problem.

What to do? I changed the root namespace of my project to be com.taulty via the properties window;

Capture13

and that got me back to being able to build (for the moment, at least).

I wrote just enough code to have a dummy implementation of my lightbulb service which is implementing the service interface defined in the WinRT component project that came from the code generated by the alljoyncodegen.exe tool;

namespace LightbulbService
{
  using com.taulty;
  using Windows.Devices.AllJoyn;
  using Windows.Foundation;

  class LightbulbService : IlightbulbService
  {
    public IAsyncOperation<lightbulbSwitchResult> SwitchAsync(AllJoynMessageInfo info, 
      bool switchOn)
    {
      return (null);
    }
  }
}

and then I wrote some code that ran when my main app page loaded in order to advertise this service;

namespace LightbulbService
{
  using Windows.Devices.AllJoyn;
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;
  using com.taulty;

  public sealed partial class MainControl : UserControl
  {
    public MainControl()
    {
      this.InitializeComponent();
      this.Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
      AllJoynBusAttachment busAttachment = new AllJoynBusAttachment();
      busAttachment.AuthenticationMechanisms.Add(AllJoynAuthenticationMechanism.SrpAnonymous);

      lightbulbProducer producer = new lightbulbProducer(busAttachment);

      producer.Service = new LightbulbService();

      producer.Start();
    }
  }
}

and I altered my manifest to include AllJoyn as a capability for my app;

Capture14

and then I ran this and, surprisingly, it seemed to work!

NB: I'm choosing to not attempt to secure this service, that's perhaps not the best plan but I'm just experimenting at this point.

To see if my service might be advertised I downloaded the getajxml.exe tool from GitHub here;

https://github.com/MS-brock/AllJoynToasterDemo/tree/master/getajxml

and that gave me this output;

Capture15

which seemed quite 'promising' and I learned that I can use this tool to grab the introspection XML from my service (not that I didn't know it already :-)) as in;

Capture16

and so it looks like my lightbulb service has magically grown a org.freedesktop.DBus.Introspectable interface which is, I guess, how I come to have this interface description being returned to me from the tool (presumably it goes out onto the network asking for implementations of this Introspectable interface in some fashion?).

I also grabbed the "AllJoyn Explorer" app from here;

https://ms-iot.github.io/content/en-US/win10/AllJoyn.htm

and ran than up and it saw my service;

capture19

although I wasn’t 100% sure that it was seeing my interface based on this “0 interfaces” view below;

Capture20

and this didn't seem right and so I thought I'd better make sure (as per the instructions) that this particular application was exempt from the standard WinRT loopback network restrictions and so I ran this command line;

CheckNetIsolation LoopbackExempt -a -n=adff22a6-72bc-4137-be64-04899205e0a7_hk7qwdpjbkp1m

and I then found that I got a different view;

capture21

and I can drill in and see;

Capture22

and that seemed a little more promising!

However…my next step was to be to deploy this same code to Windows IoT Core running on Raspberry Pi 2 but I initially struggled to get the advertisement of the service to show up on my laptop either when viewed by the command line getajxml.exe or via the AllJoyn Explorer app.

I spent "a little while" on this. One thing I learned while doing it was to take a look at the web interface for the Windows IoT machine ( for me, that's http://minwinpc on my local network) and to play with the AllJoyn tracing there although it didn't ultimately solve the problem for me but it did tell me that one or two things were at least happening;

Capture23

I haven't quite figured this out yet.

I got to the point of "I wonder if this works if my laptop is on WiFi" and I found that (even though my home network is very simple with WiFi and wired translating into the same network) things did start working with my laptop on WiFi and my PI 2 on (of course) wired.

Oddly, I then found that when I moved my laptop back to wired, things continued to work so it was one of those “things started working” scenarios that I can’t really explain.

However, I finally managed to get this screenshot from the AllJoyn Explorer with it finding the available services on my Raspberry PI 2;

Capture24

and you'll note that the IoT Core preview image here also shipped with a ZWaveAdapter interface built into it which the AllJoyn Explorer also sees.

I thought I'd then continue by adding the IoT Extension SDK to my producer app;

Capture17

so that I could implement my service to change the values on GPIO pin 5 on my PI 2 as per my previous blog post here to make an LED light up and that left me with this implementation;

namespace LightbulbService
{
  using com.taulty;
  using System;
  using System.Threading.Tasks;
  using Windows.Devices.AllJoyn;
  using Windows.Devices.Gpio;
  using Windows.Foundation;

  class LightbulbService : IlightbulbService
  {
    static LightbulbService()
    {
      gpioController = new Lazy<GpioController>(() =>
      {
        return (GpioController.GetDefault());
      });
      ledPin = new Lazy<GpioPin>(() =>
      {
        var pin = gpioController.Value.OpenPin(GPIO_LED_PIN);
        pin.SetDriveMode(GpioPinDriveMode.Output);
        return (pin);
      });
    }
    public IAsyncOperation<lightbulbSwitchResult> SwitchAsync(AllJoynMessageInfo info, 
      bool switchOn)
    {
      return (this.SwitchAsync(switchOn).AsAsyncOperation());
    }
    async Task<lightbulbSwitchResult> SwitchAsync(bool switchOn)
    {
      ledPin.Value.Write(switchOn ? GpioPinValue.Low : GpioPinValue.High);
      return (lightbulbSwitchResult.CreateSuccessResult());
    }
    static Lazy<GpioController> gpioController;
    static Lazy<GpioPin> ledPin;
    static readonly int GPIO_LED_PIN = 5;
  }
}

and what was then fun was that I can now use the AllJoyn Explorer to turn on my LED as you can see in the little videos below;

that’s the PC view, this is the external view;

which seemed like quite an 'immediate' approach to being able to consume my service but I thought I'd try and write my own consumer as well..

Writing an AllJoyn Consumer

I added another, blank, UWP project into my solution which I called lightswitch and then I added a reference to my lightbulb project containing the WinRT component with the generated code in it;

capture18

and I threw together a little piece of XAML that would let me build up a list of lightbulb services and then switch bulbs on/off;

<Page
    x:Class="lightswitch.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:lightswitch"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBlock Text="Available lightbulbs"/>
    <ListBox ItemsSource="{Binding Lightbulbs}" Grid.Row="1"
             SelectedValue="{Binding SelectedLightbulb,Mode=TwoWay}">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding UniqueName}"/>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
    <ToggleSwitch Grid.Row="2" Header="Light Status" Toggled="OnLightswitchToggled"/>
  </Grid>
</Page>

and some code behind to actually do the work (very hacked together);

namespace lightswitch
{
  using com.taulty;
  using System;
  using System.Collections.ObjectModel;
  using System.ComponentModel;
  using System.Linq;
  using Windows.Devices.AllJoyn;
  using Windows.UI.Core;
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;

  public sealed partial class MainPage : Page, INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    public class LightbulbEntry
    {
      public string UniqueName { get; set; }
      public lightbulbConsumer Consumer { get; set; }
    }
    public MainPage()
    {
      this.InitializeComponent();
      this.Loaded += OnLoaded;
      this.Lightbulbs = new ObservableCollection<LightbulbEntry>();
    }
    void OnLoaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
    {
      this.DataContext = this;

      this.StartDiscoveringLightbulbs();
    }
    void StartDiscoveringLightbulbs()
    {
      this.busAttachment = new AllJoynBusAttachment();
      this.busAttachment.AuthenticationMechanisms.Add(AllJoynAuthenticationMechanism.SrpAnonymous);

      this.lightbulbWatcher = new lightbulbWatcher(this.busAttachment);

      this.lightbulbWatcher.Added += OnAdded;

      this.lightbulbWatcher.Start();
    }
    async void OnAdded(lightbulbWatcher sender, AllJoynServiceInfo args)
    {
      await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
        async () =>
        {
          lightbulbJoinSessionResult joinResult = await lightbulbConsumer.JoinSessionAsync(
            args, sender);

          if (joinResult.Status == AllJoynStatus.Ok)
          {
            this.Lightbulbs.Add(
              new LightbulbEntry()
              {
                UniqueName = args.UniqueName,
                Consumer = joinResult.Consumer
              }
            );
            joinResult.Consumer.SessionLost += OnLightbulbLost;
          }
        }
      );
    }
    async void OnLightbulbLost(lightbulbConsumer sender, AllJoynSessionLostEventArgs args)
    {
      await this.Dispatcher.RunAsync(
        CoreDispatcherPriority.Normal,
        () =>
        {
          this.Lightbulbs.Remove(
            this.Lightbulbs.Single(entry => entry.Consumer == sender));
        }
      );
    }
    public LightbulbEntry SelectedLightbulb
    {
      get
      {
        return (this.selectedLightbulb);
      }
      set
      {
        if (this.selectedLightbulb != value)
        {
          this.selectedLightbulb = value;
          this.RaisePropertyChanged("SelectedLightbulb");
        }
      }
    }
    void RaisePropertyChanged(string property)
    {
      var changeWatchers = this.PropertyChanged;
      if (changeWatchers != null)
      {
        changeWatchers(this, new PropertyChangedEventArgs(property));
      }
    }
    async void OnLightswitchToggled(object sender, RoutedEventArgs e)
    {
      if (this.SelectedLightbulb != null)
      {
        bool value = ((ToggleSwitch)sender).IsOn;
        await this.SelectedLightbulb.Consumer.SwitchAsync(value);
      }
    }
    public ObservableCollection<LightbulbEntry> Lightbulbs { get; private set; }
    AllJoynBusAttachment busAttachment;
    lightbulbWatcher lightbulbWatcher;
    LightbulbEntry selectedLightbulb;
  }
}

and I seemed to be 'in business' in the sense that I could then run this application, have it display my list of lightbulb services – i.e. the one and only instance running on my Raspberry PI 2 and switch the bulb on/off;

Capture25

and that all worked pretty well for a first attempt at an AllJoyn consumer and an AllJoyn producer.

I'll be digging in more to this in the future and I'm particularly interested in how these kinds of scenarios can combine with Cortana for scenarios like;

  • Me: "Hey, Cortana, turn the lights on with MikesLightBulbApp"
  • C: "Which lights do you want to turn on? Upstairs? Downstairs? Lounge?"
  • Me: "Lounge"
  • C: “Ok, the lounge lights are now on”.

which all seems quite achievable given these sorts of pieces. I’m also curious as to how this works with the app lifecycle model of a Windows 10 UWP app where I might want to be able to have background tasks implement some of the work as advertisements come on/off an AllJoyn bus and I’m not sure how that works – more to investigate on that front right now.