Spanish <SOLUCIONADO> Como recibir 2 bytes por timer (cada x miliseg), no por interrupción de entrada.

edochan

Member
Licensed User
Longtime User
Hola.
Estoy bastante frustrado, no avanzo nada en cuanto a la capacidad de comunicación serial fiable (más concretamente por Bluetooth).
Me he estado pegando con la recepción de bytes por interrupción AStream_NewData (Buffer() As Byte), pero en mi caso, irremediablemente cada cierto tiempo se producen errores. Me he leido 200 post y sigo igual.
Seguramente sea por el "protocolo" que uso, que es exageradamente simple: el emisor me envía 2 bytes seguidos y secuencialmente cada 10 miliseg otros 2. Con este método puedo extraer del primer byte la variable concreta que esta entrando, y del segundo byte el valor correspondiente. Con appinventor funciona a la perfección. Pero al tratar de hacerlo con B4A a veces en el Buffer entra 1 o entran 2 o 3 bytes seguidos que guardo en un Dim temporal y que luego debo discriminar, pero ello supone que se pierde ese sincronismo entre variable-valor, ya no sé cuales entraron seguidos y cual no, o si es un ruido que se coló por el medio como una señal más. Tambien probé varias condiciones tipo que si Buffer.Lenght > 2 lo descarte, pero nada.

El caso es que os rogaría ("por lo que más queráis"), que me expliquéis a poder ser con un ejemplo sencillito, un método alternativo a la captura de datos por interrupción de entrada.
O sea, que pueda decirle al programa con un Timer que cada x miliseg (sin romper la conexión Bluetooth, claro) lea lo que entra en ese preciso momento, luego ya discriminaré si entra 1 o 2 bytes, no me importa descartar/perder alguno, y luego desactivar la recepción para que no atienda nada más en ningún buffer, (o algo así); necesito mantener ese simple protocolo, lo he intentado de otras formas (que si TextReader - readline, que si Serial1.InputStream), pero no acierto en la sintaxis del compilador, por lo que sea, siempre me tira algún error, y seguro que es por que soy el más estúpido de los que han adquirido una licencia de este lenguaje para intentar hacer una aplicación, pero así es.
Aunque os parezca mentira, es una de las pocas barreras que no he logrado superar en meses de intentos.
Espero vuestra comprensión y ayuda. Gracias.
Saludos
 

JordiCP

Expert
Licensed User
Longtime User
Buenas,


No sé si tienes acceso al código del micro que envía los datos, pero en caso que así sea yo haría lo siguiente.

Si hay un valor que las variables o sus valores NUNCA pueden tomar (por ejemplo, si la variable nunca será 255 y el valor tampoco), en vez de crear tramas de 2 bytes, créalas de 3, empezando por el 255. Así, cada vez que recibas, te podrás "alinear" con el inicio de trama, y tomas el siguiente par de valores como válidos.

Para comunicaciones más complejas se suelen "encapsular" las tramas de manera más compleja, con cabeceras, checksums, carácteres de escape, etc.... Pero en este caso, con esto debería ser suficiente.
 

edochan

Member
Licensed User
Longtime User
Hola Jordi.
Muchas gracias por tu consejo, lo tendré muy en cuenta, pero lo hago así por aprovechar la capacidad del micro-controlador que hace los envíos. Enviar 2 bytes no representa pérdida de tiempo que impida otras funciones, ya que aprovecho el buffer de salida que tiene este micro que se tiene que ocupar de otras interrupciones más importantes. Para enviar 3 bytes ya necesita algún milisegundo más y lo compromete.
Por eso lo que busco es poder hacerlo como lo consigo con APP inventor, simplemente leo lo que entro cada 4 miliseg y si entro 1byte lo descarto, pero si en ese periodo entraron 2 es que son adecuados, se borra el bufer y a la siguiente revisión vuelve a comprobar que sean 2 los recibidos. Y funciona al 100% .
Necesito encontrar un método análogo que lo haga con B4A.
En un nuevo modelo del circuito que envía ya no tendré esa prioridad y usare una trama más elaborada pero el que tengo ahora funciona así.

Un saludo.
 

JordiCP

Expert
Licensed User
Longtime User
Creo que con AsyncStreams no se puede bajar a nivel de buffer, y sólo se reciben los datos por interrupción. En este caso, al no haber delimitador de trama, si se desincroniza se estropea el invento.

Otra solución, rizando mucho el rizo, pero que podría funcionar (sin dejar de usar AsyncStreams), es:
*Si ninguna de las variables ni de los valores supera el valor 127 (o sea, el bit mas alto a cero), añades la mascara 80h al primero de ellos. Así, cuando recibes, miras el que tiene la máscara 80h para sincronizarte, la quitas a la hora de procesarlo y ya está


