Velocidad de transmisión del Arduino Ethernet Shield

Buenas!

Estoy desarrollando un proyecto en el cual el Arduino funciona como servidor. Utilizo el módulo Ethernet Shield con el chip Wiznet W5100 para transmitir los ficheros almacenados en la tarjeta micro SD a los clientes que se conectan al servidor. Las librerías que uso son la Ethernet.h, la SD.h y la SPI.h.

Mi modus operandi es el siguiente: cada vez que tengo que transmitir un archivo lo que hago es ir llenando un buffer de 1 KB con los datos del archivo y cuando este está lleno se lo paso al cliente. Hago esto hasta que se ha transmitido todo el archivo. El problema es que tengo un archivo que pesa 400 KB y tarda unos 30 segundos en transmitirse (además, en un futuro tendré que enviar archivos que pesaran varios MB y a saber cuanto tardaran en transmitirse).

Mi duda: ¿hay alguna manera de incrementar la velocidad de transmisión? ¿quién me la está limitando? ¿la comunicación por el bus SPI entre Arduino y tarjeta SD/chip Wiznet W5100 o la propia comunicación entre el módulo Ethernet y el router/PC?

Muchas gracias!

Kane12:
¿hay alguna manera de incrementar la velocidad de transmisión?

  • Optimizar el código
  • Cambiar a un micro con más RAM y frecuencia de reloj (a menos que ya se haya alcanzado el límite de frecuencia de reloj SPI del módulo Ethernet).
  • Cambiar completamente de equipo; desde un SBC (ej.: Raspberry Pi) hasta un PC portátil.

Kane12:
¿quién me la está limitando? ¿la comunicación por el bus SPI entre Arduino y tarjeta SD/chip Wiznet W5100 o la propia comunicación entre el módulo Ethernet y el router/PC?

Yo diría más lo primero. Los factores que lo limitan son los siguientes:

  • Compartición del bus: ya que al módulo Ethernet y SD no se le pueden hablar al mismo tiempo, entonces se turnan; lo que implica que se acceden en tiempos compartidos y secuenciales. En resumen, el "ancho de banda" (así por decirlo) del bus SPI se reparte, debido a que ambos dispositivos utilizan el mismo canal.
  • Escasez de memoria RAM: las operaciones de entrada/salida siempre (e inevitablemente) son un cuello de botella en un sistema computacional; de ahí surgen técnicas como el buffer o la caché. Estas se implementan en memoria RAM debido a que su velocidad supera con creces a cualquier otro tipo de almacenamiento digital (excepto los registros y caché de CPU, pero eso ya es otro cuento). Los microcontroladores suelen venir escasos de RAM, lo que los condena a un bajo rendimiento en transferencias voluminosas de datos.
  • Carencia de DMA: el acceso directo a memoria o DMA es una tecnología que permite a los periféricos (como el controlador SPI) acceder a una parte de la memoria sin intervención de la CPU (microprocesador). Sin DMA, este último tiene que estar moviendo cada byte del flujo hacia o desde la RAM (cosa que los micros no ARM tienen que hacer); evidentemente no es muy eficiente, sin embargo dicha implementación todavía se utiliza.
  • Frecuencia del reloj: la mayoría de dispositivos SPI pueden trabajar hasta 20 MHz; pero debido a los 16 MHz de los AVR, la frecuencia máxima del SPI es de apenas 2 MHz (10 veces más lento que su máximo).

La red en no sería un problema, lo más probable es que las interfaces sean Fast Ethernet, que va hasta los 100 Mbps (12 MB/s). Si hay comunicación inalámbrica de por medio (Wi-Fi), la velocidad sería más como de 54 Mbps (6.8 MB/s).

En cualquier caso, la limitante sigue siendo el Arduino. En Arduino Uno estimo que la velocidad efectiva de transferencia es de más o menos 240 Kbps (30 KB/s) para descarga; y 176 Kbps (22 KB/s) para carga.

Esta podría ser la solución:

https://www.emartee.com/product/42271/Taijiuino%20Due%20Pro%20R3%20%20Arduino%20Due%20Compatible

Gracias por contestar!

En mi caso no lo había dicho, utilizo un Arduino Mega 2560.

