Despues de un buen dia de programación... he conseguido comunicar el PC con el Netduino a traves del XBEE. La solución se compone de 5 proyectos, un cliente Windows, un cliente NetDuino, una libreria de acceso al modulo XBee que es compartida por 2 proyecto uno plataforma .NET 4.0 y otro MicroNET 4.1 y por ultimo una libreria comun con las trazas de comunicación entre ambas plataformas.
El proyecto NetDuino es sencillo:
public class Program
{
private static OutputPort OnboardLED;
private static XBEE _comm;
public static void Main()
{
OnboardLED = new OutputPort(Pins.ONBOARD_LED, initialState: false);
_comm = new XBEE("COM1");
_comm.OnReciveData += new ReciveDataDelegate(_comm_OnReciveData);
Thread.Sleep(Timeout.Infinite);
}
static void _comm_OnReciveData(CommunicationData data)
{
TestData _data = ((TestData)data);
OnboardLED.Write(_data.BoolData);
_comm.Write(new TestData(_data.BoolData));
}
}
El proyecto instancia la clase XBee y establece el delegado de lectura de datos. Con la lectura de datos se procede a enceder/apagar la luz de la placa y reenvia el mensaje al PC.
El proyecto de PC sigue la misma linea que el de NetDuino:
XBEE _xbee;
public frmPpal()
{
InitializeComponent();
}
private void btnConnect_Click(object sender, EventArgs e)
{
_xbee = new XBEE(cmbPorts.SelectedItem.ToString());
_xbee.OnReciveData += new ReciveDataDelegate(_xbee_OnReciveData);
}
void _xbee_OnReciveData(CommunicationData data)
{
this.rtbLog.Invoke(new MethodInvoker(delegate() { this.rtbLog.Text += data.ToString() + Environment.NewLine; }));
}
private void btnSend_Click(object sender, EventArgs e)
{
_xbee.Write(new TestData(rdbOn.Checked));
}
private void Form1_Load(object sender, EventArgs e)
{
cmbPorts.Items.AddRange(SerialPort.GetPortNames());
}
private void btnClearAll_Click(object sender, EventArgs e)
{
this.rtbLog.Clear();
}
En este caso el proyecto instancia la clase XBee y establece el delagado de escucha. A traves de un boton y 2 radiobuttons se permite el envio de mensajes desde el PC a NetDuino.
La clase de XBee es la mas compleja:
#region Members
private readonly SerialPort _xbee;
private int indexSentence = 0;
private byte[] tempSentence = new byte[200];
#endregion
#region Construcctor
public XBEE(string port)
{
_xbee = new SerialPort(port, 9600, Parity.None, 8, StopBits.One);
if (_xbee.IsOpen)
_xbee.Close();
_xbee.Open();
_xbee.DataReceived += new SerialDataReceivedEventHandler(_xbee_DataReceived);
}
#endregion
#region Internal Methods
void _xbee_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int numc = 0;
byte data;
numc = _xbee.BytesToRead;
if (numc > 0)
{
for (int i = 0; i < numc; i++)
{ // Process bytes received
data = (byte)_xbee.ReadByte();
tempSentence[indexSentence] = data;
indexSentence++;
if (TestData.IsSecuenceComplete(tempSentence))
{
string tempData = new string(Encoding.UTF8.GetChars(tempSentence));
OnReciveData(TestData.TryParse(tempData));
indexSentence = 0;
tempSentence = new byte[200];
continue;
}
}
}
}
#endregion
#region Public Methods
public void Write(CommunicationData data)
{
byte[] _data = Encoding.UTF8.GetBytes(data.ToString());
_xbee.Write(_data, 0, _data.Length);
}
#endregion
#region Public Events
public event ReciveDataDelegate OnReciveData;
#endregion
#region IDisposable
public void Dispose()
{
_xbee.Close();
_xbee.Dispose();
}
#endregion
Esta clase es la encargada de abrir el puerto con la configuracion preestablecida. La clase se encarga de realizar los envios y recepciones de datos. Los envios no tienen nada en particular, pero si la recepción en la cual hay que escuchar hasta que la trama que se recibe es la correcta. Para ello no es posible leer toda la informacion que existe en el buffer del puerto de golpe sino que hay que leer byte a byte y analizar la informacion existente para determinar si es una trama valida.
La ultima clase es la que define la trama a comunicar. Para ello he definido una clase abstracta de la que deriven el resto de tramas:
#region Members
public static byte ByteStartDelimiter = 33;
public static byte ByteFinishDelimiter = 63;
public static byte ParameterDelimiter = 15;
private string version = "1.0.0";
#endregion
#region Properties
public string Version
{
get { return version; }
set { version = value; }
}
#endregion
#region Overrided Methods
public abstract override string ToString();
#endregion
#region Static Methods
public static TestData TryParse(string sentence) { return null; }
public static string RemoveDelimiters(string sentence)
{
string res = sentence;
int inicio = res.LastIndexOf((char)TestData.ByteStartDelimiter) + 1;
int fin = res.LastIndexOf((char)TestData.ByteFinishDelimiter) - 1;
res = sentence.Substring(inicio, fin);
return res;
}
public static bool IsSecuenceComplete(byte[] tempData)
{
bool res = false;
int index = 0;
while (!res && index < tempData.Length)
{
if (tempData[index] == TestData.ByteStartDelimiter)
{
while (!res && index < tempData.Length)
{
if (tempData[index] == TestData.ByteFinishDelimiter)
{
res = true;
break;
}
else
index++;
}
break;
}
else
index++;
}
return res;
}
#endregion
Esta clase da acceso a los metodos basicos de definicion de tramas. Como metodos importantes destacar la implementacion obligatoria del metodo ToString que sera el encargado de serializar los datos a enviar por la linea y el metodo TryParse que sera el que encargado de realizar la conversion desde un string a la clase correspondiente.
La implementacion de la trama de prueba es la siguiente:
#region Construcctor
public TestData() { }
public TestData(bool data)
{
this.booldata = data;
}
#endregion
#region Members
private bool booldata=false;
#endregion
#region Properties
public bool BoolData
{
get { return booldata; }
set { booldata = value; }
}
#endregion
#region Overrided Methods
public override string ToString()
{
string res = string.Empty;
res += (char)TestData.ByteStartDelimiter;
res += booldata ? "ON" : "OFF";
res += (char)TestData.ByteFinishDelimiter;
return res;
}
#endregion
#region Static Methods
public new static TestData TryParse(string sentence)
{
TestData res = new TestData();
sentence = sentence.Trim();
sentence=RemoveDelimiters(sentence);
if (sentence == "ON")
res.BoolData = true;
else
res.BoolData = false;
return res;
}
#endregion
Para los que querais acceder al codigo fuente lo teneis disponible en codeplex : http://netuav.codeplex.com/
Blog creado para describir el proceso de creación de un UAV teledirigido basado en plataformas .NET.
domingo, 20 de febrero de 2011
Vuelta a la carga
Despues de unas semana de trabajo intenso, vuelo a la carga con el proyecto, pero despues del frenazo anterior al no poder enviar/recibir informacion a traves de los modulos de XBEE, he decidido crear un proyeto de test para poder establecer esta comunicacion.
Suscribirse a:
Entradas (Atom)