more G-Labs products

Author Topic: Communicate from MCU to HomeGenie, simple protocol  (Read 5678 times)

February 04, 2014, 01:44:45 PM
Read 5678 times

mrx23dot

  • *
  • Information
  • Newbie
  • Posts: 16
Hi All,

Which is the simplest protocol to communicate with HG on Windows, through:
MCU->UART->USB->Virtual COM port->HomeGenie?

Do I need to emulate a CM15A at least?
For starters I need to send Lamp on/off commands.

Thanks,
Toth

February 04, 2014, 02:31:15 PM
Reply #1

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
You can use HG WebService API.

Some examples for controlling X10/Z-Wave:

Turn on X10 unit A5
/api/HomeAutomation.X10/A5/Control.On

Turn off Z-Wave node 8
/api/HomeAutomation.ZWave/8/Control.Off

Is there a specific reason for using a virtual com port?

February 04, 2014, 02:59:05 PM
Reply #2

mrx23dot

  • *
  • Information
  • Newbie
  • Posts: 16
Hi,

Thanks for the reply.
I need the fastest way to start developing/communicating with HG.

Virtual Serial port is the fastest way for beginners to get started e.g. with an Arduino. (also avoiding X10 high voltages, and Zwave?s high cost). Remote control from MCU/sensor data collection.

My long term plan is adding VSCP with CAN bus protocol (most robust protocols for DIY HA) for HG.

So I think I will need Virtual Serial Port ? websocket gateway to get things started.
Do I need authentication/framework or anything special? 

A big software architecture image on docs page would be really helpful for new developers.

Thanks,

February 04, 2014, 05:17:52 PM
Reply #3

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
Ok... we have a couple of options that I'll try to describe briefly here.

OPTION 1
Using Raspberry Pi builtin serial port  ( /dev/ttyAMA0 )

