Making Mayhem with .NET Gadgeteer

Mayhem is an open source application that supports triggers, events, and reactions.  The core Mayhem application runs on a PC, but the events and reactions it uses are interoperable with just about any platform or device.  The guys making Mayhem say that non-programmers can use it to automate anything!  This seems an invitation to try Mayhem with .NET Gadgeteer.

Here’s a screen shot of Mayhem running in Visual Studio with a couple of events (click to enlarge).

Mayhem running in Visual Studio Debugger

Mayhem running in Visual Studio Debugger

Creating events and reactions that run with Mayhem is not difficult after a look at the API Reference, which explains methods to override.  The example implemented in this article requires a static class in order to use a single instance of Paul Mineau’s XBeeClient. The XBeeClient is a driver for XBee radios.  It provides a serial interface that services two events. Mayhem persists state information when the events are turned off or Mayhem is shut down.

How to Derive Mayhem EventBase and ReactionBase Classes

The Mayhem CodePlex site describes a simple Hello-World example that pops up a message box in response to an event selected in the UI. The model uses attributes from the System.Runtime.Serialization assembly as shown in the following code.  The following ReactionBase implementation shows the required override of the Perform method.

  [DataContract]
  [MayhemModule("Popup Window", "Creates a popup window")]
  public class Mayhem2 : ReactionBase
    {
        public override void Perform()
        {
            MessageBox.Show("Hello Mayhem World");
        }
    }

The libraries needed to run Mayhem use NuGet to manage packages.  Nuget is an open source Visual Studio add-on.  Follow instructions to install the packages in Visual Studio, and add the required references.  Then run Mayhem  in the Visual Studio debugger as an exernal program to test the Mayhem class library.

Mayhem running in Visual Studio Debugger

Mayhem running in Visual Studio Debugger

Set events and reactions in the Mayhem UI. The following screen-shot shows the pop-up window created by the ReactionBase code.

Timer Event and Mayhem Pop Up Window

Timer Event and Mayhem Pop Up Window

.NET Gadgeteer Devices Automated by Mayhem Events and Reactions

Embedded microcontroller devices often run without an operating system, and they communicate with PCs in various ways.  Mayhem EventBase and ReactionBase classes can automate .NET Gadgeteer devices as in the example we’re building, which uses XBee radio communication to and from a device. The device uses .NET Gadgeteer compatible GHI Electronics Light Sensor  and Seeed Relay modules and the .NET Gadgeteer XBee adapter for XBee radio communication.

Gadgeteer Mayhem Light Sensor and Relays

Gadgeteer Mayhem Light Sensor and Relays

The relay module in this device can control AC electrical circuits that use power up to 15 Amps/120 Volts or 7 Amps/250 Volts. The light sensor is an analog device.  Mayhem automation of this device includes events that originate on the device and events that originate on the PC.  Paul Mineau wrote the XBeeClient open-source driver , available on CodePlex, that runs the XBees.

The code behind this hardware sets up the device so Mayhem can automate its modules.  The code runs the XBeeClient to communicate with the PC hosting Mayhem.  It runs a timer that reads the Light Sensor at 10 second intervals and sends  notifications when the sensor detects light greater than 92.00 percent of maximum.   It toggles the Relay that reacts to Mayhem automation events.

The source code for the .NET Gadgeteer device follows.  The timer_Tick event implements the notification that the light sensor sends when it senses light above the defined threshold.  You can set the percentage to the desired level. The xBeeClient_StringReceived event toggles the relay, which is the reaction activated on the device when it receives an event notification from Mayhem.

using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using Gadgeteer.Modules.Seeed;
using GTM.Community;

namespace MakeMayhemGadgeteer
{
    public partial class Program
    {
        GT.Timer timer;

  void ProgramStarted()
        {
            timer = new GT.Timer(10000);
            timer.Tick += new GT.Timer.TickEventHandler(timer_Tick);

            xBeeClient.StringReceived +=
                new GTM.Community.XBee.StringReceivedEventHandler(xBeeClient_StringReceived);

            multicolorLed.TurnGreen();
            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
            Debug.Print("Program Started");
        }

        void timer_Tick(GT.Timer timer)
        {
            double percent = lightsensor.ReadLightSensorPercentage();
            if (percent > 92.00)
            {
                xBeeClient.UploadStringAsync("Light sensor high");
                multicolorLed.BlinkOnce(GT.Color.Red);
            }
        }

