Switch your lights with LightSwitch

LightOn

With a name like LightSwitch, you know someone had to do it eventually. Smile

Here is fun little project that combines Microsoft LightSwitch and a Microsoft Micro Framework controller to switch LEDs on and off.  Here is the hardware:

LSFritzing

  • FEZ Panda
  • Breadboard small
  • 2 LEDs, one red, one green
  • Jumper wire
  • 1 or 2 100 ohm resistors. Sharing a resistor here.

2011-06-13 14.08.17

And here is the LS screen to both control lights and see current light status:

LSLightScreen2

Below is the LS table that drives everything:

LSTable

Basically, it works as follows:

  • I hook into the tables Updating event.
  • Inside the event, I send a single line command to the NETMF device via SerialPort.
  • The NETMF device is listening on USB serial device in a loop reading lines and performs the on/off switching based on what light name is send in the command line.
  • Before leaving Updating event, I update the light status to on or off to reflect its new state.
  • After the Save, the screen reflects the new state information that was set inside the Updating event.
partial void LightStatusSet_Updating(LightStatus entity)
{
    SendLightCommand(entity);

    if (!entity.LastTurnOnTime.HasValue && entity.TurnOnNow)
    {
        // Turn on stopwatch.
        entity.LastTurnOnTime = DateTime.Now;
    }
    else if (entity.LastTurnOnTime.HasValue && ! entity.TurnOnNow)
    {
        // Turn off stopwatch.
        TimeSpan ts = (DateTime.Now - entity.LastTurnOnTime.Value);
        entity.HoursOn += ts.TotalHours;
        entity.LastTurnOnTime = null;
    }
}
private static void SendLightCommand(LightStatus entity)
{
    if (!entity.IsActive) return;
    if (port == null)
    {
        port = new SerialPort("com3");
        port.Open();
        Debug.WriteLine("Port opened. Send cmds.");
    }

    if (entity.TurnOnNow)
    {
        port.WriteLine(entity.Name + ",ON");
        entity.IsOn = true;
    }
    else
    {
        port.WriteLine(entity.Name + ",OFF");
        entity.IsOn = false;
    }
}

The device side is done same way. Just block reading lines from the SerialPort in a loop and handle the commands by name.  Here is the device code:

using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.USBClient;
using System.Text;

namespace FezPandaApp1
{
    public class Program
    {
        static OutputPort sysLed;
        static OutputPort led1;
        static OutputPort led2;

        public static void Main()
        {
            Debug.EnableGCMessages(false);
            sysLed = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, false);
            led1 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di6, false);
            led2 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di7, false);

            ReadComm();

            Debug.Print("Done.");
        }

        public static USBC_CDC StartCDC()
        {
            // Check debug interface. We don't do this check with CDC debugging port
            //if (Configuration.DebugInterface.GetCurrent() == Configuration.DebugInterface.Port.USB1)
            //    throw new InvalidOperationException("Current debug interface is USB. It must be changed to something else before proceeding. Refer to your platform user manual to change the debug interface.");

            // Start CDC
            Debug.Print("Hit Break all. Then Continue to Init CDC.");
            USBC_CDC cdc = USBClientController.StandardDevices.StartCDC_WithDebugging();

            // Send "Hello world!" to PC every second. (Append a new line too)
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes("Hello world!\r\n");

            int count = 10;
            while (count-->0)
            {
                Debug.Print("Sending on comm.");
                // Check if connected to PC
                if (USBClientController.GetState() != USBClientController.State.Running)
                {
                    Debug.Print("Waiting to connect to PC...");
                }
                else
                {
                    cdc.Write(bytes, 0, bytes.Length);
                }
                Thread.Sleep(500);
            }
            return cdc;
        }

        public static void ReadComm()
        {
            USBC_CDC port = StartCDC();
            port.ReadTimeout = -1;
            while (true)
            {
                string line = CDCReadLineEx(port, 512);
                bool result = HandleCommand(line);
                if (!result) break; // Exit cmd.
                //CDCWriteLine(port, line); // Write reply if needed.
            }
        }
        
        public static bool HandleCommand(string line)
        {
            string[] cmds = line.Split(',');
            if (cmds.Length != 2) return true;
            string cmd = cmds[0].Trim().ToLower();
            string arg1 = cmds[1].Trim().ToLower();

            switch (cmd)
            {
                case "sys":
                    if (arg1 == "on")
                        sysLed.Write(true);
                    else
                        sysLed.Write(false);
                    break;
                case "led1":
                    if (arg1 == "on")
                        led1.Write(true);
                    else
                        led1.Write(false);
                    break;
                case "led2":
                    if (arg1 == "on")
                        led2.Write(true);
                    else
                        led2.Write(false);
                    break;
                case "exit":
                    return false;
                default: // Ignore
                    break;
            }
            return true;
        }

        public static int CDCWriteLine(USBC_CDC port, string line)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(line+"\n");
            return port.Write(bytes, 0, bytes.Length);
        }

        public static string CDCReadLineEx(USBC_CDC sr, int bufLen)
        { 
            char[] readLineBuff = new char[bufLen];
            int curPos = 0;
            byte[] ba = new byte[1];
            while (true)
            {
                int read = sr.Read(ba, 0, 1);
                if (read <= 0) break;
                readLineBuff[curPos] = (char)ba[0];
                if (readLineBuff[curPos] == '\n')
                {
                    return new string(readLineBuff, 0, curPos);
                }
                curPos++;
            }

            if (curPos == 0) return null; // Null fix.
            return new string(readLineBuff, 0, curPos);
        }
    }
}

For this sample, we are just switching LEDs. But you could easily switch actual lights and other high power devices by adding Relays and/or MOSFETs to the setup.

Cool thing about using LightSwitch for this is it is already internet-ready and multi-user ready.  So deploy to the cloud and switch your lights over the internet.

So what if your LightSwitch server is hosted at a provider or not near your light sources?  Good news. You are covered there also. Instead of using a serial port, you can hook your NETMF device to Ethernet or wireless instead.  Do some port forwarding and/or open up your NAT at home and your LS server can send commands to your public facing NAT IP address which can forward to your FEZ.  You could also create a WCF RIA service on some box at home (or work) and talk to it from the LS server side.  You have many options with some imagination.  Just don’t open up your screen to the public if you don’t want your lights blinking on and off! Smile

Have fun and post blogs of your project !

–William Stacey

This entry was posted in .Net Micro Framework, C#, LightSwitch and tagged , , . Bookmark the permalink.

3 Responses to Switch your lights with LightSwitch

  1. Ok this is definitely “cool” 🙂

  2. Eric Hall says:

    How about “amping up the wattage” with a LightSwitch compatible controller pattern as a NETMF wrapper?

    EricH
    tinyclr.com

  3. Prueba Lightswitch Beta 2 says:

    Hi William

    Good Job, with a good dose of imagination, created something practical.

    congratulations

    Jaime

Comments are closed.