Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Home Automation with Microsoft Robotics Developer Studio 2008

Typically we think of robots as machines from science fiction or as industrial robots such as those that build and paint cars. In the world of Microsoft Robotics Developer Studio, anything that has sensors and/or actuators can be considered a robot. In this article, we look at an automated house as a robot and apply the Decentralized Software Services model of Microsoft Robotics Studio to implementing some home automation tasks.


Difficulty: Intermediate
Time Required: 6-10 hours
Cost: $100-$200 for hardware (Optional, $0 if using only simulation)
Software: Visual Studio or Visual Studio Express, Microsoft Robotics Developer Studio 2008 CTP April, ControlThink Z-Wave PC SDK
Hardware: (All Optional) Elk M1G/M1EZ Security and Automation Panel, Z-Wave Dimmer Switches, Z-Wave Controller such as the ControlThink ThinkStick or equivalent.
Download: Download

House, Robot

Those of you who know me or who have read my previous Coding4Fun article Home Automation with Windows Workflow, know that I'm nuts about home automation. I'm even more passionate about robotics and I've wanted to try out some things with Microsoft Robotics Studio since the very first version. Recently I had a long weekend, so I spent a few hours putting together some simple home automation examples using Microsoft Robotics Developer Studio 2008 CTP April. Check out the Microsoft Robotics Developer Center for details on how to get the CTP.

The examples in this article duplicate what I did using Windows Workflow in my previous article, but instead use the Decentralized Software Services (DSS) approach of Microsoft Robotics, and Microsoft Visual Programming Language (VPL) for a simple way to write automation tasks. The Microsoft Robotics Developer Center contains a lot of detailed information about DSS, VPL, and the other technologies found in Microsoft Robotics Developer Studio. For now, let's just cover some basics. To get further along with the code in this article, you'll want to check out some of the excellent tutorials presented by the Microsoft Robotics team.

Services, Messages, and Ports

A DSS Service is the basic component upon which Microsoft Robotics applications are built. In fact, DSS services are a generic construct that can and have been used outside of the context of robotics. DSS services contain state and the service state is manipulated via messages sent to the service on a service port.

Messages sent to a service are structured .NET classes that may contain a message payload that determines how or even if state will be modified, or what part of a service state should be retrieved. There are also message that do not directly manipulate state, but may have some other side-effects. Services respond to messages such as CREATE, LOOKUP, UPDATE, etc. In addition, you can define messages that extend the semantics of the core messages.

Ports are the mechanism through which services communicate. Ports accept a set of message types that are defined by the service itself. In addition, ports are also used for outbound communication in situations such as subscribing to event notification from a service.

Again, there are excellent online resources for learning the details of Microsoft Robotics, DSS, and the underlying technologies for Microsoft Robotics developer Studio. You'll probably want basic familiarity with the Microsoft Robotics architecture including the Concurrency and Coordination Runtime (CCR) along with the notion of Arbiters and iterators.

Robo-Moose

The security and automation system in my house is centered on an Elk M1G alarm panel from Elk Products Incorporated . This panel allows for up to 208 input zones in the form of contact switches, motion sensors, and so on. It also allows for up to 208 outputs, on-board task scripting and many other features. One of the key features for my use is the capability of being able to monitor and control the panel via an Ethernet adaptor. So, my first order of business was to write a DSS service – the ElkService – to communicate with the Elk M1G via sockets. This ElkService exchanges messages with the Elk M1G via an ASCII protocol that is documented here.

The code that the ElkService uses to read from the Elk M1G hardware is shown below. Note that the code looks sequential, but is actually asynchronous. The StreamAdapter.Read method sets up a task to do an asynchronous IO operation and the line “yield return (Choice)ioResultPort;” returns this task to the runtime which is iterating over all tasks returned by ElkReader.