Antes de los AsyncStreams había por aquí unas librerías serie que no iban por interrupciones y por tanto permitían leer buffers cada X tiempo, pero no las he utilizado.
 

edochan

Member
Licensed User
Longtime User
Me da la impresión de que es como dices.
Desarrollando lo que me escribiste anteriormente, he creado en la aplicación del receptor (B4A) un "separador" de pares de datos con un timer, y lo estoy perfeccionando.
De manera que cuando recibe un dato en Buffer() se activa el timer a 5 mseg, si entra otro valor antes, se reinicia este timer y ese segundo byte lo guarda como la pareja del primero. Pero si se llega al tick, El timer crea y guarda un valor = 300 (fuera del rango max. que puede entrar) también en el archivo donde guardo secuencialmente todo lo que entra, eso me permite luego ir leyendo las parejas de bytes separadas por un valor = 300. ¿Que te parece?
Aun me da fallos, pero creo que es cosa de pulirlo.
Cuando este mínimamente decente, pegare esa parte de código por si alguien le interesa.
Muchas gracias por molestarte Jordi.
Buenas noches.
 

Matias Ferrer

Member
Licensed User
Longtime User
Buenas,

Pasaba leyendo y me encontré con sus comentarios!!


Hace algún tiempo trabaje en un proyecto similar, con Atmel, VB6 (soy viejo) y Gambas2.
Creo que debes evaluar lo que comenta JordiCP, agregar un encabezado FF a la secuencia y yo agregaría un CHECKSUM al final. Lo que te da mas seguridad de los datos transmitidos, teniendo en cuenta que se transmiten por bluT. En mi caso lo hacíamos por radios de VHF.

En cuanto al buffer y el ingreso de datos es algo simple de resolver. Lo hice así.
-> Lectura de puerto
> Almacena los datos en una variable string de memoria (string1) . Concatenando al final de la misma.​
-> Timer (siempre corriendo cada nnn seg)
> Concatena los datos de string1 al final del string2 (otra variable de buffer)
> eliminas de string1 los datos leidos
> Procesa desde el primer FF 4 bytes (en tu caso) y controla el chksum
> eliminas de string2 esos 4 bytes y listo..

Todo sigue su ciclo. No te detenes al leer el puerto y procesas lo que tengas completo.

El problema lo tenes en los ciclos del micro, si podes aumentale los Mhz de trabajo para que te sobre tiempo. No te combiene trabajar con los tiempos justos cuando tenes varias interrupciones que se puedan disparar al mismo tiempo y se solapen.

En cuanto a B4A, soy nuevo también y creo que otra solución sería trabajar con alguna librería que utilice los timers a bajo nivel.


Espero que te sirva de algo mi experiencia!!
Saludos!!
 

JordiCP

Expert
Licensed User
Longtime User
Bienvenido Matias!:)

Tienes razón, sin delimitación de trama es difícil. En su caso, esta delimitación sólo reside en la distancia temporal entre los bytes transmitidos. El problema es que al haber "algo" en el otro lado que funciona por eventos (y no necesariamente tiempo real), a veces puede dar problemas. Si se está refrescando la pantalla o atendiendo cualquier evento, las interrupciones del AsyncStreams pueden ir con retraso, y ya la hemos liado ;-P


@edochan: Si el micro transmite secuencias de 2 bytes , <variable, valor> ... estas variables que transmite el micro son secuenciales/cíclicas? (por ejemplo, V1,V2,V3,....,Vn,V1,V2,V3.....). Si es así, tienes otra pista para delimitar: Si el primer byte recibido corresponde a una variable Vn, esperarás la V(n+1) en el tercer byte, V(n+2) en el quinto, etc.... Mientras se vaya cumpliendo, aceptas los pares <variable,valor> porque que estás sincronizado. En el momento en que no se cumpla, descartas aquel valor y te intentas sincronizar con un byte desplazado...asi´todo el rato

Por probar que no quede...
 

edochan

Member
Licensed User
Longtime User
Buenos días.
Lo de enviar 3 o 4 bytes, estoy intentando evitarlo por lo que comenté antes, en un modelo posterior ya no tengo esa premisa, pero en este sí.
Llevo varios días enfocado precisamente en lo que propone Matias, un bufer primario y otro "general" digamos, que se carga y descarga conjuntado con el primero (lo que recoge en cada momento), creo que por ahí voy a encontrar más fiabilidad. Y ya con esto ultimo que me sugiere Jordi, tendré una doble capa de control, le dará más precisión, porque efectivamente, las variables son secuenciales, desde la 1 a la 10, los valores evidentemente no. A ver si hoy tengo tiempo y lo voy desarrollando, ya os contaré. Muchas gracias a los dos. Saludos.
 

Matias Ferrer

Member
Licensed User
Longtime User
Hola gente,

Edochan, como te ha ido con tu problema? has podido avanzar?.