(http://lavalink.com/wp-content/uploads/2012/04/raspberry-pi-serial_sm-241x300.jpg)

To handle such kind of I/O, we'll be creating an Automation Program Plugin from Configure->Automation that will be using the SerialPortHelper:

Code: [Select]
var portname = "/dev/ttyAMA0";

Action<string>
HandleStringReceived = (string message) => {
 
  // this will be called every time a message is received from serial port
Program.Notify("SerialPort String", message);
 
};

Action<byte[]>
HandleMessageReceived = (byte[] message) => {
 
  // this will be called every time a message is received from serial port
Program.Notify("SerialPort Bytes", BitConverter.ToString(message));
 
};

Action<bool>
HandleStatusChanged = (bool connected) => {
 
  // this will be called every time the connection status changes
  Program.Notify("SerialPort Status", connected ? "CONNECTED!" : "DISCONNECTED!");
 
};

// open the serial port channel and register handlers
SerialPort
  .WithName( portname )
  .OnStatusChanged( HandleStatusChanged )
  .OnMessageReceived( HandleMessageReceived )
  .OnStringReceived( HandleStringReceived )
  .Connect( 9600 ); // change baud rate if needed

while (Program.IsEnabled)
{
  // send a test message through serial port every 10 seconds
  //
  byte[] message = { 0x00, 0x00, 0x00, 0x00, 0x00 };
  SerialPort.SendMessage(message);
  //
  Pause(5);
  SerialPort.SendMessage("Hello Things!");
  //
  Pause(10);
}

We can also give an higher level interface to control the communication between the two endpoints by implementing a custom WebService as shown in the following example code:

Code: [Select]
//
// TODO: Initialize serial port here...
//
When.WebServiceCallReceived("Arduino.Switches", ( args ) =>
{
string[] reqs = ((string)args).Split('/');
var res = "{ 'ResponseValue' : 'ERROR' }";
//
try
{
string switchid = reqs[1];
    string command = reqs[2];
    // get reference to the virtual module
    var module = Modules.InDomain("Arduino.Switches").WithAddress(switchid).Get();
  //
    switch(command)
    {
      // eg. http://hg_address/api/Arduino.Switches/1/Control.On
      case "Control.On":
      // TODO: Send command by serial port to the arduino to turn on the switchid
Program.RaiseEvent(module, "Status.Level", "1", "Switch " + switchid);
res = "{ 'ResponseValue' : 'OK' }";
break;
          // eg. http://hg_address/api/Arduino.Switches/2/Control.Off
      case "Control.Off":
      // TODO: Send command by serial port to the arduino to turn off the switchid
Program.RaiseEvent(module, "Status.Level", "0", "Switch " + switchid);
res = "{ 'ResponseValue' : 'OK' }";
break;
    }
}
catch (Exception ex)
{
    res = ex.Message + " " + ex.StackTrace;
}
// unable to process request
return res;
});
//
Program.GoBackground();


OPTION 2
Using Raspberry Pi SPI interface

(http://hackadaycom.files.wordpress.com/2013/01/raspi_arduino_spi.png%3Fw%3D580%26h%3D378)

To handle lower level communication (SPI / I2C and simple GPIO) we can use the RaspberrySharp helper class:

Code: [Select]
var adcClock = ConnectorPin.P1Pin23.ToProcessor();
var adcMiso = ConnectorPin.P1Pin21.ToProcessor();
var adcMosi = ConnectorPin.P1Pin19.ToProcessor();
var adcCs = ConnectorPin.P1Pin22.ToProcessor();

var spiConnection = new Raspberry.IO.SerialPeripheralInterface.SpiConnection(adcClock, adcCs, adcMiso, adcMosi, 0 /*Endianness.LittleEndian*/);

using(spiConnection.SelectSlave())
{
 
  // Start bit
  spiConnection.Write(true);
 
  // Channel is single-ended
  spiConnection.Write(true);
 
  // Channel Id
  spiConnection.Write((byte)2, 3);

  // Let one clock to sample
  spiConnection.Synchronize();
 
  while (true)
  {
    // Read 8 bits
    for (int i = 0; i < 8; i++)
    {
      var data = spiConnection.Read(8);
      Console.Write( data.ToString("X2") + " "); 
    }
    Console.WriteLine("");
  }
}

Using Raspberry Pi I2C bus

This is a working example for MCP23017 GPIO extender. It also implement a WebService for higher level controlled I/O:

Code: [Select]
// This examples will add 16 modules of type "Switch"
// corresponding to A0-A7 and B0-B7 pins, configured as OUTPUT
//
// Change SDA and SCL pins to match your current setup
//
var sdaPin = ConnectorPin.P1Pin03.ToProcessor();
var sclPin = ConnectorPin.P1Pin05.ToProcessor();

//

// implement Control.On, Control.Off, Control.Toggle serice API
// for the domain Components.MCP23017
When.WebServiceCallReceived("Components.MCP23017", ( args ) =>
{
string[] reqs = ((string)args).Split('/');
  var errormessage = "";
try
{
string pinid = reqs[1];
      string command = reqs[2];
        //
        var module = Modules.InDomain("Components.MCP23017").WithAddress(pinid).Get();
      //module.Parameter("Status.Level").Value = "0";
        //
      // modules with id from 1 to 8 are mapped as A0-A7
      // modules with id from 9 to 16 as B0-B7
      var pinname = "";
      var i = int.Parse(pinid);
      if (i <= 8)
        {
          pinname = "A" + (i - 1).ToString();
        }
      else
        {
          pinname = "B" + (i - 9).ToString();
        }     
      //
var mcppin = (Mcp23017Pin)Enum.Parse(typeof(Mcp23017Pin), pinname);
        using (var driver = new I2cDriver(sdaPin, sclPin))
        {
            var deviceConnection =  new Mcp23017I2cConnection(driver.Connect(0x20));
            deviceConnection.SetDirection(mcppin, Mcp23017PinDirection.Output);
            //
            switch(command)
            {
                // eg. http://hg_address/api/Expanders.MCP23017/1/Control.On
                case "Control.On":
deviceConnection.SetPinStatus(mcppin, true);
                break;
                // eg. http://hg_address/api/Expanders.MCP23017/3/Control.Off
                case "Control.Off":
deviceConnection.SetPinStatus(mcppin, false);
                break;
                // eg. http://hg_address/api/Expanders.MCP23017/9/Control.Toggle
              case "Control.Toggle":
deviceConnection.Toogle(mcppin);
              break;
            }
Program.RaiseEvent(module, "Status.Level",
                               deviceConnection.GetPinStatus(mcppin) ? "1" : "0", "MCP23017 " + pinname);
        }       
      //
        return "{ 'ResponseValue' : 'OK' }";   
    }
  catch (Exception ex)
    {
    errormessage = ex.Message + " " + ex.StackTrace;
    }
    // unable to process request
    return "{ 'ResponseValue' : 'ERROR " + errormessage + "' }";
});

Program.GoBackground();

Using simple GPIO operations

The following example is used in the HomeGenie Raspberry Pi Board for controlling the two leds:

Code: [Select]
// Initially turn the leds off
var systemleds = new PinConfiguration[] { ConnectorPin.P1Pin15.Output().Name("ActivityLed").Disable(),
                                          ConnectorPin.P1Pin13.Output().Name("ReadyLed").Disable() };
//
var ledsname = new Dictionary<string, string>() { {"1", "ReadyLed"}, {"2", "ActivityLed"} };
var ledbusy = false;
using (var connection = new GpioConnection(systemleds))

When.ModuleParameterChange( (module, parameter) => {
      if (!ledbusy && module.Instance.Domain != "HomeGenie.Leds")
        {
          ledbusy = true;
            connection.Blink("ActivityLed", 10); // 10 ms
            ledbusy = false;
        }
      return true;
});
 
   
    When.WebServiceCallReceived("HomeGenie.Leds", ( args ) =>
    {
        string[] reqs = ((string)args).Split('/');
        var res = "{ 'ResponseValue' : 'ERROR' }";
   
      //Command cmd = new Command((string)args);
        try
        {
            string command = reqs[2];
    string ledid = reqs[1];
            var module = Modules.InDomain("HomeGenie.Leds").WithAddress(ledid).Get();
          //
            switch(command)
            {
              // eg. http://hg_address/api/HomeGenie.Leds/1/Control.On
              case "Control.On":
              connection[ledsname[ledid]] = true;
                Program.RaiseEvent(module, "Status.Level", "1", "Led " + ledid);
                res = "{ 'ResponseValue' : 'OK' }";
                break;
              // eg. http://hg_address/api/HomeGenie.Leds/2/Control.Off
              case "Control.Off":
              connection[ledsname[ledid]] = false;
                Program.RaiseEvent(module, "Status.Level", "0", "Led " + ledid);
                res = "{ 'ResponseValue' : 'OK' }";
                break;
              // eg. http://hg_address/api/HomeGenie.Leds/1/Control.Toggle
              case "Control.Toggle":
              connection[ledsname[ledid]] = !connection[ledsname[ledid]];
                Program.RaiseEvent(module, "Status.Level", connection[ledsname[ledid]] ? "1" : "0", "Led " + ledid);
                res = "{ 'ResponseValue' : 'OK' }";
                break;
            }
        }
        catch (Exception ex)
        {
            res = ex.Message + " " + ex.StackTrace;
        }
        // unable to process request
        return res;
    });
 
 
 
  while(Program.IsEnabled)
    {
      // HomeGenie heart beat
      connection.Blink("ReadyLed", 10);
    Pause(0.060);
      connection.Blink("ReadyLed", 230);
    Pause(0.700);
    }
}



« Last Edit: February 04, 2014, 05:24:17 PM by Gene »

February 04, 2014, 07:18:39 PM
Reply #4

mrx23dot

  • *
  • Information
  • Newbie
  • Posts: 16
Wow great tutorial, thank you!
Is SerialPortHelper also available for windows?
Because if it is, than the rest would be the same.

PS. is there any noticeable performance loss when running HG on Rasberry?

February 04, 2014, 07:31:50 PM
Reply #5

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
Yes, SerialPortHelper will work under Windows as well.
Just change the port name with COM<n>.
There are no noticeable performance issues using the serial port on Raspberry Pi.


February 04, 2014, 11:28:10 PM
Reply #6

mrx23dot

  • *
  • Information
  • Newbie
  • Posts: 16
My god, this is insanely great software:D
I see nice future for it. Connect a Bus Pirate on it and you have all major protocols available trough serial port.
1-Wire, UART, I2C, SPI, raw 2-wire, raw 3-wire, any bitbang.

February 05, 2014, 12:54:56 AM
Reply #7

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
10x mrx  :)

spread the word  ::) the more users HG gets, the more HG will become a better product.

October 30, 2014, 05:43:04 AM
Reply #8

ram

  • *
  • Information
  • Newbie
  • Posts: 13
Hi,
How do I use similar method to control an Arduino UNO? I'm a newbie. Any useful information would be highly helpful.