        void xBeeClient_StringReceived(object sender, GTM.Community.XBee.StringReceivedEventArgs e)
        {
            multicolorLed.BlinkOnce(GT.Color.Red);

            if (e.Message.IndexOf("Start") != -1)
                relays.Relay1 = true;
            else
                relays.Relay1 = false;
        }

        void button_ButtonPressed(Button sender, Button.ButtonState state)
        {
            if (!button.IsLedOn)
            {
                timer.Start();
                button.TurnLEDOn();
            }
            else
            {
                timer.Stop();
                button.TurnLEDOff();
            }
        }
    }
}

Next we’ll look at the XBeeClient running on the PC.  The XBee radio receives event notifications from the .NET Gadgeteer device and sends notifications from the PC to the device.

Mayhem EventBase and ReactionBase Class Implementations

Before we can use the Mayhem EventBase and ReactionBase classes, we need a PortStatus class to manage the XBee radio connection that services the notifications.  One notification comes to the PC from the light sensor on the device and another is sent from the PC to the device to toggle a relay.  Both notifications have to use the XBee radio on COM port 9, therefore the PortStatus class contains a static boolean Status property that indicates whether the radio and port 9 are in use.  We don’t know precisely when the constructor for a static class will be called in C#, but it will be called when it is used, so  both the EventBase and ReactionBase in the Mayhem code call it in their OnBeforeLoad methods to set their own clientStatus variables.  This instantiates the XBeeClient in the PortStatus constructor so both EventBase and ReactionBase objects can use it.

    public static class PortStatus
    {
        public static XBeeClient xbeeClient;
        private static bool status;
        public static bool Status
        {
            get
            {
                return status;
            }
            set
            {
                status = value;
            }

        }

        static PortStatus()
        {
            try
            {
                if (!status)
                {
                    xbeeClient = new XBeeClient(9);
                    status = true;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLine("Port in use. Delete XBee client and reinititialize. Message: " + ex.Message);

            }

            Logger.WriteLine("Port in use. Delete XBee client and reinititialize.");
        }
    }

The ReactionBase code is activated by an event assigned in the Mayhem UI.  Then it sends a notification to the device that toggles the relay.  The Perform method is as simple as the previous pop-up window example and decorated by the [DataContract] and [MayhemModule] attributes of the System.Runtime.Serialization assembly.

Mayhem creates modules in two ways. The first case is that of instantiating a new instance, which calls the following methods in this order: OnBeforeLoad, OnLoadDefaults, OnAfterLoad. The second case deserializes a module after it is turned off or Mayhem is turned off.   Mayhem saves state and recreates objects. This case calls the following methods in this order: OnBeforeLoad, OnLoadFromSaved, OnAfterLoad.  These methods do not require implementation in every application.  This example has to maintain the XBeeClient on COM 9, so both OnBeforeLoad and OnLoadFromSaved assign the PortStatus.Status property.  This assures that the XBeeClient instance is available but that there is no attempt to instantiate it more than once.

    [DataContract]
    [MayhemModule("XBee Radio Relay", "XBee transmission to relay")]
    public class XBeeRelay : ReactionBase
    {
        private bool clientStatus;
        protected bool relayOn;

        protected override void OnBeforeLoad()
        {
            clientStatus = PortStatus.Status;
            relayOn = false;
        }

        protected override void OnLoadFromSaved()
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnDeleted()
        { }

        public override void Perform()
        {
            if (!relayOn)
            {
              PortStatus.xbeeClient.UploadStringAsync("Start Relay 1");
                relayOn = true;
            }
            else
            {
                PortStatus.xbeeClient.UploadStringAsync("Stop Relay 1");
                relayOn = false;
            }
        }

        protected override void OnEnabling(EnablingEventArgs e)
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnDisabled(DisabledEventArgs e)
        { }
    }

The EventBase implementation is siimilar to ReactionBase.  Because this class receives notifications from the device, it must assign the XBee.StringReceivedEventHandler, which it does in the OnAfterLoad method.  This assures that the XBeeClient object exists before assigning the handler.

    [DataContract]
    [MayhemModule("LightSensorHigh", "Event when light sensor is high")]
    public class LightSensorEvent : EventBase
    {
        private bool clientStatus;

        protected override void OnBeforeLoad()
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnLoadFromSaved()
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnAfterLoad()
        {
            if (PortStatus.Status == true)
            {
                PortStatus.xbeeClient.StringReceived +=
                    new Gadgeteer.Modules.Community.XBee.StringReceivedEventHandler(xbeeClient_StringReceived);
            }

        }

        protected override void OnDeleted()
        { }

        void xbeeClient_StringReceived(object sender, Gadgeteer.Modules.Community.XBee.StringReceivedEventArgs e)
        {
            if(e.Message.IndexOf("Light sensor high") != -1)
                Trigger();
        }

        protected override void OnEnabling(EnablingEventArgs e)
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnDisabled(DisabledEventArgs e)
        { }

    }

Complete code for both Mayhem modules

We’ve discussed all the elements of a Mayhem/ .NET Gadgeteer implementation. This is a basic example, but adaptable in many ways. The Mayhem vision is to connect everything to everything else. The team is looking for some help and sponsoring a contest.  They are offering a $3,000 prize to motivate some of you to build on their platform.

using System;
using MayhemCore;
using System.Runtime.Serialization;
using System.Windows.Forms;
using Gadgeteer.Modules.Community;

namespace Mayhem2
{
    [DataContract]
    [MayhemModule("XBee Radio Relay", "XBee transmission to relay")]
    public class XBeeRelay : ReactionBase
    {
        private bool clientStatus;
        protected bool relayOn;

        protected override void OnBeforeLoad()
        {
            clientStatus = PortStatus.Status;
            relayOn = false;
        }

        protected override void OnLoadFromSaved()
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnDeleted()
        { }

        public override void Perform()
        {
            if (!relayOn)
            {
              PortStatus.xbeeClient.UploadStringAsync("Start Relay 1");
                relayOn = true;
            }
            else
            {
                PortStatus.xbeeClient.UploadStringAsync("Stop Relay 1");
                relayOn = false;
            }
        }

        protected override void OnEnabling(EnablingEventArgs e)
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnDisabled(DisabledEventArgs e)
        { }
    }

    [DataContract]
    [MayhemModule("LightSensorHigh", "Event when light sensor is high")]
    public class LightSensorEvent : EventBase
    {
        private bool clientStatus;

        protected override void OnBeforeLoad()
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnLoadFromSaved()
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnAfterLoad()
        {
            if (PortStatus.Status == true)
            {
                PortStatus.xbeeClient.StringReceived +=
                    new Gadgeteer.Modules.Community.XBee.StringReceivedEventHandler(xbeeClient_StringReceived);
            }

        }

        protected override void OnDeleted()
        { }

        void xbeeClient_StringReceived(object sender, Gadgeteer.Modules.Community.XBee.StringReceivedEventArgs e)
        {
            if(e.Message.IndexOf("Light sensor high") != -1)
                Trigger();
        }

        protected override void OnEnabling(EnablingEventArgs e)
        {
            clientStatus = PortStatus.Status;
        }

        protected override void OnDisabled(DisabledEventArgs e)
        { }

    }

    public static class PortStatus
    {
        public static XBeeClient xbeeClient;
        private static bool status;
        public static bool Status
        {
            get
            {
                return status;
            }
            set
            {
                status = value;
            }

        }

        static PortStatus()
        {
            try
            {
                if (!status)
                {
                    xbeeClient = new XBeeClient(9);
                    status = true;
                }
            }
            catch (Exception ex)
            {
                Logger.WriteLine("Port in use. Delete XBee client and reinititialize. Message: " + ex.Message);

            }

            Logger.WriteLine("Port in use. Delete XBee client and reinititialize.");
        }
    }

    [DataContract]
    [MayhemModule("Popup Window", "Creates a popup window")]
    public class Mayhem2 : ReactionBase
    {
        public override void Perform()
        {
            MessageBox.Show("Hello Mayhem World");
        }
    }
}

, , , , , , ,

  1. #1 by MJ on June 21, 2012 - 2:40 PM

    Awesome article. Really well explained.

  2. #3 by Lin on June 21, 2012 - 7:29 PM

    I can’t use Nuget in my VS2010 C# Express.

  1. Making Mayhem con .NET Gadgeteer (Italiano) « Integral Design
  2. Making Mayhem con .NET Gadgeteer (Italiano) « Integral Design
  3. Utilizzare Mayhem con .NET Gadgeteer (Italiano) « Integral Design

Leave a comment