Cuenta con nosotros para lo que necesites.

Saludos,
 

edochan

Member
Licensed User
Longtime User
Gracias Matias.
Estoy haciendo pruebas con las indicaciones que me disteis, efectivamente es mucho mejor creando un nuevo Dim global donde ir almacenando todo lo que entra, y en general muy bien, pero aun se producen "desincronismos", me falta adaptar lo que comentó Jordi, sabiendo que entro la variable 2, esperar que la siguiente clave sea la 3 y así sucesivamente. Creo que tengo algún que otro problema, al convertir inmediatamente los bytes (signed) en unsigned antes de guardalos en ese Dim global. Uso este método de recopilación y almacenamiento:


Sub AStream_NewData (Buffer() As Byte) ' interrupción por recepción de datos serie

Dim Cont As Int ' este Contador sirve para luego leer desde el primero al ultimo numero almacenado en buffer
Dim bytes As RandomAccessFile ' esto es lo que realmente transforma en UNSIGNED los valores de byte(-128 a 127)
Dim Datos(50) As Int ' Este es el archivo donde se guardan los valores del buffer ya convertidos

For Cont = 0 To Buffer.Length - 1
bytes.Initialize3(Buffer,True) ' inicializa(¿3?) la conversión del valor en buffer a la variable bytes
Datos(Cont) = bytes.ReadUnsignedByte(Cont)
bufer(g) = Datos(Cont) ' bufer es la matriz global int ya guardados en unsigned
g = g + 1
Next

buffer_L.text = f & ":" & g ' visualizo lo que queda por mostrar en un text.box
If g > f + 2 Then mostrar ' si el bufer (global) tiene > 2 datos sin leer

End Sub



Pero es que tengo poco tiempo para hacer pruebas y voy poco a poco.

Cuando confirme que ya no recibo fallos, prometo escribir lo que hY encontrado.

pero vamos... estoy seguro que con las indicaciones que me disteis encontraré la solución. Es cuestión de tiempo.
Reiterar mi agradecimiento a ambos, es reconfortante comprobar que aunque poca, hay gente que se molesta y entrega tiempo a los que nos iniciamos en B4A ( en Español especialmente). Ojala vaya sabiendo yo lo suficiente para hacer yo lo propio.
Saludos.
 

edochan

Member
Licensed User
Longtime User
Bueno, creo que ya tengo algo estable.
En 2 días encendido no ha registrado errores de sincronismo y se recepciono lo que se le enviaba.
de 2 en 2 bytes. Y usando un bufer global evidentemente. He aprendido mucho y gracias a vosotros he probado bastantes cosillas que me serán de utilidad.
Dejo a continuación la parte de código donde se ve como recepciono y discrimino, si solo entró 1 byte lo desprecio, si ya hay 2 antes de que un timer borre el primero, valido el par a falta de comprobar que cada valor está dentro del rango aceptable a la clave.

Saludos y un millón de gracias a ambos.


Sub timer4_tick ' IF enabled = cada 50 mseg salta
If g > 0 Then g = g - 1 ' si en 50 mseg no entro otro byte, sobreescribimos el anterior
timer4.Enabled = False
End Sub

Sub AStream_NewData (Buffer() As Byte) ' interrupción por recepción de datos serie

Dim Cont As Byte ' este Contador sirve para luego leer desde el primero al ultimo numero almacenado en buffer y poder pasarlos a UNSIGNED
Dim bytes As RandomAccessFile ' esto transforma en UNSIGNED los valores de byte(-128 a 127)

For Cont = 0 To Buffer.Length - 1
bytes.Initialize3(Buffer,True) ' inicializa(¿3?) la conversión del valor en buffer a la variable bytes
g = g + 1 ' suma un contador si hay un dato mas que meter
bufer(g) = bytes.ReadUnsignedByte(Cont) ' el bufer global
Next
If g > 1 Then ' sabiendo que entre envíos (de 2 bytes) siempre hay más de 50 mseg (casi 80 en concreto)
timer4.Enabled = False
mostrar ' esto asegura que se almacenan pares de bytes seguidos
Else
timer4.Enabled = True ' y descarta los "solitarios"
End If

End Sub

Sub mostrar ' ahora que ya almaceno en bufer correctamente la lectura es secuencial y parece correcta
If g > f Then ' llevo 2 días con el android mostrando y enviando a una DB y no ha habido errores, el tiempo dirá
If bufer(f)> 0 AND bufer(f) < 11 Then
clave = bufer(f)
f = f + 1
valor = bufer(f)
exp_L.Text = clave & ":" & valor
Else
f = f + 1
mostrar ' si no es una clave valida, probar el siguiente byte en bufer
End If
recibidos = recibidos + 1 ' se puede dar por recibido pero falta saber si existe sincronismo clave - valor

