Ok... we have a couple of options that I'll try to describe briefly here.
OPTION 1Using 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 2Using 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 busThis 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 operationsThe following example is used in the
HomeGenie Raspberry Pi Board 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);
}
}