Datalogger bus CAN

Hola, quería comentaros mi problema a ver si alguien ha estado en lo mismo o si alguien tiene una solución. La cosa es que recojo los mensajes CAN con el mcp2515 y vía bus SPI son recogidos por Arduino por interrupciones (mensajes pueden llegar en orden de 2 mensajes en 1ms). Despúes quiero guardar estos mensajes en una tarjeta SD y aquí llegan los problemas... ya que la tarjeta SD debe compartir el bus SPI con el mcp2515 y la escritura en la tarjeta debe de ser en bloques de 512 bytes (tarda en orden de 4ms en esa escritura, he de decir que con la libreria SDFat.h no se solventa el problema), por lo que en esos 4ms he podido perder hasta 8 tramas!!

Posibles soluciones que he pensado:

-- Vi que es posible diseñar otro bus SPI con otros pines libres para que no tengan que compartir el bus el mcp2515 y módulo SD... no lo he probado y me cuesta creer que funcione
-- Reformar el diseño a MCP2515---SPI--Arduino1---i2C---Arduino2---SPI---módulo SPI (el problema que veo aquí son las limitaciones del protocolo i2C que me haga cuello de botella)

¿De cuánto es la longitud de un "mensaje" (en bytes)? Si dices que son dos por milisegundo; necesitaré determinar si es posible reenviarlos por USART (puerto serie). A 115200 bps, se pueden enviar aprox. 10 KB/s; pero puede que haya que agrandar el búfer TX para evitar enviar mensajes incompletos.

Un segundo bus SPI imposible a nivel de hardware; y a nivel de software nunca será tan veloz.

Mi sugerencia de hecho involucraba dos Arduinos. No pueden interconectarse por SPI porque el bus ya está ocupado en ambas partes. El protocolo de I2C no creo que permita un flujo de datos continuo sin tener que pasar por los procesos de inicialización repetidas veces; algo que sé que en USART es posible (solo haces el Serial.begin() una vez y ya estás listo para enviar/recibir datos indefinidamente).
Escribir un bloque en una tarjeta SD inevitablemente tarda unos milisegundos; por lo tanto en el receptor tendrás que crear un búfer lo suficientemente grande para que disminuya la pérdida de datos.
Si tuvieras la tarjeta libre de absolutamente todo archivo (formatear con SDFormatter de ser posible), quizá ganes un poquito más de velocidad. Crear un archivo con cierta cantidad de clústeres contiguos también ayuda.

PD: ¿Es posible en ciertas ocasiones "paralizar" el flujo de datos en el bus CAN? Eso aliviaría muchas cargas.

No puedo paralizar el flujo de datos, de hecho quería implementar un botón que cuando sea pulsado haga un close() del fichero y termine todo, pero hasta entonces el flujo será continuo.

La solución Serial que comentas creo que seguirá haciendo cuello de botella...

Hola.

Para tener una idea mas clara del problema:

  • ¿cuantos bytes de informacion necesitas almacenar por cada mensaje recibido?
  • ¿realmente no hay tiempos muertos entre mensajes?. El uso de las interrupciones sugiere que sí.
  • el bus que loggeas es el "low speed" supongo ¿no?

Saludos.

albdom:
La solución Serial que comentas creo que seguirá haciendo cuello de botella...

Todas formas de comunicación posible entre Arduinos van a ser un cuello de botella de cualquier modo; lastimosamente siendo SPI (por hardware) el más rápido de los tres.

En teoría, USART puede ir hasta 2 Mbps (151 KB/s); sin embargo la frecuencia de interrupciones sería tan ridícula (200 KHz) que cuando corresponda la escritura a la tarjeta SD, bastantes bytes se perderían si el búfer alcanza a llenarse.
Aunque... dependiendo de cuántos bytes por segundo deba recibir, lo anterior más bien puede no suponer un problema.

albdom:
No puedo paralizar el flujo de datos, de hecho quería implementar un botón que cuando sea pulsado haga un close() del fichero y termine todo

Es lo mejor. Re-abrir el archivo múltiples veces genera retrasos de hasta 30 milisegundos.

También conozco otras optimizaciones que tal vez ayuden; por ejemplo: leer el estado de un pin sin digitalRead

Alfaville:
Hola.

