Problemas en la recepción de datos


Considerando interesante la pregunta y con el permiso de Cream, me gustaría compartir esta pequeña cuestión en tanto a la metodología  en la recepción de datos usando el IO.Ports.

La pregunta:
“no siempre recibo bien los datos o no los capturo por el puerto serie”.. el código que estoy usando actualmente es el siguiente :

puertoSerie.write(“R”)                                              ‘enviamos carácter
dato=Asc(puertoSerie.ReadExisting.Chars(1))          ‘leemos y cogemos valor de x

Una de las características de las comunicaciones serie en uno de sus modos, es que son asíncronas… lo que significa que los ‘Bytes’ se serializan y transmiten sin que el receptor conozca el momento preciso. Es decir el bit de ‘start’ indica al receptor el envío de un ‘byte’ pero en ningún caso y por ausencia de protocolo conocemos de antemano la cantidad de ‘bytes’ que vamos a recibir asi como el intervalo de tiempo que va a transcurrir en la transmision  entre byte’s.

Como tu bien dices y expresas en tus razonamientos, estas ejecutando la lectura directamente sin esperar al evento de ‘Data Received’,  es presumible que en la rapidez de proceso del PC nos dispare la excepción de   “index out of range exception”  pues estas intentando procesar la posición del segundo carácter que evidentemente aun no has recibido y por lo tanto es inexistente.

El mandato ReadExisting, lee los bytes recibidos que actualmente permanecen en el buffer, en tu caso y en ocasiones, dependiendo del flujo de ejecución, en el buffer debes tener por leer, 0, quizas 1 o cuando te funciona bien2 bytes.

La práctica correcta es formalizar el intercambio de tramas delimitándolas con un carácter de inicio y otro de fin… por ejemplo la mayoría implementan el CR o Chr(13) como fin de trama, algunos fabricantes optan por utilizar como fin de trama secuencias tales como 2 bytes de FCS + Un Carácter identificativo + LF + CR, en fin como siempre hay para todos los gustos. Al menos con este método podrás estar seguro de que la transmisión a finalizado correctamente.

Deberías implementar el disparo de recepción y procesar los datos una vez se ha completado la recepción.  En todo caso otra opción podría ser algo parecido a:

‘String de recepción utilizado como buffer
Private PortSerie_Recepcion As String = “”    
‘Anadir el manipulador de recepción en la sub New, Load…   
AddHandler Serie.DataReceived, AddressOf Rx
 
Sub
Rx(ByVal sender As Object, _
       ByVal e As serialDataReceivedEventArgs)       
   Try

    ‘Añadir la recepción actual al buffer            
    PortSerie_Recepcion += Serie.ReadExisting
    If PortSerie_Recepcion.Contains(Chr(13)) Then                
        ….PROCESAR LA INFORMACION
        PortSerie_Recepcion = “”           
    End If       
   Catch ex As Exception
    ‘En caso de excepción       
  End Try   
 End Sub

Si la longitud de tus tramas es de 2 bytes… una idea muy básica puede ser controlar el número de caracteres que están en el buffer pendientes de ser leídos y antes de procesarlos con: PuertoSerie.BytesToRead

         Prohibido definitivamente
       
        If PuertoSerie.BytesToRead = 2 Then
            dato = Asc(PuertoSerie.ReadExisting.Chars(1))
       
End If

        ‘ Definitivamente NO ES RECOMENDABLE!
       
        If PuertoSerie.BytesToRead > 2 Then
            dato = Asc(PuertoSerie.ReadExisting.Chars(1))
       
End If

El problema utilizando el primer caso es que nunca se procesara el dato si no se reciben exactamente dos caracteres, por ende y peor aún si BytesToRead es > 2 nunca procesara los bytes recibidos, poniendo en apuros a nuestra aplicación cuando él se desborde el buffer de recepción. En segundo caso y en determinadas situaciones de intercambios muy rápidos o con interferencias podria perder la secuencia lecturas de pares de bytes y procesar inadecuadamente por desplazamiento de posiciones… como mal menor necesitarías usar PuertoSerie.BytesToRead > 2 conjuntamente con PuertoSerie.Read(Dato(),0,2), aunque insisto lo mejor es incluir un fin de trama en cada transmisión desde el firmware del dispositivo.

        ‘ una propuesta… Quizas

       

        Dim Trama() As Char

        If PuertoSerie.BytesToRead > 2 Then

            PuertoSerie.Read(Trama, 0, 2)

            dato = Asc(Trama(1))

        End If

Saludos,
Pep Lluis,

 

 

 

10 thoughts on “Problemas en la recepción de datos”

  1. Hola Pep Lluis, he leído varias veces las soluciones que das en los diferentes espacios y foros, y quizá esta pregunta te la hayan hecho ya, pero la verdad ando algo atorado con este problemilla… va..

    La idea es tener 2 datagrid :

    1er Datagrid : Muestra el resultado de una búsqueda, de este datagrid deben seleccionar 1 o varios registros y deberán pasarse al

    2do Datagrid : Que debe mostrar los registros seleccionados en el 1er datagrid.

    Si paso un registro del Datagrid 1 al 2, el registro desaparecerá del 1 y aparecerá en el 2, y viceversa.

    Gracias de antemano

  2. Hola Pep Lluis,

    Con respecto a la segunda opcion que desaconsejas: ” PuertoSerie.BytesToRead > 2 ”

    Es solo para ese caso en particular? Yo estoy recibiendo una trama que tiene como minimo 13 bytes, por lo tanto pensaba que no era necesario invocar al delegado que procesa si a priori ya se que menos de 13 bytes no pueden ser una trama completa.

    El problema es que me aperecen esporadicamente cadenas descartadas (encontre el fin pero no el inicio) y estoy bastante desconcertado por ese problema. Una version vieja en VB6 usando timer no pierde datos, imaginate la bronca que tengo :S

    Lo que si ahora estoy comprobando si el error pasa por usar bytesRecibidos.Lenght (la cadena donde concateno todo lo recibido en port.ReadExisting()) y no por usar port.BytesToRead

    Muchas Gracias por tu pagina! es de muchisima ayuda

  3. Hola Hector,
    Ciertamente lo discutido es para este caso en concreto.
    Lo ideal a mi forma de ver, es componer un buffer de recepcion propio llenandolo con un ‘ReadExisting’ y componiendo las tramas en funcion a la recepcion de Inicio/fin y preferentemente comprovando CRC’s.

    Si te parece concreta el tipo de tramas y veo como darte alguna idea.
    Saludos,

  4. Hola PepLluis. Tengo un problema de conversion.

    Resulta que estoy actualizando un progma hecho en vb version 3 que utiliza un contol mscom de 16 bits.

    Ahora en net lo he transformado en system.io.serialport.

    El problema en concreto es, que los caracteres que recibo del bufer se me quedan el mas alto al trasformarlo a hexadecimal en 3F cuando en la version vb3 en la misma lectura me indica 8D.

    Es decir como si el juego de caracteres interno del objeto serialport fuera menor que el juego de caracteres del control mscomm de la version vb 3. Con el control mscomm de vb3 se pueden leer caracteres que llegarian 255 decimal FF hexadecimal pero con el serialport de net los deja capados en 3F no me interpreta caracteres de valor superior.

    No si puede ser, que no utiliza un juego de caracteres extendido o algo asi ?

    A ver si me puedes dar una pista.

    utilizo el serialport.readexisting para recuperar las cadenas

    gracias

  5. Hola PepLluis
    quisiera hacerte una preg muy especifica o quizas no la veas asi pero es que ya he recorrido varios blog y sale tu nombre
    yo trato de leer un contador , que tengo que pasarle
    nn 04 0732 0002 cc cc
    nn: número de periférico (p.ej: 01)
    04: código de lectura
    0732: dirección de una variable
    0002: nº de integers (2 bytes) pedidos
    cc cc: valor del crc 16 bits calculados

    ok eso
    pero que o me explota o no me lee
    y sin embargo se activa el DataReceived cuando desconecto y vuelvo a conectar el contador

  6. Hola Harry,

    Creo que deberias verificar que las lineas de transmissión/recepción no esten invertidas, pues si dispara al desconectar es porque a la desconexion recibe un cambio de estado y es obvio que no es normal.

    En otro caso puedes contactarme para continuar la conversacion.
    Pep Lluis,

  7. Hola Pep te consulto lo siguiente, tengo un circuito hecho que me devuelve la palabra “hola” cuando le mando un numerico 255, en mi app no logro obtener el “hola”.
    Utilizo vb.net 2008 con el componente SerialPort.
    He probado con aplicaciones como Hercules o Nipple para testear el circuito conectado al puerto serie. EN el Hercules le mando un FF en hexa y me devuelve bien, pero en mi codigo es como que no lo estimula al puerto o no se que pasa que no puedo catchear la respuesta en el evento SerialPort_DataReceived. La configuracion es por defecto.

    Gracias Saludos

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>