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 classIOSampleDecodedEventArgs : EventArgs
{
public string DecodedPacket { get; set; }
publicIOSampleDecodedEventArgs(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 voidProcessPacket(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() + “rn“;
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 + ” rn“);
try
{
usbSerial.SerialLine.Write(str + ” rn“);
}
catch (Exception e)
{
Debug.Print(“USB Not enabled” + ” rn“);
Debug.Print(e.ToString() + ” rn“);
}
}
This method is just a debug script. It is very helpful.
public XBeeResponse[] GetPackets(int timeout)
{
throw newNotImplementedException();
}
}
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 voidsample_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.