HomeGenie Forum
Automation Program Plugins and Wizard Scripting => Raspberry Pi GPIO/SPI/I2C => Topic started by: mrx23dot on February 04, 2014, 01:44:45 PM
-
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
-
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?
-
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,
-
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:
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:
//
// 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:
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:
// 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 (http://www.homegenie.it/docs/doityourself.php) for controlling the two leds:
// 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);
}
}
-
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?
-
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.
-
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.
-
10x mrx :)
spread the word ::) the more users HG gets, the more HG will become a better product.
-
Hi,
How do I use similar method to control an Arduino UNO? I'm a newbie. Any useful information would be highly helpful.