Para tener una idea mas clara del problema:

  • ¿cuantos bytes de informacion necesitas almacenar por cada mensaje recibido?
  • ¿realmente no hay tiempos muertos entre mensajes?. El uso de las interrupciones sugiere que sí.
  • el bus que loggeas es el "low speed" supongo ¿no?

Saludos.

55 bytes por mensaje van a ser almacenados en la SD.

El bus donde capturo los datos tiene una velocidad de 500kbps, y es un tráfico con ráfagas.

Me gustaría preguntaros si es posible en un diseño con 2 arduinos (2 buses SPI) si 1 arduino por ejemplo puede recoger datos de un bus SPI( por ejemplo el conectado al mcp) y después recoger o enviar datos al otro bus SPI(SD).

55 bytes por mensaje van a ser almacenados en la SD.

cuantos mensajes?

Hasta que se pulse el interruptor... por lo que debe de tratar los datos instantáneamente. Echando cálculos las escrituras en la SD se hacen cada 9 mensajes aproximádamente.

55 bytes por mensaje.
2 mensajes por milisegundo = 110 bytes por milisegundo.
110 bytes por milisegundo = 110000 bytes por segundo (aprox. 107 KB/s).

Hace tiempo, hice una prueba de escritura pero con la librería SD; en ATmega328P a 16 MHz logré hasta 57 KB/s; por lo tanto de momento parece imposible mantenerle el ritmo al bus CAN.
Debería hacer pruebas, pero puede que esa cifra mejore si hacemos la escritura a un nivel más bajo (ignorando el sistema de archivos). Escribiendo bloques enteros continuamente para calcular la verdadera velocidad máxima en una SD funcionando con SPI (velocidad de bus máxima posible).

Puede sonar extraño, pero tiene sentido: podríamos ganar un poco más de velocidad deshabilitando la interrupción que hace que delay, millis y micros funcionen.

Lucario448:
55 bytes por mensaje.
2 mensajes por milisegundo = 110 bytes por milisegundo.
110 bytes por milisegundo = 110000 bytes por segundo (aprox. 107 KB/s).

Hace tiempo, hice una prueba de escritura pero con la librería SD; en ATmega328P a 16 MHz logré hasta 57 KB/s; por lo tanto de momento parece imposible mantenerle el ritmo al bus CAN.
Debería hacer pruebas, pero puede que esa cifra mejore si hacemos la escritura a un nivel más bajo (ignorando el sistema de archivos). Escribiendo bloques enteros continuamente para calcular la verdadera velocidad máxima en una SD funcionando con SPI (velocidad de bus máxima posible).

Puede sonar extraño, pero tiene sentido: podríamos ganar un poco más de velocidad deshabilitando la interrupción que hace que delay, millis y micros funcionen.

Totalmente de acuerdo, de esa manera será imposible soportarlo.

La función millis() la estoy usando para etiquetar las tramas después en el fichero.

Me sugiere una duda el protocolo/bus SPI que podría jugar con ello. Si hay 2 buses SPI inizializados por diferentes Arduinos, un Arduino(maestro) puede cambiarse de bus dando su señal de reloj y su respectivo MOSI/MISO sin tener que inicializar de nuevo la comunicación SPI?

Yo con DUE logré tasas mas altas con una SD.

9 meseajes x 55 bytes = 495 bytes

Veamos como se puede conseguir de meter 512 bytes en una SD en 1 mseg.

Una posible solución seria usar un TeenSy que tiene DMA y podrias usarlo

Lucario448:
Debería hacer pruebas, pero puede que esa cifra mejore si hacemos la escritura a un nivel más bajo (ignorando el sistema de archivos). Escribiendo bloques enteros continuamente para calcular la verdadera velocidad máxima en una SD funcionando con SPI (velocidad de bus máxima posible).

Eso acabo de hacer y aquí los resultados:

Escritura de 100 bloques consecutivos utilizando Sd2Card: 51200 bytes en 589468 microsegundos.

Si dividimos ambas cifras y el resultado lo multiplicamos por 1000, obtenemos que a nivel bajo solo se logran escribir aprox. 87 bytes por milisegundo. Un mensaje por milisegundo a duras penas podrá, pero dos jamás.
A fin de cuentas, 85 KB/s. Esto refuerza aún más la sugerencia de surbyte.

PD: espero y que haya estado funcionando a la velocidad frecuencia_CPU / 2 (8 MHz en este caso), porque Sd2Card intenta realizar las transacciones a 25 MHz.

Viendo los numeros que se manejan, creo que solo hay dos soluciones "realistas":

  • Montar una placa con memoria SRAM en cantidad suficiente (el acceso es mucho mas rápido que la SD)
  • Usar una placa mas potente. En este caso escogeria la Arduino DUE por lo siguiente:
  • memoria SRAM de 96kB- control integrado del bus CAN (timestamp incluido)- I2C, USART, SPI, DMA,..- ... y un largo etcetera

Más datos de las prestaciones del chip que lleva la placa Datasheet (ver pagina 1188 para bus CAN)

Yo con DUE logré tasas mas altas con una SD.

voy a repetirlas pero estaban por encima de los 700kb/seg

Continuo trabajando en ello. Estoy intentando solucionarlo con un diseño de 2 arduinos en paralelo teniendo así 2 buses SPI y cada Arduino accede a un bus por medio de multiplexores.

Ahora, cada Arduino recoge X tramas CAN (menos de 512 bytes) y después se cambia de bus y escribe esta información en la SD (por medio de myFile.print(variable) y después fuerzo la escritura con flush()). Acá llega mi duda, cómo puedo de la manera más eficiente almacenar esas tramas(obviamente sin llamar a las clases de la librería SD). Con variables char no hay problemas, pero me obligaría a crear X variables char como X tramas haya y cuando pruebo con Strings o Arrays me dan sólo problemas de conversiones.

¿Eficiente?

  • Usar write en vez de print.
  • Evitar flush.
  • No reabrir el archivo con frecuencia.
  • Preferiblemente hacer write en grupos de bytes o caracteres; eso involucra arrays.

albdom:
y cuando pruebo con Strings o Arrays me dan sólo problemas de conversiones.

String sinceramente no lo recomiendo como "búfer eficiente".
Problemas con arrays creo saber por qué, pero mejor me quedo a esperar tu explicación.

Sigo luchando...

Actualmente estoy almacenando en char o bufferes propios información y luego llamo del tirón a write() varias veces, el problema es que la librería SDFat reserva 512 bytes y mis buffers no pueden llegar a 512 bytes por tamaño insuficiente en la RAM y tengo que forzar la escritura con un flush() el cual tarda entre 10-20ms en escribir... no puedo gastar ese tiempo...

Hay alguna manera de escribir directamente en el buffer de la libreria SDFat sin tener que llamar a una de sus funciones? Ya que si llamo a alguna de las funciones Arduino intenta comunicarse con la SD y al no estar conectado en ese momento con la SD y falla...

albdom:
Hay alguna manera de escribir directamente en el buffer de la libreria SDFat sin tener que llamar a una de sus funciones?

Hacerlo así no es buena idea. Ese búfer muy probablemente también tenga variables contadoras; modificar su contenido sin pasar por las funciones podría ocasionar funcionamiento incorrecto en el programa.
No es por nada que ciertos atributos sean "privados".

albdom:
Ya que si llamo a alguna de las funciones Arduino intenta comunicarse con la SD y al no estar conectado en ese momento con la SD y falla...

Primero: ¿por qué puede fallar? Si la inicialización de la tarjeta falló, no tiene por qué todavía intentar abrir un archivo y escribir en él.

Segundo: aparte de flush; quizá sean print y println los que siempre garantizan acceso a la tarjeta. write no; este lo hace sólo cuando el búfer se llena.

Lucario448:
Primero: ¿por qué puede fallar? Si la inicialización de la tarjeta falló, no tiene por qué todavía intentar abrir un archivo y escribir en él.

Tengo 2 arduinos funcionando a la vez y cada uno o está comunicandose con el MCP o con la tarjeta SD, cada módulo está en un bus spi diferente. Por lo que a la hora de capturar las tramas del mcp la comunicación con la SD no es posible.

En conclusión necesito hacer una escritura del orden de 1-2ms de un bloque total de 200bytes aproximadamente(lo que me da la RAM para hacer mis buffers) divididos en 8 char, osea que llamo a write() 8 veces seguidas y después flush() y me tarda una eternidad...

¿Y si en vez de usar búferes intermedios lo mandas directamente con write?
Se supone que la librería sabe cuándo hacer flush.