While building a separate demo for a talk, I decided to ‘AllJoyn’ enable the Sphero demo that I’d built in this post;
That demo produced an app which would connect over Bluetooth to a paired Sphero and it would run on PC, Phone, Raspberry PI 2 but on the IoT device specifically the app is hard-coded to do certain work with certain GPIO switches such that the Sphero can be controlled with some switches and a rotary encoder on a breadboard attached to the PI. The details are in the previous post.
I thought it might be ‘nice’ to offer the Sphero ball as a service on my local subnet via AllJoyn.
I’ve posted a bit on AllJoyn in these posts;
Windows 10, UWP, Raspberry PI 2, AllJoyn and Lightbulb Demo
Windows 10–Adding Cortana and Voice Control to the UWP/AllJoyn/IoT Core/Lightbulb Demo
and the idea that I had for my demo is to enable this sort of scenario where an IoT device acts as a gateway for a Sphero such that other devices can use it from the local subnet via AllJoyn;
I wrote an ‘AllJoyn’ interface to represent a few pieces of functionality that the Sphero can do (it can do more);
<node name="/com/taulty/lightbulb"> <interface name="com.taulty.sphero"> <method name="Rotate"> <arg name="angle" type="i" direction="in"/> </method> <method name="Forward"> <arg name="speed" type="d" direction="in"/> </method> <method name="Stop"> </method> <method name="SwitchLight"> <arg name="on" type="b" direction="in"/> </method> <method name="SetColor"> <arg name="red" type="i" direction="in"/> <arg name="green" type="i" direction="in"/> <arg name="blue" type="i" direction="in"/> </method> </interface> </node>
and I ran the AllJoynCodeGenerator on it and then brought the generated code into the solution that I already had from the previous blog post and, largely, I just added a few methods to the existing service that I’d written in that post to control the Sphero and made sure that it ran some extra code to advertise an implementation of the service defined by the interface above to the AllJoyn network. The net new code ends up being something like;
public void StartAdvertising() { // We need to attach to the AllJoyn bus. this.busAttachment = new AllJoynBusAttachment(); this.AddMetadataToAllJoynBusAttachment(); // We are given a handy, generated type that knows how to advertise // our interface implementation on that bus... this.spheroProducer = new spheroProducer(this.busAttachment); // we have implemented our lightbulb service on this MainPage class. this.spheroProducer.Service = new AllJoynSpheroService(this); // let it go! this.spheroProducer.Start(); } void AddMetadataToAllJoynBusAttachment() { this.busAttachment.AboutData.DateOfManufacture = DateTime.Now; this.busAttachment.AboutData.DefaultAppName = "Sphero Control Service"; this.busAttachment.AboutData.DefaultDescription = "Control Your Sphero"; this.busAttachment.AboutData.DefaultManufacturer = "Mike Taulty"; this.busAttachment.AboutData.ModelNumber = "Version 1"; this.busAttachment.AboutData.SoftwareVersion = "1.0"; this.busAttachment.AboutData.SupportUrl = new Uri("http://www.mtaulty.com"); } void StopAdvertising() { this.spheroProducer.Stop(); this.spheroProducer.Dispose(); this.busAttachment.Disconnect(); this.busAttachment = null; } AllJoynBusAttachment busAttachment; spheroProducer spheroProducer;
and I attempt to call those Start/Stop methods from the existing code I had for handling when the Sphero connects/disconnects;
void OnSpheroConnectionStateChanged(Robot sender, ConnectionState state) { var handlers = this.SpheroConnectionChanged; if ((state == ConnectionState.Disconnected) || (state == ConnectionState.Connected)) { handlers(this, EventArgs.Empty); } switch (state) { case ConnectionState.Failed: break; case ConnectionState.Disconnected: // NEW! this.StopAdvertising(); break; case ConnectionState.Connecting: break; case ConnectionState.Connected: // NEW! this.StartAdvertising(); break; default: break; } }
The only other addition was to write a little implementation of the WinRT interface generated from that previous XML file that, largely, just points back to the implementation I already had. That’s the class AllJoynSpheroService referred to from the code snippet above and it does;
namespace BigWhiteBall { using com.taulty.sphero; using Services; using System; using System.Threading.Tasks; using Windows.Devices.AllJoyn; using Windows.Foundation; class AllJoynSpheroService : IspheroService { IAppInternalSpheroService spheroService; public AllJoynSpheroService(IAppInternalSpheroService spheroService) { this.spheroService = spheroService; } // These are the public interface members that deal in WinRT types. public IAsyncOperation<spheroForwardResult> ForwardAsync( AllJoynMessageInfo info, double speed) { return (this.InternalForwardAsync(speed).AsAsyncOperation()); } public IAsyncOperation<spheroRotateResult> RotateAsync( AllJoynMessageInfo info, int angle) { return (this.InternalRotateAsync(angle).AsAsyncOperation()); } public IAsyncOperation<spheroSetColorResult> SetColorAsync( AllJoynMessageInfo info, int red, int green, int blue) { return (this.InternalColorAsync(red, green, blue).AsAsyncOperation()); } public IAsyncOperation<spheroStopResult> StopAsync( AllJoynMessageInfo info) { return (this.InternalStopAsync().AsAsyncOperation()); } public IAsyncOperation<spheroSwitchLightResult> SwitchLightAsync( AllJoynMessageInfo info, bool onOff) { return (this.InternalLightAsync(onOff).AsAsyncOperation()); } // These are the internal implementations that deal in .NET types. async Task<spheroForwardResult> InternalForwardAsync(double speed) { this.spheroService.Drive((float)speed); return (spheroForwardResult.CreateSuccessResult()); } async Task<spheroStopResult> InternalStopAsync() { this.spheroService.Stop(); return (spheroStopResult.CreateSuccessResult()); } async Task<spheroRotateResult> InternalRotateAsync(int angle) { this.spheroService.Rotate(angle); return (spheroRotateResult.CreateSuccessResult()); } async Task<spheroSetColorResult> InternalColorAsync(int red, int green, int blue) { this.spheroService.SetColour((byte)red, (byte)green, (byte)blue); return (spheroSetColorResult.CreateSuccessResult()); } async Task<spheroSwitchLightResult> InternalLightAsync(bool on) { this.spheroService.SwitchLight(on); return (spheroSwitchLightResult.CreateSuccessResult()); } } }
I shoe-horned this in somewhat in all honesty but it works for my demo purposes.
Now, when I run this code I get;
and then once the Sphero is found, I can control him via the UI as before;
but I can run up the AllJoyn Explorer ( from here ) and I can see that the Sphero is on the network;
with its interface;
and I can manipulate it;
and drive it (literally) over AllJoyn.
Now, the UI in the app here is still talking directly to the Sphero over Bluetooth but it’s pretty easy to now break that out of the app such that the app becomes 2 (or has 2 modes perhaps) – one for talking to the Sphero directly over Bluetooth and the other for talking to a remote Sphero that it finds on the network as an AllJoyn service.
I’ve done that breaking apart for a slightly more involved demo that I’m showing at a few events in the coming weeks – it’s about 20 minutes additional work.
Meanwhile, here’s the code if you’re at all interested in these bits (remember that if you run this on an IoT device then it has a bunch of code which tries to open up various GPIO pins and so on and so might not work unless your breadboard is set up the way that I set mine up).