Overview
1. Connect to VivaPlanet
This next step is a bit more complex and
requires some C# and visual studio with the Gagetter software from GHI
Installed. This will take some time...persist it will be worth it in the
end...There are a lot of great tutorials out there on how to get this installed
on your machine so I won't go into that here. I spent some time getting all the
right references so I've taken a screen shot of all the references I'm using in
this script. This is a two part post. This post covers the initial libaries, setup and some of the XBee packet handling.
Figure
1: References to be used in Visual Studio
Now we’ll start the teardown of the code. I’m going to put my commentary in order of
how it appears within the code. So to really understand what’s going on you’ll
need to read through it at least twice. I'm taking a XBee packet and writing
it's ADC values to a file. Then I'm reading this packet from the file to the
serial output. I'm doing this every 5 seconds. There are several of debug
points in this script and I've left them in. You can literally use this code
and get some real time data analytics from your sensor system. You can even
send pictures. But this is more complex and may be in a different project all
together.
2. Getting the right headers.
First we need to make sure we have all the system essential
headers. This will be a multi-threaded application so we need System.Threading
and we’ll have a serial port output so we’ll also need the System.Io libraries.
using System;
using System.Collections;
using System.Threading;
using System.Text;
using System.Security.Cryptography ;
using System.IO; //for series 2
using System.IO.Ports;
Next we’ll need to add some Microsoft libraries to support
the hardware. The most important are Microsoft.SPOT and SPOT.hardware. The rest
don’t need to be there yet. There will be functionality in the future for these
libraries so I chose to leave them.
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;
using Microsoft.SPOT.Touch;
using Microsoft.SPOT.IO;
Lastly the API’s need to be added. This includes GHI’s
libraries, some xbee libraries and the netmf toolbox library. I spent a lot of
time finding these libraries so please use them!
using GHIElectronics.Gadgeteer;
using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.OpenSource;
using Gadgeteer.Modules.GHIElectronics;
using GHI.OSHW.Hardware;
using NETMF.OpenSource.XBee.Api;
using NETMF.OpenSource.XBee.Api.Common;
using Toolbox.NETMF;
using NETMF.OpenSource.XBee.Api.Zigbee;
using NETMF.OpenSource.XBee.Util;
The main program namespace I call VivaPlanetEmbedded. This
namespace handles all of the datarouting on the embedded system. Right now it
has Xbee functionality and serial out functionality. In the future we’ll add
some Ethernet and camera functionality as well.
namespace VivaPlanetEmbedded
{
The first class is a manager class of sorts. It takes a
packet listener and makes it publicly available to other classes. This was really
a key learning for me to add this class. It works very well and makes the code
stable.
public class
IOSampleDecodedEventArgs : EventArgs
{
public string DecodedPacket { get; set; }
public
IOSampleDecodedEventArgs(string myString)
{
this.DecodedPacket = myString;
}
}
The IOSampleListener class handles the incoming Xbee
packets. I am using two XBees in my system but I’m sure it can take more. Let’s walk through this class.
public class IoSampleListener : IPacketListener
{
public bool Finished { get { return false; } }
public XBeeResponse[]
Packets;
UsbSerial usbSerial
= new UsbSerial(1);
static bool flash = true;
public event EventHandler
IOSampleDecoded;
Here we initialize the variables for the class. I have a ‘finished’
flag to make sure that I know when the packets are done. The UsbSerial variable is for debug. It doesn’t
have to be here but it sure helps. We need an public event handler and a packet
variable to put the packets somewhere. Keep in mind most of these datatypes
come from the XBee API at Github. There is some documentation there but some of
this I had to learn myself and ask others.
public void ReportPacket(string packet)
{
EventHandler
handler =
IOSampleDecoded;
if (handler != null)
{
handler(this, new IOSampleDecodedEventArgs(packet));
}
}
This method passes the received string to the even handler
to do something with it. The string is formatted as an xbee packet which makes
it really nice. I don’t have to do any fancy parsing.
public void
ProcessPacket(XBeeResponse packet)
{
if (packet is IoSampleResponse)
{
IoSampleResponse myPackets =
ProcessIoSample(packet as IoSampleResponse);
}
}
public IoSampleResponse
ProcessIoSample(IoSampleResponse packet)
{
DateTime time = new DateTime(2007, 06, 21, 00, 00, 00);
Utility.SetLocalTime(time);
try
{
for (int ii = 0; ii < 5; ii++)
{
//String
format: Address HasData DataId SensorType TimeValue
string s = "^" + packet.SourceAddress.Address.ToString() + "^" + "true" + "^" + "0" + "^" + ii.ToString() + "^" + time.TimeOfDay.ToString() + "^" + packet.Analog[ii].ToString() + "\r\n";
ReportPacket(s);
}
}
catch (Exception e) //Catch any exceptions
{
Debug.Print(e.ToString()); //print the error to the debug ouput
}
return packet;
}
This method is the meat-and-potatoes of the Xbee portion. We
format the string and the ‘report’ the packet to the ReportPacket Class. This
enables other classes within this namespace to access the Xbee data.
private void DebugPrint(string str)
{
flash = !flash;
Debug.Print(str + " \r\n");
try
{
usbSerial.SerialLine.Write(str + " \r\n");
}
catch (Exception e)
{
Debug.Print("USB Not enabled" + " \r\n");
Debug.Print(e.ToString() + " \r\n");
}
}
This method is
just a debug script. It is very helpful.
public XBeeResponse[]
GetPackets(int timeout)
{
throw new
NotImplementedException();
}
}
Below we can see the main portion of the .NetMF class. First
it is important to note there is a lot of autogenerated code in .Netmf. So this
class is only a partial class. The other portion of it is hidden.
public partial class Program
{
public const string DeviceSerialNumer = "17564321";
private string _root; //volume
bool flash = true;
bool newPackets = false;
//set up the Xbee
const string serialPortName = "COM1";
const Cpu.Pin resetPin = GHI.Hardware.FEZCerb.Pin.PB0;
const Cpu.Pin sleepPin = GHI.Hardware.FEZCerb.Pin.PC13;
XBee xBee = new XBee(serialPortName, resetPin, sleepPin) {
DebugPrintEnabled = true };
There are some more variables declared here up front. The
most important part here is setting up the Xbee. This code affects the onboard
Xbee on the FEZCerbuino Bee. It enables the reset and sleep functions as well
as the serial functions. I have a lot of
comments in this next portion that describe what the code is doing so I’m going
to rely on them until the next method.
// This method is run when the mainboard is powered up or
reset.
void ProgramStarted()
{
Debug.Print("Viva Planet!");
try //both
usbSerial.Configure and usbSerial.Open()have different exceptions but it is
rare that they will triggered in this specific code
{
//mount the SD card
GHI.OSHW.Hardware.StorageDev.MountSD();
//configure the usbSerial port
usbSerial.Configure(9600, GT.Interfaces.Serial.SerialParity.None, GT.Interfaces.Serial.SerialStopBits.One, 8);
//if the port is not open then open it
if (!usbSerial.SerialLine.IsOpen)
{
usbSerial.SerialLine.Open();
DebugPrint("opened the
serial port");
}
if (VolumeInfo.GetVolumes()[0].IsFormatted)//check to make sure the SD card is formatted
{
_root = VolumeInfo.GetVolumes()[0].RootDirectory;
DebugPrint("SD Card is
formatted: " + _root);
}
else
{
DebugPrint("SD Card is not
formatted"); //print the error to the debug output
}
}
catch (Exception e) //Catch any exceptions
{
DebugPrint(e.ToString()); //print the error to
the debug output
}
//Gets the command data from the intertubes. This is interrupt
driven.
usbSerial.SerialLine.DataReceived += new GT.Interfaces.Serial.DataReceivedEventHandler(SerialLine_DataReceived);
//Implement the interface member
IoSampleListener sample = new IoSampleListener(); //created 1x only
sample.IOSampleDecoded +=
sample_IOSampleDecoded; //every time the event is raised (report packet)
//add the listener
xBee.Api.AddPacketListener(sample);
// initialize the loop timer and send a file to the server
GT.Timer timer = new GT.Timer(5000);
//set the interrupt
timer.Tick += new GT.Timer.TickEventHandler(timer_Tick);
timer.Start();
}
//this method is an event handler that decodes the packet within
this class
private void
sample_IOSampleDecoded(object sender, EventArgs e)
{
IOSampleDecodedEventArgs eventargs = (IOSampleDecodedEventArgs)e;
string packet = eventargs.DecodedPacket;
writeToFile(packet); //write the packet to
file
newPackets = true;
}
private void writeToFile(string packet)
{
//initialize variables
string fileName = Path.Combine(_root, "file.txt");
Stream stream;
try
{
//write it to file
if (File.Exists(fileName))
{
stream
=
File.OpenWrite(fileName);
stream.Position = stream.Length;
}
else
{
stream
=
File.Create(fileName);
}
using (var writer = new StreamWriter(stream))
{
writer.WriteLine(packet);
DebugPrint("wrote: " + packet);
}
stream.Dispose();
}
catch (Exception e)
{
DebugPrint(e.ToString());
}
}
This is the timer event handler. It fires at the designated time and allows the code to run in an endless loop on the embedded device. It is better than using a while loop for a lot of different reasons.
private void timer_Tick(GT.Timer timer)
{
DebugPrint("tick");
if (newPackets)
{
//populate the device struct with the measurements
DeviceReport device = readPacketData();
//Send the data
NotifyServiceQueue(device, null);
newPackets = false;
}
else
{
DebugPrint("no new
packets");
}
}
Stay tuned for the next update. We'll finish going over the code and show some working output.
No comments:
Post a Comment