/// <summary>
/// ElkReader opens a socket connection to the hardware panel. It then
/// enters a loop that
/// </summary>
/// <returns></returns>
public IEnumerator<ITask> ElkReader()
{
    Connect();

    // Send a request to the Elk panel get a report of the entire zone status.
    SendElkMessage(RequestStrings.ZoneStatus);

    byte[] buffer = new byte[256];

    int bytesRead = -1;
    Exception ex = null;

     do
    {
        var ioResultPort = StreamAdapter.Read(networkStream, buffer, 0, buffer.Length);

        yield return (Choice)ioResultPort;

        ex = ioResultPort;
        if (ex != null)
            throw ex;

        bytesRead = ioResultPort;
        if (bytesRead != 0)
            ProcessRawElkMessage(Encoding.ASCII.GetString(buffer, 0, bytesRead));

    } while (bytesRead != 0);
}

When the ElkService receives a packet from the hardware panel, it converts the packet from a string to a custom message containing the raw sensor data and posts the message to the main port of the ElkService itself. For example, when the service receives a “ZC” message from the hardware, it converts this to an UpdateRawZone message that contains the raw data from the security panel. This happens in the method ProcessRawElkMessage. Here is a fragment of that code:

public void ProcessRawElkMessage(string message)
{
    string messageType = message.Substring(2, 2);

    switch (messageType)
    {
        case "ZC": // Zone status change
            var zoneState = new UpdateRawZoneRequest
            {
                Id = byte.Parse(message.Substring(4, 3)),
                State = byte.Parse(message.Substring(7, 1),
                              NumberStyles.HexNumber)
            };

            var updateZoneMessage = new UpdateRawZone();
            updateZoneMessage.Body = zoneState;

            _mainPort.Post(updateZoneMessage);
            break;
…

UpdateRawZoneRequest is the message payload and is used to send data about a zone change event to a subscriber. UpdateRawZone is the actual message type that is transmitted on the port for the ElkService.

[DataContract]
[Description("UpdateRawZone Request Message Payload")]
public class UpdateRawZoneRequest
{
    [DataMember]
    public byte Id { get; set; }

    [DataMember]
    public byte State { get; set; }
}


/// <summary>
/// Update Elk zone status
/// </summary>
[Description("UpdateZone request message")]
public class UpdateRawZone : Update<UpdateRawZoneRequest,
        PortSet<DefaultUpdateResponseType, Fault>>
{
}

The message processing code can also be written a bit more compactly as:

public void ProcessRawElkMessage(string message)
{
    string messageType = message.Substring(2, 2);

    switch (messageType)
    {
        case "ZC": // Zone status change
            var updateZoneMessage = new UpdateRawZone
            {
                Body = new UpdateRawZoneRequest
                {
                    Id = byte.Parse(message.Substring(4, 3)),
                    State = byte.Parse(message.Substring(7, 1),
                          NumberStyles.HexNumber)
                }
            };

            _mainPort.Post(updateZoneMessage);
            break;
…

UpdateRawZoneHandler is a message receiver that is active on the main port and which responds to the update message by updating internal state, and notifying subscribers of the change in state.

[ServiceHandler(ServiceHandlerBehavior.Exclusive)]
public IEnumerator<ITask> UpdateRawZoneHandler(UpdateRawZone message)
{
    _state.ZoneStates[message.Body.Id - 1] = message.Body.State;

    SendNotification(_submgrPort, message);

    message.ResponsePort.Post(DefaultUpdateResponseType.Instance);

    yield break;
}

In the Zone

The ElkService represents the core service that communicates with the Elk M1G hardware. The next service – ElkZoneSensor – represents a higher level “sensor array” that is attached to the Elk hardware. ElkZoneSensor “partners” with the ElkService. This means that the ElkZoneSensor relies on the ElkService to function. The DSS infrastructure will ensure that the ElkService is started if needed when starting the ElkZoneSensor service.

The ElkZoneSensor subscribes to updates from the ElkService for changes in the hardware zones. When these changes occur, the ElkService notifies the ElkZoneSensor which updates its state and notifies any of its subscribers. The ElkZoneSensor uses a higher level message – UpdateZone – that contains a sensor ID and an enumeration that describes the state of the sensor.

[DataContract]
public class Zone
{
    [DataMember]
    [Description("The Elk hardware zone id of the sensor")]
    [DataMemberConstructor]
    public byte Id { get; set; }

    [DataMember]
    [Description("The Elk defined state of the sensor")]
    public ZoneStatus Status { get; set; }
}

Candlepower

The next services that we need for the robotic house are services to control the lights. In this case, I started with a generic contract for the DSS service. A generic contract in DSS terms just defines the set of messages to which a service will respond and defines the port type on which those messages will be sent. The generic contract does not contain code to actually implement the service behavior. One of the benefits of a generic service contract is that you can specify the contract, write algorithms that use the contract, and later connect the generic service contracts to code that actually implements the behavior. This is very much like programming to an abstract interface in the object oriented world, and having multiple classes that actually implement the interface.

We start off with a generic service called the GenericDimmer that represents a dimmer switch and we implement two concrete services: the SimulatedDimmer and the ZWaveDimmer. SimulatedDimmer just logs a message to represent its state change. The ZWaveDimmer actually controls the lights via a hardware Z-Wave controller.

Here's what the GenericDimmer contract looks like:

namespace Robotics.GenericHouseControls.Dimmer
{


    /// <summary>
    /// GenericDimmer Contract class
    /// </summary>
    public sealed class Contract
    {
        /// <summary>
        /// The Dss Service contract
        /// </summary>
        [DataMember]
        public const String Identifier = "http://schemas.tempuri.org/2008/04/generichousecontrolsdimmer.html";
    }

    /// <summary>
    /// The GenericDimmer State
    /// </summary>
    [DataContract]
    public class GenericDimmerState
    {
        [DataMember]
        [DataMemberConstructor(Order = 1)]
        public int Id;

        [DataMember]
        public int Level;
    }

    /// <summary>
    /// GenericDimmer Main Operations Port
    /// </summary>
    [ServicePort(AllowMultipleInstances = true)]
    public class GenericDimmerOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, HttpGet, On, Off, SetLevel>
    {
    }

    /// <summary>
    /// GenericDimmer Get Operation
    /// </summary>
    public class Get : Get<GetRequestType, PortSet<GenericDimmerState, Fault>>
    {

        /// <summary>
        /// GenericDimmer Get Operation
        /// </summary>
        public Get()
        {
        }

        /// <summary>
        /// GenericDimmer Get Operation
        /// </summary>
        public Get(Microsoft.Dss.ServiceModel.Dssp.GetRequestType body) :
            base(body)
        {
        }

        /// <summary>
        /// GenericDimmer Get Operation
        /// </summary>
        public Get(Microsoft.Dss.ServiceModel.Dssp.GetRequestType body, Microsoft.Ccr.Core.PortSet<GenericDimmerState, W3C.Soap.Fault> responsePort) :
            base(body, responsePort)
        {
        }
    }

    [DataContract]
    public class OnRequest
    {
    }

    [Description("Turn the switch on")]
    public class On : Update<OnRequest, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }

    [DataContract]
    public class OffRequest
    {
    }

    [Description("Turn the switch off")]
    public class Off : Update<OffRequest, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }

    [DataContract]
    public class SetLevelRequest
    {
        [DataMember]
        [DataMemberConstructor]
        public int Level { get; set; }
    }

    [Description("Set the dim level")]
    public class SetLevel : Update<SetLevelRequest, PortSet<DefaultUpdateResponseType, Fault>>
    {
    }
}

This code defines the contract only. Now to implement that contract we can run the DssNewService utility with the /implement switch and generate a service based on this contract. For example:

dssnewservice /s:SimulatedDimmer /n:Robotics.HouseControls.SimulatedDimmer /i:GenericHouseControls.Y2008.M04.Proxy.dll

will generate a new service called SimulatedDimmer in the .NET namespace Robotics.HouseControls.SimulatedDimmer. The service will have a stubbed out implementation of the GenericDimmer contract including service startup code, state, and stubbed out message handlers.

Here's what the main service file looks like for the simulated dimmer after I added logging to the generated methods. I've also deleted some of the using statements for easier reading.

using pxdimmer = Robotics.GenericHouseControls.Dimmer.Proxy;

namespace Robotics.HouseControls.SimulatedDimmer
{


    /// <summary>
    /// House Controls Service
    /// </summary>
    [DisplayName("Simulated Dimmer")]
    [Description("The Simulated Dimmer Switch Service")]
    [Contract(Contract.Identifier)]
    [AlternateContract(pxdimmer.Contract.Identifier)]
    public class SimulatedDimmerService : DsspServiceBase
    {

        /// <summary>
        /// _state
        /// </summary>
        [ServiceState]
        [InitialStatePartner(Optional = true)]
        private pxdimmer.GenericDimmerState _state = new pxdimmer.GenericDimmerState();

        /// <summary>
        /// _main Port
        /// </summary>
        [ServicePort("/simulateddimmer", AllowMultipleInstances = true)]
        private pxdimmer.GenericDimmerOperations _mainPort = new pxdimmer.GenericDimmerOperations();


        /// <summary>
        /// Default Service Constructor
        /// </summary>
        public SimulatedDimmerService(DsspServiceCreationPort creationPort) :
            base(creationPort)
        {
        }

        /// <summary>
        /// Service Start
        /// </summary>
        protected override void Start()
        {
            base.Start();

            // Add service specific initialization here.
            LogInfo(string.Format("Dimmer State => Id:{0} Level:{1}", _state.Id, _state.Level));
        }

        [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
        public IEnumerator<ITask> OnHandler(pxdimmer.On update)
        {
            LogInfo(string.Format("Simulated Dimmer => Id:{0} On", _state.Id));

            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
            yield break;
        }

        [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
        public IEnumerator<ITask> OffHandler(pxdimmer.Off update)
        {
            LogInfo(string.Format("Simulated Dimmer => Id:{0} Off", _state.Id));

            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
            yield break;
        }

        [ServiceHandler(ServiceHandlerBehavior.Exclusive)]
        public IEnumerator<ITask> SetLevelHandler(pxdimmer.SetLevel update)
        {           
            _state.Level = update.Body.Level;
            LogInfo(string.Format("Simulated Dimmer => Id:{0} Level:{1}", _state.Id, update.Body.Level));

            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);            
            yield break;
        }
    }
}

Check the code download for the implementation of the ZWaveDimmer. You'll find that it's pretty straightforward. It should be simple to write similar code if you use a different technology for lighting control. You just write a service that matches the GenericDimmer contract, and you should be able to configure the GenericDimmer using a manifest to point to the actual dimmer service. Check out the documentation in Microsoft Robotics Developer Studio for more information on manifests and how to use the manifest editor.

Bulb On, Bulb Off!

The “Hello, World” application for any home automation software that I've tested in my house has always been the automating of my pantry light. The pantry has a Z-Wave light switch inside and the pantry door has a contact switch that is connected to the Elk M1G. When the door is open, we want the light to turn on, and when the door is closed we want the light to turn off. Now that we have all the pieces for this, here's what the VPL diagram for this looks like:

image

As you can see, it was also a relatively easy matter to add text-to-speech to announce the opening and closing of the pantry door. You may find out quickly that this is an annoyance to those in the testing areas – for me that was my kitchen – so it's nice to know that two quick hits of the delete key will also remove the text-to-speech. This is part of the beauty of the Microsoft Robotics programming model!

The second example shows how you can use a motion detector – in this case connected to zone 19 on the Elk – to trigger a light. The light comes on at a level that you control from the VPL code, and stays on for a duration that you can also control from the code. For the example, I set the timer to a pretty low level, but you can customize it. You could also add additional rules to only turn on the light at certain times, or better still connect an ambient light sensor to decide whether you should turn the light on.

image

In this diagram, if motion is detected – that is zone 19 is triggered with a status of ViolatedOpen – the light is turned on at a low dim level and a timer is set. When the timer completes, the light is turned off. If, however, there is more motion before the timer completes, the SetTimer message will cancel the old timeout and restart the timer.

There are more VPL examples included in the download including an extra service that you can use to send email from your VPL code. I'm experimenting with using the email service to send me a text message whenever my alarm is armed or disarmed.

Miscellanea

Since I haven't had a lot of time to study the testing framework and simulation environments used by Microsoft Robotics, I included a service called the ElkTestDataPlayback service that allows me to replay the contents of a text file to simulate the ElkService getting actual data. This works pretty well for testing. The contents of the file are merely raw data captures that I made from the data sent by the Elk M1G panel. To use this service in VPL, you just drop it onto your diagram, configure it's TestDataFileName via “Set initial configuration” to point to a data file, and send it a message to start playback.

Important: If you have the Elk hardware, you will want to uncomment the SpawnIterator(ElkReader) line in the ElkService.Start method. I know this is a hack for now. It would probably be better to write a SimulatedElk service based on a generic service contract. If I have time, I will do that in the future. That would involve a lot more work than I had for this weekend project!

TODO List

Since this was my first foray into Microsoft Robotics Developer Studio and I had limited time to really explore the features in depth, there are a few things that I've left unimplemented. Here are some of the things that I would like to expand on in the future:

1. Support for more of the Elk features: Currently, I only support Zone Change notifications. Fuller support of the Elk hardware would not be difficult and would be beneficial to anyone wanting to use this on an ongoing basis. There's some stub code for a few other notifications if you want to add to the service.

2. Generic contracts for the Elk M1G: I didn't base the Elk services on generic contract because I'm not familiar with enough other types of security and automation panels to make the needed generalizations. It would be interesting to try to come up with something more generic. I thought about using the Generic Contact Sensor array, but I'm not sure that it models what we need. For example, a door contact is considered violated when it's open, but a water sensor is considered violated when it's closed.

3. Additional Z-Wave features: I've left out notification for lighting level changes, support for scenes and a host of other features. There's so much more that's possible with full Z-Wave support.

4. Refined Generic Contracts: Generic contracts allow developers to write code that is independent of the final hardware. That is one of the major benefits of the model used by Microsoft Robotics. You could for example have a generic light switch for simulation and testing, and based on configuration you could plug in a Z-Wave switch or an Insteon switch when it's time to deploy the actual solution.

5. Simulation Support: One of the major benefits of Microsoft Robotics Developer Studio is the simulation environment. For example, if we had a motion detector in the simulation environment, we could test some of the algorithms that I presented above by having a simulated mobile robot “violate” the motion detector's zone and trigger the corresponding VPL code.

6. Exception Handling: As I build more on the code in this article, I'll add exception handling and retry logic. Microsoft Robotics has some extremely robust features such as Causalities that make handling exceptions in a concurrent environment much easier.

7. HttpHandlers: I have a start at code for handling HTTP requests in the ZWaveController service. Handling HTTP requests is handy for building client applications that access your services without themselves having to be DSS services.

Other experiments that might be fun to try include integrating your Microsoft Robotics controlled home with remote control applications that are based on WPF or Silverlight, or perhaps integrating control from a Windows Media Center PC or Windows Home Server.

I hope you enjoy Microsoft Robotics Developer Studio as much as I have. I look forward to seeing what other people build with this fantastic technology. If people are interested, I can continue to share my home automation experiments. I think Microsoft Robotics Developer Studio will be the major tool in my home automation toolbox for quite some time.

Project Settings Note:

You will have to modify the project settings for the projects included in the House.Robot solution since your installation of Microsoft Robotics Developer Studio will be in a different directory than mine. The DssProjectMigration.exe command line tool will make short work of this. Just run it with a parameter that specifies the directory you want it to search, and it will find all of the project files in that directory and subdirectories and convert them to be buildable with your installation settings.

If you use the ElkTestDataPlayback service, in the examples, you'll need to set the path to the location of the test data file either in the initial configuration for the component, or in a manifest that configures the component. Make sure that you do not put quotes around the path name. I included a test data file from my system.

Tags:

Follow the Discussion

  • DeanDean

    Now that was interesting, a great article!  I've been wanting a suitable project to get into using the MS Robotics Studio, and have also wanted to do some work with home automation.... you have just provided me the justification haha!   Thanks, great work Smiley

  • SledgeSledge

    I agree with Dean. I am interested into home automation and robotics as well. Please, post more articles for this topic.

  • David David

    I was thinking of doing a Home Automation Project with MRDS, and was just googling some information on what would be involved with Home Automation...and here you guys beat me to it Smiley...GOOD JOB!!!

  • Delving Into Dynamics (and other cool stuff!)Delving Into Dynamics (and other cool stuff!)

    Microsoft Senior Software Architect Charles Stacy Harris was recently interviewed on .NET Rocks talking

  • EricJilotEricJilot

    Great article, hope to see more.  Love to see this working with Home Server.

  • Windows Embedded BlogWindows Embedded Blog

    From MSDN Coding4Fun - Typically we think of robots as machines from science fiction or as industrial

  • kettchkettch

    I'm thinking of doing some home automation projects, but there are a ton of different hardware platforms out there. I'm curious, it seems that the elk platform can be programmed against without too much trouble, are there any other attributes that stand out?

    Do you happen to know a good source for comparisons of the various platforms?

    Thanks, great article!

  • Cassio Rogerio EskelsenCassio Rogerio Eskelsen

    MS Visual Studio "Unusual Editions"

  • stacyhstacyh

    The Elk is very flexible and also has on-board programming capabilities and lots of hardware options. There are also companies like HAI that make similar products. One of the best sources for information is http://www.cocoontech.com.

  • DinaDina

    How can I use Microsoft Robotics Studio DSS service in VC++ Project?

  • GrangerGranger

    There are details on how to use DSS with Managed C++ on the Robotics Studio Forums. You'll get a more detailed answer if you post your question there.

  • Clint RutkasClint I'm a "developer"

    @Granger:  Thanks for the follow up.

    http://forums.microsoft.com/msdn/default.aspx?ForumGroupID=383&SiteID=1  are the MSRS forums

  • BradBrad

    Some hardware pics would have been helpful in determining if this were something I would want to undergo... Otherwise a great article =)

  • US ISV Developer Evangelism TeamUS ISV Developer Evangelism Team

    Growing up it was easy to believe that by now robots would be doing all the tasks around the house that

  • This Week on C9: Scott Hanselman and Charlie Eriksen in the house | Games MoneyThis Week on C9: Scott Hanselman and Charlie Eriksen in the house | Games Money

    PingBack from http://games-money.com/blog/2008/10/15/this-week-on-c9-scott-hanselman-and-charlie-eriksen-in-the-house/

  • Delving Into Dynamics Delving Into Dynamics

    Microsoft Senior Software Architect Charles Stacy Harris was recently interviewed on .NET Rocks talking

  • Peter Crisp Peter Crisp

    Has anyone used Microsoft Robotics Studio for theatre animation (light controllers, etc.?) and animatronics? It strikes me that an intelligent RC servo driver/sequencer could be built easily, but I can't find that anyone has tried these areas yet. I'm an engineer, not a programmer. Any students interested in the challenge perhaps?

  • Curt LefebvreCurt Lefebvre

    The code for this project doesn't appear to be available for download any more. Does anyone know where I could find it?

    Thanks,

  • Clint RutkasClint I'm a "developer"

    http://www.codeplex.com/houserobot/SourceControl/ListDownloadableCommits.aspx

  • SrikanthSrikanth

    hi every one, i hav a problem with vplHandsonlabs/lab7. This mvpl file is working properly but the problem is about the c# code. After converting the mvpl program to C#, the code is not running properly. The robot in simulated environment is not responding to recognized speech.

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.