@Lucario448 Buena explicación! Lo único a lo que no veo sentido es a esto:

Frecuencia del reloj: la mayoría de dispositivos SPI pueden trabajar hasta 20 MHz; pero debido a los 16 MHz de los AVR, la frecuencia máxima del SPI es de apenas 2 MHz (10 veces más lento que su máximo).

¿Por qué la frecuecia del SPI no es de 16 MHz? No entiendo porque tiene que ser de 2 MHz cuando los dos pueden trabajar a 16 MHz.

En cuanto a la configuración de la librería SPI, ¿sabéis si puedo opmitizar algo? ¿o ella lo configura en función del microcontrolador detectado?

@ard_newbie Sería una buena opción! Micro de 32 bits, DMA, CPU Clock a 84 Mhz y 96 KB de SRAM.

Kane12:
@ard_newbie Sería una buena opción! Micro de 32 bits, DMA, CPU Clock a 84 Mhz y 96 KB de SRAM.

Para ver:

http://forum.arduino.cc/index.php/topic,142908.0.html

tenga en cuenta también, la biblioteca TurboSpi(42 MHz):

Kane12:
¿Por qué la frecuecia del SPI no es de 16 MHz? No entiendo porque tiene que ser de 2 MHz cuando los dos pueden trabajar a 16 MHz.

Excelente pregunta. Hay dos motivos por los cuales el reloj general (16 MHz en la mayoría de micros AVR) no es usado como señal de reloj para SPI (SCK):

Así esta configurado por hardware. El controlador SPI en realidad sí parte de la señal general de reloj (la generada por el oscilador externo); excepto que de por medio tiene algo llamado "prescaler", un contador interno utilizado para dividir la frecuencia (con un divisor de potencia 2), retrasando en la salida, el tiempo transcurrido entre conmutaciones.
El controlador SPI de un microcontrolador AVR (también presente en el Arduino Mega) tiene un prescaler cuya división mínima es de 2. Así es, me había equivocado en esa parte; por lo tanto, la frecuencia máxima sería de 8 MHz (de hecho, hasta dentro de la librería SD es lo que se define como "full speed" o "velocidad máxima"); otorgando así una velocidad "bruta" de 125 KB/s. Quizá pienses que sea un mal diseño, pero lo que no sabes es que es se debe a la compensación de la capacitancia e inductancia parásitas (ambos distorsionan una señal digital), y al siguiente motivo.

Carece de DMA. Recuerda que sin este, el CPU debe atender el evento de recepción que ocurre por cada byte (8 bits). Si la señal del reloj general estuviera conectada al del SPI, el CPU estaría "distrayéndose" (más exactamente "siendo interrumpido") cada 8 ciclos. Por si me lo preguntaras, esa frecuencia es tan excesiva que casi no tendría tiempo para ejecutar el resto del programa.
Aunque bueno, no es gran pérdida si se usa SPI.transfer(), ya que esta función es bloqueante de todos modos; pero si el uso del controlador SPI se hiciera "manualmente", sí podría ser un problema.

A 16 MHz, había mencionado que SPI tiene una velocidad de 125 KB/s; sin embargo había dicho que era un valor bruto ya que en la práctica, con tanto código adicional (sobrecargo), un valor más realista sería 100 KB/s o incluso menos. Por lo tanto pondría más o menos 95 KB/s como "máximo absoluto". Con el código de la librería que maneja el dispositivo más los posibles tiempos de espera que entorpezcan una transmisión continua, la velocidad efectiva se reduce aún más (usualmente a 70 KB/s en el mejor de los casos).
Con DMA, aparte de poderse transferir múltiples bytes sin intervención del CPU; en grandes bloques la velocidad efectiva podría acercarse mucho más a los 125 KB/s (de nuevo, a menos que el "esclavo" SPI introduzca retrasos intencionalmente).

En fin, una prueba de por qué el mismo Arduino es el cuello de botella de tu proyecto; aunque se cambie a un micro más veloz y todo, se llegará al punto en que el cuello de botella termina siendo el mismo módulo Ethernet y/o SD. Si se alcanzara dicho punto, no habría más remedio que reemplazar por un SBC o un PC.

Excelente explicación @Lucario448. Muchas gracias!