Puertos con C#. Envio y Recepción de Datos.

Aprenderemos a abrir el puerto, enviar datos y recibirlos. La recepción de datos puede ser de forma síncrona o asíncrona. Yo voy a enfocar todo el artículo a la forma de recepción síncrona, ya que necesito leer la trama y dejar de leer cuando este todo recepcionado, así que tendremos que ir analizando cada byte que recibimos.

En primer lugar abrimos el puerto:

    public void AbrirPuertoSerie(string Puerto,int Velocidad)
        {

            try
            {

                if (_PuertoSerie.IsOpen)
                    _PuertoSerie.Close();
                _PuertoSerie.PortName = Puerto;
                _PuertoSerie.BaudRate = Velocidad;

                _PuertoSerie.Parity = System.IO.Ports.Parity.None;
                _PuertoSerie.DataBits = 8;
                _PuertoSerie.StopBits = System.IO.Ports.StopBits.One;
                _PuertoSerie.Handshake = System.IO.Ports.Handshake.None;

                _PuertoSerie.RtsEnable = false;
                _PuertoSerie.DtrEnable = true;

                //_PuertoSerie.DataReceived += new SerialDataReceivedEventHandler(com_DataReceived);
                //_PuertoSerie.ErrorReceived += new SerialErrorReceivedEventHandler(com_ErrorReceived);

                _PuertoSerie.Open();

            }
            catch (ArgumentOutOfRangeException Ex)
            {
                _Errores.add("AbrirPuertoSerie. " + Ex.Message);
            }
            catch (InvalidOperationException Ex)
            {
                _Errores.add("AbrirPuertoSerie. " + Ex.Message);
            }
            catch (ArgumentException Ex)
            {
                _Errores.add("AbrirPuertoSerie. " + Ex.Message);
            }
            catch (IOException Ex)
            {
                _Errores.add("AbrirPuertoSerie. " + Ex.Message);
            }
            catch (Exception ex)
            {
                _Errores.add("AbrirPuertoSerie. " + ex.Message);
            }

        }

Una vez abierto ya podremos hacer el envío de los datos.

Los datos, la trama, la construiremos con un List<byte> que es lo más cómodo de utilizar y el envío se produce sin errores. He tenido muchos problemas al enviar los datos como char, inexplicable.

El envío de datos es muy simple:

        public void EnviarPuertoSerie(byte[] Datos)
        {

            try
            {

                if ((_PuertoSerie != null))
                {
                    _PuertoSerie.Write(Datos, 0, Datos.Count());
                }
            }
            catch (Exception ex)
            {
               _Errores.add("EnviarPuertoSerie. " + ex.Message);
            }
        }

La recepción sería:

        private List LeerPuerto()
        {
            List Respuesta = new List();

            try
            {
                while (_PuertoSerie.BytesToRead > 0)
                {
                    Respuesta.Add((byte)_PuertoSerie.ReadByte());
                }
            }
            catch (Exception ex)
            {
                  _Errores.add("LeerPuerto. " + ex.Message);
            }

            return Respuesta;
        }

Para el envío de datos de forma síncrona utilizaremos una función que envíe y seguidamente reciba. Al necesitar una trama de recepción específica tendremos que leer hasta que la tengamos. Para asegurarnos de que no nos quedamos escuchando eternamente tendremos un TimeOut preparado.

        public List EnviarPuertoSerieSinc(byte[] Datos)
        {
            List read = new List();
            sRespuesta Respuesta = new sRespuesta();
            bool TramaOK = false;

            try
            {
                EnviarPuertoSerie(Datos);
                read = LeerPuerto();

                //Comprobamos que la trama tenga la longitud
                //Necesaria, si no es así volvemos a leer
                DateTime startToWait = DateTime.Now; //start timeout
                bool isTimeout = false;

                while (!TramaOK && !isTimeout)
                {
                    List Lectura = new List();
		    //Volvemos a leer y añadimos lo leido a la lista read
                    Lectura = LeerPuerto();
                    if (Lectura.Count > 0)
                        read.AddRange(Lectura);

                    if (read.Count > 0)
                    {

                        //Detectamos el final de la trama
                        if (read.Count > 0)
                        {
                            int BuscaETX = read.FindLastIndex(x => x == (byte)enComandosDecimal.ETX);
                            if (BuscaETX > 0)
                            {
                                if ((read.Count == BuscaETX + 5)
                                    && (BuscaETX > 0)
                                    && (read[BuscaETX - 1] != (byte)enComandosDecimal.ESC) //Comprobamos que no venga precedido por un caracter de escape
                                    && (read[BuscaETX - 2] != (byte)enComandosDecimal.STX)) //Comprobamos que no sea la respuesta rápida
                                    TramaOK = true;
                            }
                        }
                    }
                    isTimeout = (DateTime.Now.Subtract(_timeout) >= startToWait);
                }

            }
            catch (Exception ex)
            {
                _Errores.add("EnviarPuertoSerieSinc. " + ex.Message);
            }
            return read;
        }

En esta función controlamos la recepción de una trama la cual sabemos su composición, sabemos que termina con un ETX, 03Hex, así que esperamos a la recepción de dicho byte para terminar de recibir. Controlamos también que dicha recepción no nos ocasione un problema de bucle infinito, así que lo controlamos con un timeout.

Una vez devuelta toda nuestra trama ya podemos procesarla, pero esto lo dejo para otro capítulo.

Un pensamiento en “Puertos con C#. Envio y Recepción de Datos.

  1. Interesante. Los problemas que comentas usando ‘char’ puede que se deban a que, al utilizar el CLR caracteres Unicode para las cadenas, estés enviando palabras de más de 8bits.

Deja un comentario