Gadgeteer Home Automation System

Posted by Marco Minerva

In this post I want to describe how to create a Home Automation System that puts together many of the modules and source code we talked about on this blog during the past months. In particular,  we’ll create a Gadgeteer device that uses touch screen capabilities of Display T35 to provide access to all the functionalities of the system.

So, let’s start. Connect the following modules to a FEZ Spider Mainboard:

  • USB ClientDP;
  • WiFi RS21, to retrieve current time from the Internet;
  • Display T35, to manage the system using touch;
  • SD Card, to store the configuration file of the application;
  • Temp & Humidity, to show an example of information that can be acquired using sensors;
  • Relays, to control eletrical devices;
  • Camera, with which we can show, for example, people that ring our bell.

You can see the result in following screenshot.

The Gadgeteer device schema in the Designer

The Gadgeteer device schema in the Designer

Almost all the FEZ Spider sockets are filled. As said before, we’ll use source code from some articles that have been published on this blog. In particular:

Moreover, we need to create some custom fonts for the application. .NET Micro Framework SDK provides a command line tool that allows to do this. Alternatively, you can use the Tiny Font Tool GUI to make things simpler. In the ZIP file attached to this article you can find all the fonts we’ll use.

The project consists of many files, as you can see in the Solution Explorer:

The project in the Solution Explorer

The project in the Solution Explorer

Let’s see how the application works:

private const string CONFIG_FILE_PATH = "HomeAutomation.config";
private DispatcherTimer tmrIdleTimer;
private Window window;

// This method is run when the mainboard is powered up or reset.
void ProgramStarted()
{
    this.InitializeDisplay();
    this.ReadConfiguration();

    // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
    Debug.Print("Program Started");
}

private void InitializeDisplay()
{
    window = display.WPFWindow;
    window.Background = new SolidColorBrush(GT.Color.White);
    window.Child = WindowsManager.StartupScreen;
}

CONFIG_FILE_PATH contains the name of the file, on the SD Card, that holds the configuration settings. This is a standard .NET XML configuration file that looks like the following:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="NetworkSsid" value="YOUR_NETWORK_SSID" />
    <add key="NetworkPassphrase" value="YOUR_NETWORK_PASSPHRASE" />
    <add key="DegreeType" value="C"/>
    <add key="TimeZoneOffset" value="60"/>
    <add key="TimeFormat" value="HH:mm"/>
    <add key="IdleScreenTimeout" value="60"/>
    <add key="IdleScreenTimeFormat" value="HH:mm:ss"/>
    <add key="IdleScreenDateFormat" value="dd/MM/yyyy"/>
  </appSettings>
</configuration>

tmrIdleTimer is the timer used to check when the application is idle, so that the idle screen is shown. window contains a reference to the WPF window that represent the display. We will see how these variables are used in a moment.

In the ProgramStarted method, we initialize the display and show the Startup screen, that simply contains an hourglass and a label that notifies initialization progress. The window is retrieved using the static WindowManager class, that holds references to all the windows defined in the application. You can see its declaration in the ZIP file that comes with this article. After that, we call the ReadConfiguration method, that reads configuration settings from the SD Card:

private void ReadConfiguration()
{
    if (sdCard.IsCardMounted)
    {
        try
        {
            WindowsManager.StartupScreen.SetStatusMessage("Reading configuration...");

            // Reads configuration file.
            using (Stream configStream =
                sdCard.GetStorageDevice().OpenRead(CONFIG_FILE_PATH))
            {
                // Loads settings.
                ConfigurationManager.Load(configStream);
            }

            // Retrieves configuration values.
            AppSettings.IdleScreenTimeout = int.Parse(
                ConfigurationManager.GetAppSetting("IdleScreenTimeout", "60"));
            AppSettings.NetworkSSID = ConfigurationManager.GetAppSetting("NetworkSsid");
            AppSettings.NetworkPassphrase = ConfigurationManager.GetAppSetting("NetworkPassphrase");

            // ...

            // Makes the Wi-Fi connection.
            WindowsManager.StartupScreen.SetStatusMessage("Acquiring network address...");

            WiFi_RS21.WiFiNetworkInfo info = new WiFi_RS21.WiFiNetworkInfo();
            info.SSID = AppSettings.NetworkSSID;
            info.SecMode = WiFi_RS21.SecurityMode.WPA2;
            info.networkType = WiFi_RS21.NetworkType.AccessPoint;

            wifi.NetworkUp += new GTM.Module.NetworkModule.NetworkEventHandler(wifi_NetworkUp);

            wifi.UseDHCP();
            wifi.Join(info, AppSettings.NetworkPassphrase);
        }

    // ...

}

This piece of code shows the use of ConfigurationManager to retrieve settings from configuration file and save them in the AppSettings class. In particular, note that we read network SSID and password and then we establish a Wi-Fi connection. When connection is available, in the NetworkUp event we try to synchronize the clock using an Internet time service:

private void wifi_NetworkUp(GTM.Module.NetworkModule sender, GTM.Module.NetworkModule.NetworkState state)
{
    // Network is available.
    AppSettings.IPAddress = wifi.NetworkSettings.IPAddress;

    // Synchronize the Clock with a Reference Server on the Internet.
    WindowsManager.StartupScreen.SetStatusMessage("Synchronizing time...");
    TimeManager timeManager = TimeManager.Instance;
    timeManager.TimeSynchronized += new EventHandler(timeManager_TimeSynchronized);
    timeManager.Synchronize(AppSettings.TimeZoneOffset);
}

The TimeManager class uses the TimeService class, as described in the post Synchronize the Clock with a Reference Server on the Internet. When the time is obtained, the TimeSynchronized event is raised; all the configurations have been completed, so we can show the main window of the application:

void timeManager_TimeSynchronized(object sender, EventArgs e)
{
    if (!(sender as TimeManager).FirstTimeSynchronized)
    {
        Dispatcher.BeginInvoke(delegate(object arg)
        {
            // Initialization has successfully completed.
            TempHumidityManager.Initialize(temperatureHumidity, AppSettings.DegreeType);
            RelaysManager.Initialize(relays);
            CameraManager.Initialize(camera);
            window.Child = WindowsManager.MainWindow;

            WindowsManager.MainWindow.TouchDown +=
                new TouchEventHandler(MainWindow_TouchDown);
            WindowsManager.IdleWindow.TouchDown +=
                new TouchEventHandler(IdleWindow_TouchDown);

            tmrIdleTimer = new DispatcherTimer();
            tmrIdleTimer.Interval = TimeSpan.FromTicks
                (AppSettings.IdleScreenTimeout * TimeSpan.TicksPerSecond);
            tmrIdleTimer.Tick += new EventHandler(tmrIdleTimer_Tick);
            tmrIdleTimer.Start();
            return null;
        },
        null);
    }
}

Note that we execute this code only if it is the first time synchronization, because the TimeManager periodically asks for time updates, and every time raises this event, but we need to actually initialize the system only once. We call the Initialize method on TempHumidityManager, RelaysManager and CameraManager classes, passing the corresponding module to them. These static classes are used to access modules and their information from outside the Program.cs class.

After that, we set the Child property of display window to WindowsManager.MainWindow, in order to show the main window of the application.  Then, we register on the TouchDown event of MainWindow and IdleWindow: the latter is shown when the touch screen isn’t used for the amount of time that is specified in the IdleScreenTimeout property of the configuration file. This check is done with the trmIdleTimer timer:

void tmrIdleTimer_Tick(object sender, EventArgs e)
{
    tmrIdleTimer.Stop();
    window.Background = new SolidColorBrush(GT.Color.Black);
    window.Child = WindowsManager.IdleWindow;
}

When the TouchDown event occurred in the main window, we reset the timer, in order to restart the idle timeout. In the same way, when the user touches the idle window, the main window is shown and the timer is started again:

void MainWindow_TouchDown(object sender, Microsoft.SPOT.Input.TouchEventArgs e)
{
    // Stops and starts the idle timer, i.e. restarts it.
    tmrIdleTimer.Stop();
    tmrIdleTimer.Start();
}

void IdleWindow_TouchDown(object sender, Microsoft.SPOT.Input.TouchEventArgs e)
{
    // When a touch events occurs in the Idle window, the Main window is automatically shown.
    window.Background = new SolidColorBrush(GT.Color.White);
    window.Child = WindowsManager.MainWindow;
    tmrIdleTimer.Start();
}

This completes our overview of the main program file. Now let’s take a look on the MainWindow class, that contains the core part of the application. Basically, it consists of a title bar, a central area with which the user can interact, and a status bar. Is it created as described in the post Windows-like Interface for the .NET Gadgeteer Display. It contains three main icons, that corresponds to the functions of the application. Every icon is created using the CreateMenuItem method:

private void CreateMenuItem(Bitmap bitmap, TouchEventHandler handler,
    int iconMarginLeft, string labelContent, int labelMarginLeft)
{
    var image = new Image(bitmap);
    image.TouchDown += handler;
    canvasMenu.Children.Add(image, 0, iconMarginLeft);

    var label = new Text()
    {
        Font = FontManager.Arial10Bold,
        Width = 120,
        TextAlignment = TextAlignment.Center,
        TextWrap = true,
        TextContent = labelContent
    };
    label.TouchDown += handler;
    canvasMenu.Children.Add(label, 54, labelMarginLeft);
}

It takes five parameters:

  • the bitmap to use as menu icon;
  • the handler to the event that is raised when the icon is touched;
  • the left margin of the image (as we’re using a canvas, we need absolute positioning);
  • the text that is shown under the icon;
  • the left margin of the text.

The font of the label is retrieved using FontManager, a static class that provides access to all the fonts that has been included in the project.

Our application has three sections: Temperature & Humidity, Relays and Camera. So, we need to create three menu items:

this.CreateMenuItem(Resources.GetBitmap(Resources.BitmapResources.Temp_Humidity_Menu),
    new TouchEventHandler(TemperatureHumidity_TouchDown), 22,
    "Temperature & Humidity", 0);

this.CreateMenuItem(Resources.GetBitmap(Resources.BitmapResources.Switches_Menu),
    new TouchEventHandler(Switches_TouchDown), 130,
    "Switches", 100);

this.CreateMenuItem(Resources.GetBitmap(Resources.BitmapResources.Webcam_Menu),
    new TouchEventHandler(Camera_TouchDown), 230,
    "Camera", 200);

The result is shown in the following screenshot.

The main application window

The main application window

In the TouchDown events, we show or hide the panel that corresponds to the selected functionality. Each panel is a Canvas object. For example, here it is the Temperature & Humidity panel definition:

private void CreateTemperatureHumidityPanel()
{
    temperatureHumidityCanvas = new Canvas() { Height = 240, Width = 320 };
    CreateBackButton(temperatureHumidityCanvas,
        "Temperature & Humidity",
        new TouchEventHandler(TemperatureHumidityBackButton_TouchDown));

    var imgTemperature =
        new Image(Resources.GetBitmap(Resources.BitmapResources.Temperature));
    txtTemperature = new Text() { Font = FontManager.Arial16Bold };
    temperatureHumidityCanvas.Children.Add(imgTemperature, 63, 20);
    temperatureHumidityCanvas.Children.Add(txtTemperature, 83, 80);

    var imgHumidity =
        new Image(Resources.GetBitmap(Resources.BitmapResources.Humidity));
    txtHumidity = new Text() { Font = FontManager.Arial16Bold };
    temperatureHumidityCanvas.Children.Add(imgHumidity, 70, 200);
    temperatureHumidityCanvas.Children.Add(txtHumidity, 83, 255);

    temperatureHumidityCanvas.Visibility = Visibility.Collapsed;
    this.AddChild(temperatureHumidityCanvas, 30, 0);
}

The CreateBackButton creates a button and a label that allows to return to main menu. The txtTemperature and txtHumidity controls show the values that come from the TemperatureHumidity module. In fact, the window contains a timer, tmrUpdate, that fires every 500 milliseconds and sets the txtTime.TextContent property with the current time, using the time format that has been specified in the configuration file. Then, it uses the TempHumidityManager to retrieve temperature and humidity:

void tmrUpdate_Tick(object sender, EventArgs e)
{
    txtTime.TextContent = DateTime.Now.ToString(AppSettings.TimeFormat);
    txtTemperature.TextContent = TempHumidityManager.Temperature.ToString("f1")
        + TempHumidityManager.TemperatureDegreeSymbol;
    txtHumidity.TextContent = (int)TempHumidityManager.Humidity + "%";
}

Here is the result:

The Temperature & Humidity panel

The Temperature & Humidity panel

The Switches panel is defined in a similar way:

private void CreateSwitchesPanel()
{
    switchesCanvas = new Canvas() { Height = 240, Width = 320 };
    CreateBackButton(switchesCanvas,
        "Switches",
        new TouchEventHandler(SwitchesBackButton_TouchDown));

    StackPanel panel = new StackPanel() { Orientation = Orientation.Horizontal };

    imgSwitch1 = new Image(OffBitmap);
    imgSwitch2 = new Image(OffBitmap);
    imgSwitch3 = new Image(OffBitmap);
    imgSwitch4 = new Image(OffBitmap);

    panel.Children.Add(this.CreateSwitchButtonPanel(imgSwitch1, 1));
    panel.Children.Add(this.CreateSwitchButtonPanel(imgSwitch2, 2));
    panel.Children.Add(this.CreateSwitchButtonPanel(imgSwitch3, 3));
    panel.Children.Add(this.CreateSwitchButtonPanel(imgSwitch4, 4));

    switchesCanvas.Children.Add(panel, 60, 0);

    switchesCanvas.Visibility = Visibility.Collapsed;
    this.AddChild(switchesCanvas, 30, 0);
}

We create a StackPanel in which we insert four images that act as switches to turn the relays on or off. When each switch is pressed, the following method is raised:

void switch_TouchDown(object sender, TouchEventArgs e)
{
    var image = sender as Image;
    bool newRelayStatus = false;

    if (image == imgSwitch1)
    {
        RelaysManager.RelaysModule.Relay1 = !RelaysManager.RelaysModule.Relay1;
        newRelayStatus = RelaysManager.RelaysModule.Relay1;
    }
    else if (image == imgSwitch2)
    {
        RelaysManager.RelaysModule.Relay2 = !RelaysManager.RelaysModule.Relay2;
        newRelayStatus = RelaysManager.RelaysModule.Relay2;
    }
    else if (image == imgSwitch3)
    {
        RelaysManager.RelaysModule.Relay3 = !RelaysManager.RelaysModule.Relay3;
        newRelayStatus = RelaysManager.RelaysModule.Relay3;
    }
    else if (image == imgSwitch4)
    {
        RelaysManager.RelaysModule.Relay4 = !RelaysManager.RelaysModule.Relay4;
        newRelayStatus = RelaysManager.RelaysModule.Relay4;
    }

    if (newRelaysStatus)
        image.Bitmap = OnBitmap;
    else
        image.Bitmap = OffBitmap;
}

We check what image has been pressed and turn on or off the corresponding relay, using the RelaysManager class. Then, we change the image to graphically reflects the relay status.

The Switches panel

The Switches panel

Finally, there is the Camera panel:

private void CreateCameraPanel()
{
    CameraManager.CameraModule.BitmapStreamed +=
        new GT.Modules.GHIElectronics.Camera.BitmapStreamedEventHandler
            (CameraModule_BitmapStreamed);

    cameraImage = new Image();
    cameraImage.TouchDown += new TouchEventHandler(cameraImage_TouchDown);
    cameraImage.Visibility = Visibility.Collapsed;
    this.AddChild(cameraImage, 0, 0);
}

void CameraModule_BitmapStreamed(GT.Modules.GHIElectronics.Camera sender, Bitmap bitmap)
{
    cameraImage.Bitmap = bitmap;
}

We register the BitmapStreamed event of the Camera module (exposed by the CameraManager class), and we add an Image to the panel. When the event is raised, we show the bitmap in the control.

Before in this article we have said that, if the application isn’t used for the amout of time that is specified in the IdleScreenTimeout property, the Idle window appears. It simply shows time, date, temperature and humidity in a single window:

The idle window

The idle window

This completes the overview of our system. Now we can turn on the Gadgeteer device and see how it works:

The complete Gadgeteer application is available for download.

HomeAutomation.zip

, , , , , ,

  1. #1 by Lin on September 26, 2012 - 1:53 AM

    HI
    Does this project you developed is Gadgeteer 4.1 or 4.2? What is your SDK version?
    Because when I open your project in VS2010 Express, It shows many error.
    Loss the Seeed driver,and can’t see mamy picture of the modules.
    I have the latest SDK of GHI.
    I also can’t use the Wifi module in Gadgeteer 4.2 Project.
    The error picture is below

    Thank you very much, learn many form your blog.

    • #2 by Marco Minerva on September 26, 2012 - 2:01 AM

      The project is developed using Gadgeteer and SDK 4.1. The 4.2 version is still in beta and doesn’t support all modules yet, like the Wi-Fi.

      • #3 by Lin on September 26, 2012 - 2:05 AM

        Thanks, But now my Spider is 4.2,

        If I create the 4.1 Project, when deploying, it will shows ” Error 1 Cannot deploy the base assembly ‘mscorlib’, or any of his satellite assemblies, to device – USB:Gadgeteer twice. Assembly ‘mscorlib’ on the device has version 4.2.0.0, while the program is trying to deploy version 4.1.2821.0″ .
        How to solve it?
        Thanks.

      • #4 by Marco Minerva on September 26, 2012 - 2:14 AM

        You need to use the firmware for 4.1 version. Also, make sure your project is targeting the right Framework version, as shown here: http://wiki.tinyclr.com/index.php?title=NETMF_4.1_to_4.2

  2. #5 by Create Automation on November 8, 2012 - 7:20 AM

    What is the version of your project?

    • #6 by Marco Minerva on November 8, 2012 - 7:34 AM

      Do you mean the .NET Micro Framework I have used? I have worked with .NET MF and Gadgeteer Package version 4.1.

  3. #7 by Scott Coleman on February 8, 2013 - 2:29 PM

    Any idea when the 4.2 version will be available?

  4. #8 by VS on February 25, 2013 - 8:09 AM

    With version 4.2 problem with the line Ressources.designer.cs return ((Microsoft.SPOT.Bitmap) (Microsoft.SPOT.ResourceUtility.GetObject (ResourceManager, id)));
    I’m not very undersheet with C # (and English ..)

  1. Interview Maker - Le blog SYNERGIZ

Leave a comment