domingo, 20 de febrero de 2011

Comunicación establecida!!!

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/

No hay comentarios: