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,
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
Te he dejado la respuesta en un nuevo post :
http://msmvps.com/blogs/peplluis/archive/2008/09/19/copiar-o-mover-filas-de-un-datagridview-a-otro.aspx
Espero que esto sea lo que buscas.
Pep Lluis,
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
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,
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
Hola Favi,
He creido interesante postear la respuesta para que pueda ser util a mas personas, espero que no te importe compartirla.
Aqui tienes la respuesta:
http://msmvps.com/blogs/peplluis/archive/2008/10/15/system-io-ports-serialport-conversiones-y-codificaciones.aspx
Saludos,
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
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,
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
Hola Nicolas,
Has provado con una expresion simular a :
SerialPort1.WriteLine(&hff)
Saludos,
PepLluis,