none
Problema trama recepción de datos por SerialPort RRS feed

  • Pregunta

  • Buenos días,

    Tengo un problema con un programa que estoy realizando para la lectura de puerto serie, en éste caso del peso recibido automáticamente por una báscula cuando ésta estabiliza. Lo estoy realizando con Visual Studio 2010 Version 10.0.40219.1 SP1Rel y VB por supuesto.

    Simplemente abro el puerto serie y le configuro los parámetros:

    - baudRate: 9600

    - parity: 0

    - dataBits: 8

    - stopBits: 1

    La báscula me debería enviar 7 espacios, luego 7 caracteres más que contendría el propio peso y el literal "kg" y un espacio.

    Estoy utilizando el método ReadExisting en el evento SerialPort1_DataReceived donde una vez leído escribo en un fichero de texto en un textbox en la aplicación. Lo que antes me ocurría es que siempre me leía bien para pesos de decenas (de 0 a 99), aparentemente. Luego empezamos a leer pesos con centenares (100 a 999) y la primera vez lo leía bien pero las siguientes sólo me leía a partir de las decenas, ni tan siquiera los espacios iniciales. 

    Así que estuve leyendo y documentandome acerca de ello, leí en éste foro a usuarios que le había ocurrido algo similar y es por que se recibe de manera asíncrona, así que implementé el siguiente algoritmo para leer que lo que hago es concatenar hasta que detecto un fin de trama (en mi caso "kg" ya que no tiene salto de línea al parecer) y ahora sí que lee todo:

        Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
            Const KILOGRAMOS As String = "kg"
            Dim buffer As String
            Const CR_CHARACTER As Char = Chr(13)
            Const LF_CHARACTER As Char = Chr(10)

            Try
                buffer = SerialPort1.ReadExisting
                weight = weight + buffer
                If weight.Contains(KILOGRAMOS) Then
                    WriteFile(weight)
                    Me.Invoke(New EventHandler(AddressOf UpdateControls))
                    weight = ""
                End If

                WriteFileLog("Recibo de báscula: " + "|" + buffer + "|")

                If buffer.Contains(CR_CHARACTER) Then
                    WriteFileLog("Contiene retorno de carro: SI")
                Else
                    WriteFileLog("Contiene retorno de carro: NO")
                End If

                If buffer.Contains(LF_CHARACTER) Then
                    WriteFileLog("Contiene avance de línea: SI")
                Else
                    WriteFileLog("Contiene avance de línea: NO")
                End If


            Catch ex As Exception                                                       ' En caso de excepciones mostramos error
                MsgBoxError(ex.Message, True)
            End Try
        End Sub

    Si imprimo el log me viene a sacar algo como sigue:

    30/11/2015 11:57:02: Recibo de báscula: |       544.6 kg |
    30/11/2015 11:57:02: Contiene retorno de carro: NO
    30/11/2015 11:57:02: Contiene avance de línea: NO
    30/11/2015 11:57:13: Recibo de báscula: |       5|
    30/11/2015 11:57:13: Contiene retorno de carro: NO
    30/11/2015 11:57:13: Contiene avance de línea: NO
    30/11/2015 11:57:13: Recibo de báscula: |45.4 kg |
    30/11/2015 11:57:13: Contiene retorno de carro: NO
    30/11/2015 11:57:13: Contiene avance de línea: NO

    Mi duda es, ¿por qué la primera vez después de reiniciar el equipo sí que trae toda la traza completa como se puede apreciar y la segunda vez me lo trae de manera asíncrona en 2 partes de 8 caracteres? ¿puede ser configuración de la báscula o es configuración de mi SerialPort o es simplemente de la tecnología empleada?

    Un saludo y muchas gracias,

    Alfredo.

    • Cambiado Enrique M. Montejo domingo, 6 de diciembre de 2015 9:49 Comunicación con dispositivos externos.
    lunes, 30 de noviembre de 2015 15:26

Todas las respuestas

  • Hola Alfredo,

    En la primera iteración probablemente el JIT tarda unas milésimas más por lo que cuando ejecutas el ‘ReadExisting’ el buffer de recepción ya contiene todos los caracteres.

    El evento ‘DataReceived’ se genera inmediatamente después de recibir el primer carácter, para corregir este efecto y leer todo el contenido en una sola recepción debes insertar un pequeño retardo antes del ‘ReadExisting’.

    Feliz año nuevo!! PepLluis,


    MVP - Visual Developer

    jueves, 31 de diciembre de 2015 8:23
    Moderador
  • Hola,

    También puedes intentar usar ReceivedBytesTreshold del objeto serialport, con este se define el numero de Bytes que tiene que tener el bufer antes de lanzar el evento DataRecived.

    Un saludo.


    Es de buena educación dar las gracias cuando te ayudan, si alguna respuesta te ha sido de utilidad agradécelo marcandola como útil.
    Blog

    jueves, 31 de diciembre de 2015 9:52
  • Correcto, aunque son diferentes escenarios.

    En los casos donde las tramas recibidas puedan variar de longitud en función a su contenido, aun fijando el ‘threshold’ continuaremos teniendo la misma problemática con el efecto añadido de que no nos disparara el evento si el buffer no contiene ese mínimo de bytes.

    Saludos, Feliz entrada al 2016!


    MVP - Visual Developer

    jueves, 31 de diciembre de 2015 19:11
    Moderador
  • Muchas gracias chicos, me habéis ayudado mucho.

    Finalmente lo que he realizado es que voy concatenando lo que voy recibiendo en el evento DataReceived hasta que encuentro "kg", y luego inicializo de nuevo la variable para comenzar con una nueva lectura. Tal como está el algoritmo que puse.

    Ya está funcionando perfectamente en producción gracias a vosotros y leyendo otros post similares. Sois unos cracks!

    En próximas ocasiones tendré en cuenta vuestros consejos para tratar de recibir todo de una vez, aunque el impacto de recibirlo en partes es mínimo. Pero al menos me ha servido para comprender el motivo.

    Un saludo,

    Alfredo.



    • Editado alfredorz martes, 19 de enero de 2016 11:55
    martes, 19 de enero de 2016 11:52
  • alfredorz puedes subir el código por favor.
    martes, 19 de enero de 2016 20:17
  • alfredorz puedes subir el código por favor.

    Hola,

    Pues es el mismo pero con menos trazas, te lo copio igualmente:

        '************************************************
        ' Método Lectura de Puerto Serie
        '************************************************
        Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
            Dim weightLen, numCaracteres As Integer
            Dim RXbyte As Byte
            Dim rLine As String
            Const KILOGRAMOS As String = "kg"
            Dim buffer As String
            Const CR_CHARACTER As Char = Chr(13)
            Const LF_CHARACTER As Char = Chr(10)
    
            Try
                buffer = SerialPort1.ReadExisting
                weight = weight + buffer
                If weight.Contains(KILOGRAMOS) Then
                    WriteFile(weight)
                    Me.Invoke(New EventHandler(AddressOf UpdateControls))
                    weight = ""
                End If
    
            Catch ex As Exception                                                       ' En caso de excepciones mostramos error
                MsgBoxError(ex.Message, True)
            End Try
        End Sub

    Como la báscula envía el peso cuando estabiliza no es necesario enviar ninguna petición, en otras básculas sí que enviaba algún caracter ($).

    El resto del código del programa es de configuración de báscula (baudrate, bits, etc), configuración de fichero de salida, etc. así que no te es necesario.

    Un saludo.

    jueves, 21 de enero de 2016 10:40