Select clave
Case 1 ' estado de marcha de la unidad
If valor > = 0 AND valor < 4 Then ' un ultimo filtro, con esta clave solo entran valores en ese rango
' el label se carga con el valor correspondiente
' Case 2 ...... etc.etc.

End Sub


Gracias.
 

edochan

Member
Licensed User
Longtime User
Al final he conseguido la mayor fiabilidad de sincronismo poniendo el timer en 6 mseg y ya parece que no se producen errores.
Estoy por poner el tema como solucionado, pero voy a esperar unos días más probando.
cleardot.gif
Muchas gracias de nuevo.
 

edochan

Member
Licensed User
Longtime User
Hola de nuevo.

Después de ponerle una serie de "Trampillas" y Logs de visualización cuando ocurrían, para ver que valores estaban manejándose en ese momento y los que había antes, decidí finalmente dejar de utilizar el timer, resulta innecesario. Esto es lo que pude observar:


1- que unas veces era por una mala gestión de los errores, al descartar los valores que no entraban dentro del rango admisible para cada clave. Para ello creé un sub específico, que además los contabilizaba y señalizaba una alarma según la cantidad de estos detectada.


2- Pero en otras simplemente se perdía algún byte, no sé si por algún ruido externo o qué, pero entre emisor y receptor se perdía alguno, o que otro proceso estuviese ocupando tiempo y no lo recepcionará, pero el caso es que en el visualizador de Logs se veía como faltaba algún que otro byte muy de vez en cuando. He dejado el PC encendido varios días por períodos de hasta 12 horas, registrando fallos.


Supongo que como ocurre a menudo, lo simple es lo más eficiente.

Así que resumiendo mucho, ahora hago lo siguiente:


1- meto todo lo que entra por AStream_NewData al archivo que hace de bufer(100) y lo leo secuencialmente con un puntero que se reinicializa a 0 al leer todo.


2- Si la clave no está dentro del margen posible (1 a 10) la descarto y en el Sub de proceso de errores sumo 1 . Pruebo el siguiente byte como clave posible, si lo es y el que le sigue es un valor dentro del rango, continua y ya se vuelve a sincronizar la secuencia, de hecho no vuelve a dar errores en poco tiempo.

Si no lo es hace lo mismo y al siguiente ya engancha la secuencia normal. Desprecio ambos ya que en este caso no es crítico perder 2 o 4 bytes y ya está.


Parece mentira que me haya liado tanto para acabar haciéndolo así de sencillo ¿no os parece?

Parecido a lo que dijo Edison : “ yo …he descubierto mil maneras de hacerlo mal”


Si alguien necesita (aunque no creo) usar un protocolo tan básico y flexible como este y tiene problemas, creo que podré echarle una mano o mandarle un ejemplo.


Saludos.
 

JordiCP

Expert
Licensed User
Longtime User
Me alegro! Lo que cuesta conseguir se saborea mejor!

Lo bueno es que ahora te has convertido en un experto en este ámbito. Cualquier cambio que tengas que hacer en tu programa sabrás cómo hacerlo y cuál es la mejor opción. Muy probablemente, si te hubiera salido a la primera sin saber por qué, no sería el caso ;)
 

edochan

Member
Licensed User
Longtime User
Gracias Jordi.
Quizás en como recibir 2 bytes si, en todo lo demás estoy en pañales, je,je.
Evidentemente no es un método muy recomendable, pero consigo que el micro no se entretenga en envíos muy largos y pueda atender otras cosas igual de importantes o más, esa era la premisa.
Por cierto, te hice caso, como ahora grabo en 2 variables la ultima clave y valor recibida, en principio para visualizarlas en el Log por si se produce una entrada no valida ("error" de recepción); pero me viene muy bien para hacer lo que tu me aconsejaste, ligar la secuencia a la ultima clave que se había recibido. Esto consigue que si se produce un "salto" en la lectura de bytes, aunque lea una clave que en realidad es un valor, lo desprecie aunque ese valor esté dentro del rango de las claves posibles.
Refuerza la fiabilidad de lo que acepta como válido, ya te comenté que perder unos bytes en este caso no es critico, al segundo siguiente vuelve a entrar toda la secuencia de nuevo y la reengancha.

Case 2 ' Velocidad del Ventilador
If valor > 0 AND clave2 = 1 Then ' clave2 contiene la ultima clave válida que entró
Vc = valor * 0.3922
Vc_L.Text = NumberFormat (Vc, 0, 0)
Else
Vc_L.Text = "OFF"
End If


Muchas gracias de nuevo.
 

Matias Ferrer

Member
Licensed User
Longtime User
Muy buena Edochan!... coincido con Jordi, por ahí aunque no nos guste lo que parece complicado nos ayuda a seguir aprendiendo y sobre todo a ir perfeccionando nos en cada desafío.

Lo felicito!. ha dado un paso mas en su carrera :)
 
Top