Diferencia entre revisiones de «SD»

De ArduWiki
Saltar a: navegación, buscar
m
(Descripción)
 
(No se muestran 46 ediciones intermedias de 2 usuarios)
Línea 1: Línea 1:
 
== Descripción ==
 
== Descripción ==
Es una librería que habilita un Arduino para la interacción con tarjetas SD (Secure Digital) mediante el protocolo '''Serial Peripheral Interface''' ([[SPI]]). A pesar de que existen tres tamaños distintos, todos operan exactamente de la misma manera (a determinada capacidad); por lo tanto, esta librería funciona con todos los tamaños.
+
La librería [https://github.com/arduino-libraries/SD SD.h] de SparkFun habilita un Arduino para la interacción con tarjetas SD (Secure Digital) mediante el protocolo '''Serial Peripheral Interface''' ([[SPI]]). A pesar de que existen tres tamaños distintos, todos operan exactamente de la misma manera (a determinada capacidad); por lo tanto, esta librería funciona con todos los tamaños.
  
Debido a que utiliza SPI, la conexión debe realizarse hacia los respectivos pines del Arduino; salvo SS/CS, este puede conectarse en cualquier pin digital libre.
+
Debido a que usa [[SPI]], la conexión debe realizarse hacia los respectivos pines del Arduino; salvo SS/CS, este puede conectarse en cualquier pin digital libre.
  
{{Nota|la librería soporta tarjetas SD del estándar '''SC''' y '''HC'''; por esta razón, la capacidad máxima soportada es de '''32 GB'''.}}{{Nota|la librería está preinstalada junto con la IDE de Arduino, no es necesario descargarla.}}{{Nota|debido a limitaciones de la librería y sistema de archivos compatibles, el tamaño máximo por archivo es de 4 GB o 4294967295 bytes.}}
+
{{Nota|La librería soporta tarjetas SD del estándar '''SC''' y '''HC'''; por esta razón, la capacidad máxima soportada es de '''32 GB'''.}}
  
== Formato de la ruta de acceso ==
+
{{Tip|Librería oficial, es decir que está preinstalada junto con la [[IDE]] de Arduino, no es necesario descargarla.}}
 +
 
 +
{{Nota|debido a limitaciones de la librería y sistema de archivos compatibles, el tamaño máximo por archivo es de 4 GB o 4294967295 bytes.}}
 +
 
 +
Una mejor opción sera la libreria [https://github.com/greiman/SdFat SdFat] de Bill Greiman que soporta FAT16 y FAT32.
 +
 
 +
== La clase SD ==
 +
Es un objeto preinstanciado que se utiliza para iniciar la tarjeta y realizar operaciones generales sobre el sistema de archivos ([https://es.wikipedia.org/wiki/Tabla_de_asignaci%C3%B3n_de_archivos FAT] en sus versiones de 16 y 32 bits; '''exFAT''' no es compatible).
 +
 
 +
=== Sintaxis ===
 +
<pre>
 +
#include <SPI.h>
 +
#include <SD.h>
 +
SD.begin(CS);
 +
</pre>
 +
 
 +
=== Parametros ===
 +
;CS:Puerto 4 si es Aduino Shield.
 +
 
 +
=== Metodos ===
 +
{| class="wikitable"
 +
|+Metodos de la clase SD
 +
|-
 +
! Método !! Descripción
 +
|-
 +
| [[SD.begin()]] || Intenta inicializar la tarjeta SD al modo SPI, para así obtener de esta la información necesaria para empezar a usarla.
 +
|-
 +
| [[SD.end()]] || Devuelve la librería al estado inicial; útil para remover, "en caliente" y de forma segura, la tarjeta.
 +
|-
 +
| [[SD.open()]] || Abre un archivo o una carpeta.
 +
|-
 +
| [[SD.exists()]] || Verifica si determinado archivo o carpeta realmente existe en la tarjeta.
 +
|-
 +
| [[SD.remove()]] || Elimina un archivo.
 +
|-
 +
| [[SD.mkdir()]] || Crea un directorio (carpeta).
 +
|-
 +
| [[SD.rmdir()]] || Elimina un directorio (carpeta).
 +
|}
 +
 
 +
{{Nota|prácticamente ningún otro método funcionará si no se inicializa de antemano la tarjeta SD.}}
 +
 
 +
== La clase File ==
 +
Es un objeto que representa la instancia de un fichero (archivo) o directorio (carpeta) dentro de la tarjeta SD; con este se interactúa directamente sobre ellos. Se crea mediante las funciones [[SD.open()]] o [[File.openNextFile()]].
 +
 
 +
=== Sintaxis ===
 +
<pre>
 +
File variable1 = SD.open(nombre, modo);
 +
File variable2 = variable1.openNextFile();
 +
</pre>
 +
 
 +
=== Parámetros ===
 +
;variable:Nombre de variable a instanciar.
 +
;nombre:Nombre de archivo SFN (8.3). Ejemplo log12345.txt.
 +
;modo:Puede ser FILE_WRITE o FILE_READ
 +
 
 +
=== Metodos ===
 +
{| class="wikitable"
 +
|+Metrodos de la clase File
 +
|-
 +
!Método!!Descripción
 +
|-
 +
| [[File.read()]] || Lee un byte/caracter o varios del archivo.
 +
|-
 +
| [[File.peek()]] || Lee un byte/caracter del archivo sin avanzar al siguiente.
 +
|-
 +
| [[File.available()]] || Retorna la cantidad de bytes/caracteres disponibles para leer (antes de llegar al final del archivo).
 +
|-
 +
| [[File.write()]] || Escribe un byte/caracter o varios al archivo. Suele usarse para archivos binarios.
 +
|-
 +
| [[File.print()]] || Escribe una representación textual de un dato (o una cadena de caracteres) al archivo. Suele usarse para archivos de texto plano.
 +
|-
 +
| [[File.println()]] || Igual al anterior, excepto que agrega una nueva línea de texto.
 +
|-
 +
| [[File.flush()]] || Fuerza la actualización de los datos en la tarjeta SD.
 +
|-
 +
| [[File.seek()]] || Posiciona el "cursor" del archivo hacia una posición dada.
 +
|-
 +
| [[File.position()]] || Retorna la posición actual del "cursor" del archivo.
 +
|-
 +
| [[File.size()]] || Retorna el tamaño del archivo (en bytes).
 +
|-
 +
| [[File.name()]] || Retorna el nombre del archivo/carpeta.
 +
|-
 +
| [[File.isDirectory()]] || Verifica si esta instancia realmente representa un directorio (carpeta) y no un archivo regular.
 +
|-
 +
| [[File.openNextFile()]] || Abre el siguiente archivo de la lista (válido si esta instancia representa un directorio/carpeta).
 +
|-
 +
| [[File.rewindDirectory()]] || Devuelve la iteración de archivos al comienzo (válido si esta instancia representa un directorio/carpeta).
 +
|-
 +
| [[File.close()]] || Cierra el archivo (o carpeta) para liberar correctamente cualquier recurso que esté siendo utilizado por esta instancia.
 +
|}
 +
 
 +
{{Nota|Prácticamente ningún método funcionará si la instancia es "vacía" o representa un archivo/carpeta sin abrir.}}
 +
 
 +
=== Formato de la ruta de acceso ===
 
Esta librería utiliza el formato de rutas de UNIX/Linux: el nombre de las carpetas involucradas se separa con '''/''' (barra inclinada o slash), siendo el destino final el último nombre de la secuencia. Por ejemplo:
 
Esta librería utiliza el formato de rutas de UNIX/Linux: el nombre de las carpetas involucradas se separa con '''/''' (barra inclinada o slash), siendo el destino final el último nombre de la secuencia. Por ejemplo:
<pre>ruta/hacia/el/archivo/que/buscamos.txt</pre>
+
 
 +
<pre>
 +
datos.txt
 +
ruta/hacia/el/archivo/que/buscamos.txt
 +
</pre>
 +
 
 +
Esto hará que el archivo '''datos.txt''' sea creado o buscado en el directorio raíz; en otras palabras, fuera de toda carpeta.
 +
 
 +
{{Nota|Para efectos del sistema de archivos, los nombres '''NO SON''' sensibles a las mayúsculas; por lo tanto, '''datos.txt''', '''Datos.txt''' y '''DATOS.TXT''' hacen referencia al mismo archivo. Lo mismo aplica para directorios/carpetas.}}
 +
 
 
Siendo '''ruta''', '''hacia''', '''el''', '''archivo''' y '''que''' las carpetas que nos llevan al archivo '''buscamos.txt'''
 
Siendo '''ruta''', '''hacia''', '''el''', '''archivo''' y '''que''' las carpetas que nos llevan al archivo '''buscamos.txt'''
  
 
Ese destino no tiene que ser solamente un archivo regular, también podría ser otra carpeta.
 
Ese destino no tiene que ser solamente un archivo regular, también podría ser otra carpeta.
 
  
 
Si es algo que queremos colocar o ubicar en la "raíz", basta con simplemente colocar el nombre del elemento. Por ejemplo:
 
Si es algo que queremos colocar o ubicar en la "raíz", basta con simplemente colocar el nombre del elemento. Por ejemplo:
<pre>datos.txt</pre>
 
Esto hará que el archivo '''datos.txt''' sea creado o buscado en el directorio raíz; en otras palabras, fuera de toda carpeta.
 
  
 
+
{{Nota|ya que el directorio de trabajo de la librería siempre es la raíz, da igual que la ruta sea absoluta (con '''/''' al comienzo) o relativa (sin '''/''' al comienzo).}}
{{Nota|ya que el directorio de trabajo de la librería siempre es la raíz, da igual que la ruta sea absoluta (con '''/''' al comienzo) o relativa (sin '''/''' al comienzo).}}{{Nota|para efectos del sistema de archivos, los nombres no son sensibles a las mayúsculas; por lo tanto, '''datos.txt''', '''Datos.txt''' y '''DATOS.TXT''' hacen referencia al mismo archivo. Lo mismo aplica para directorios/carpetas.}}
 
  
 
=== Nombres SFN (8.3) ===
 
=== Nombres SFN (8.3) ===
 
Parte de la escritura de rutas también involucra respetar el formato SFN (Short File Name o nombre corto de archivo), el cuál sigue estas reglas:
 
Parte de la escritura de rutas también involucra respetar el formato SFN (Short File Name o nombre corto de archivo), el cuál sigue estas reglas:
  
* El nombre del elemento debe tener como mínimo 1 caracter, y como máximo 8 caracteres.
+
* El nombre del elemento debe tener como mínimo 1 carácter, y como máximo 8 caracteres.
 
* Si es un archivo regular, debe haber un único punto ('''.''') justo después del nombre.
 
* Si es un archivo regular, debe haber un único punto ('''.''') justo después del nombre.
 
* Si es un archivo regular, deben haber de 1 a 3 caracteres después del antes mencionado punto. Esto es lo que se conoce como la "extensión", una forma burda (pero muy usada en Windows) de etiquetar archivos por su contenido (ej.: '''txt''' para texto plano, '''jpg''' para imágenes codificadas en JPEG, '''mp3''' para audio codificado en MPEG capa 3, etc.).
 
* Si es un archivo regular, deben haber de 1 a 3 caracteres después del antes mencionado punto. Esto es lo que se conoce como la "extensión", una forma burda (pero muy usada en Windows) de etiquetar archivos por su contenido (ej.: '''txt''' para texto plano, '''jpg''' para imágenes codificadas en JPEG, '''mp3''' para audio codificado en MPEG capa 3, etc.).
Línea 30: Línea 130:
 
{{Nota|deduciendo de las reglas anteriores, los directorios/carpetas solo deben cumplir la primera; no debe haber punto ('''.''') ni extensión en el nombre.}}
 
{{Nota|deduciendo de las reglas anteriores, los directorios/carpetas solo deben cumplir la primera; no debe haber punto ('''.''') ni extensión en el nombre.}}
  
==== Caracteres permitidos ====
+
=== Caracteres permitidos ===
En el nombre puede venir cualquier caracter, excepto los siguientes:
+
En el nombre puede venir cualquier carácter, excepto los siguientes:
  
 
* <code>" * + , / : ; < = > ? \ [ ] | </code>
 
* <code>" * + , / : ; < = > ? \ [ ] | </code>
 
* Los que tienen un valor decimal del 0 al 31
 
* Los que tienen un valor decimal del 0 al 31
* El caracter de valor decimal 127
+
* El carácter de valor decimal 127
 
* El punto ('''.''') sólo si no es antecedido por al menos un caracter permitido.
 
* El punto ('''.''') sólo si no es antecedido por al menos un caracter permitido.
  
Técnicamente se permite el caracter espaciador y los del ASCII extendido (valor decimal del 128 al 255), sin embargo el primero no se recomienda porque puede hacer más complicado programar la generación de rutas; y lo segundo tampoco ya que la codificación no es estándar entre regiones geográficas (lo que puede llevar a una interpretación errónea del nombre en cuestión), eso sin contar que si el primer caracter es de valor '''0xE5''' (229 decimal), la librería podría después mal interpretarlo como un archivo eliminado (que no existe).
+
Técnicamente se permite el carácter espaciador y los del ASCII extendido (valor decimal del 128 al 255), sin embargo el primero no se recomienda porque puede hacer más complicado programar la generación de rutas; y lo segundo tampoco ya que la codificación no es estándar entre regiones geográficas (lo que puede llevar a una interpretación errónea del nombre en cuestión), eso sin contar que si el primer carácter es de valor '''0xE5''' (229 decimal), la librería podría después mal interpretarlo como un archivo eliminado (que no existe).
  
Curiosamente, las letras minúsculas están prohibidas; sin embargo esto no es cierto al crear e ingresar la ruta, ya que la librería automáticamente utiliza, en su lugar, las respectivas contrapartes mayúsculas al procesarla.
+
Curiosamente, las letras minúsculas están prohibidas; sin embargo esto no es cierto al crear e ingresar la ruta, ya que la librería automáticamente usa, en su lugar, las respectivas contrapartes mayúsculas al procesarla.
  
 +
=== Comentarios ===
 +
Hereda de la clase [[Stream]], lo que quiere decir que prácticamente comparte la mayoría de métodos (y funcionalidad) de incluso la clase [[Serial]]. Que algunos no sean mencionados en esta página, no quiere decir que no se puedan usar.
  
== La clase SD ==
+
{{Nota|a partir de la versión 1.0 del [[IDE]] de Arduino, se permite tener múltiples archivos abiertos a la vez.}}
Es un objeto preinstanciado que se utiliza para iniciar la tarjeta y realizar operaciones generales sobre el sistema de archivos ([https://es.wikipedia.org/wiki/Tabla_de_asignaci%C3%B3n_de_archivos FAT] en sus versiones de 16 y 32 bits; '''exFAT''' no es compatible).
 
  
=== Métodos ===
+
{{Nota|El flujo de entrada y salida de esta clase se categorizan como '''por bloques'''.}}
{{Nota|prácticamente ningún otro método funcionará si no se inicializa de antemano la tarjeta SD.}}
 
  
* [[SD.begin()]] - Intenta inicializar la tarjeta SD al modo SPI, para así obtener de esta la información necesaria para empezar a utilizarla.
+
{{Nota|Este objeto se puede introducir directamente en un contexto booleano (ej.: como condición de un [[if... else]]); se evaluará como verdadero o '''true''' si el archivo/carpeta que representa está abierto/a en dicha instancia, falso o '''false''' en el caso contrario.}}
* [[SD.end()]] - Devuelve la librería al estado inicial; útil para remover, "en caliente" y de forma segura, la tarjeta.
 
* [[SD.open()]] - Abre un archivo o una carpeta.
 
* [[SD.exists()]] - Verifica si determinado archivo o carpeta realmente existe en la tarjeta.
 
* [[SD.remove()]] - Elimina un archivo.
 
* [[SD.mkdir()]] - Crea un directorio (carpeta).
 
* [[SD.rmdir()]] - Elimina un directorio (carpeta).
 
  
 +
=== Cómo agregar fecha y hora a los archivos ===
 +
Todo archivo/fichero y directorio/carpeta tiene por atributos, además del nombre, la fecha y hora de creación, último acceso y última modificación (datos que se pueden mirar en las "propiedades" o "detalles" del elemento, en el explorador de archivos de tu PC). La librería por defecto no tiene definido cómo obtener dicha información; pero cuando es requerido, por defecto coloca el '''01/01/2000 1:00:00 am''' aunque haya transcurrido un tiempo desde que el sistema ha arrancado.
  
== La clase File ==
+
Para definir la obtención de esos datos, primero debes crear una función con la siguiente "firma" (declaración):
Es un objeto que representa la instancia de un fichero (archivo) o directorio (carpeta) dentro de la tarjeta SD; con este se interactúa directamente sobre ellos. Se crea mediante las funciones [[SD.open()]] o [[File.openNextFile()]].
 
  
Hereda de la clase [[Stream]], lo que quiere decir que prácticamente comparte la mayoría de métodos (y funcionalidad) de incluso la clase [[Serial]]. Que algunos no sean mencionados en esta página, no quiere decir que no se puedan utilizar.
+
<pre>void nombreFuncion(uint16_t* fecha, uint16_t* hora)</pre>
  
{{Nota|a partir de la versión 1.0 de la IDE de Arduino, se permite tener múltiples archivos abiertos a la vez.}}{{Nota|El flujo de entrada y salida de esta clase se categorizan como '''por bloques'''.}}{{Nota|este objeto se puede introducir directamente en un contexto booleano (ej.: como condición de un [[if... else]]); se evaluará como verdadero o '''true''' si el archivo/carpeta que representa está abierto/a en dicha instancia, falso o '''false''' en el caso contrario.}}
+
'''Quiere decir que dicha función DEBE retornar [[void]] y DEBE tener dos parámetros de tipo [[word|word o uint16_t]][[asterisco|*]]. El nombre no es importante.'''
  
=== Métodos ===
+
En dicha función, programarás la obtención de la fecha y hora actualizados; que pueden provenir de un reloj generado por software, un reloj NTP o un RTC.
{{Nota|prácticamente ningún método funcionará si la instancia es "vacía" o representa un archivo/carpeta sin abrir.}}
 
  
* [[File.read()]] - Lee un byte/caracter o varios del archivo.
+
Luego, en el [[setup()]], colocas esta línea para así otorgarle dicha definición a la librería:
* [[File.peek()]] - Lee un byte/caracter del archivo sin avanzar al siguiente.
 
* [[File.available()]] - Retorna la cantidad de bytes/caracteres disponibles para leer (antes de llegar al final del archivo).
 
* [[File.write()]] - Escribe un byte/caracter o varios al archivo. Suele usarse para archivos binarios.
 
* [[File.print()]] - Escribe una representación textual de un dato (o una cadena de caracteres) al archivo. Suele usarse para archivos de texto plano.
 
* [[File.println()]] - Igual al anterior, excepto que agrega una nueva línea de texto.
 
* [[File.flush()]] - Fuerza la actualización de los datos en la tarjeta SD.
 
* [[File.close()]] - Cierra el archivo (o carpeta) para liberar correctamente cualquier recurso que esté siendo utilizado por esta instancia.
 
* [[File.seek()]] - Posiciona el "cursor" del archivo hacia una posición dada.
 
* [[File.position()]] - Retorna la posición actual del "cursor" del archivo.
 
* [[File.size()]] - Retorna el tamaño del archivo (en bytes).
 
* [[File.name()]] - Retorna el nombre del archivo/carpeta.
 
* [[File.isDirectory()]] - Verifica si esta instancia realmente representa un directorio (carpeta) y no un archivo regular.
 
* [[File.openNextFile()]] - Abre el siguiente archivo de la lista (válido si esta instancia representa un directorio/carpeta).
 
* [[File.rewindDirectory()]] - Devuelve la iteración de archivos al comienzo (válido si esta instancia representa un directorio/carpeta).
 
  
 +
<pre>SdFile::dateTimeCallback(nombreFuncion);</pre>
  
 
== Advertencias ==
 
== Advertencias ==
 +
* Para efectos del sistema de archivos, los nombres '''NO SON''' sensibles a las mayúsculas; por lo tanto, '''datos.txt''', '''Datos.txt''' y '''DATOS.TXT''' hacen referencia al mismo archivo. Lo mismo aplica para directorios/carpetas.
 
* Atento al uso de la [[SRAM]], esta librería suele ser intensiva en ese recurso. Inicialmente consume alrededor de 700 bytes de este tipo de memoria.
 
* Atento al uso de la [[SRAM]], esta librería suele ser intensiva en ese recurso. Inicialmente consume alrededor de 700 bytes de este tipo de memoria.
 
* La tarjeta debe estar formateada en FAT16 o FAT32 con [https://www.sdcard.org/downloads/formatter_4/ SDFormatter] ('''NUNCA CON LOS UTILITARIOS DEL SISTEMA OPERATIVO'''), de lo contrario no funcionará con la librería o podría tener un bajo rendimiento.
 
* La tarjeta debe estar formateada en FAT16 o FAT32 con [https://www.sdcard.org/downloads/formatter_4/ SDFormatter] ('''NUNCA CON LOS UTILITARIOS DEL SISTEMA OPERATIVO'''), de lo contrario no funcionará con la librería o podría tener un bajo rendimiento.
 +
 +
== Ejemplo 1 ==
 +
<syntaxhighlight lang="c++">
 +
void obtenerFechaHora(uint16_t* fecha, uint16_t* hora) {
 +
  // Aquí llamas al reloj para que actualice los datos
 +
 +
  *fecha = FAT_DATE(anio, mes, dia);
 +
  *hora = FAT_TIME(hora, minuto, segundo);
 +
}
 +
 +
void setup() {
 +
  // Todas las demás inicializaciones
 +
  SdFile::dateTimeCallback(obtenerFechaHora);
 +
}
 +
 +
void loop() {
 +
  // El resto del programa
 +
}
 +
</syntaxhighlight>
 +
 +
Siendo '''anio''' una variable de tipo [[unsigned int]], mientras que el resto de componentes son de tipo [[byte]]. El año se agrega con un valor que tenga los cuatro dígitos.
 +
 +
El rango de fecha y hora válido es del '''01/01/1980 12:00:00 am''' al '''31/12/2107 11:59:59 pm'''.
 +
 +
Una vez "adjuntada", esta función se utiliza de manera similar a como ocurre en las [[attachInterrupt()|interrupciones]]: implícitamente cuando la librería la necesite.
 +
 +
La fecha/hora de creación se aplica únicamente al momento de crear un archivo/carpeta que antes no existía; la de acceso con el simple hecho de lograr abrirla; y la de modificación cuando se cierra un archivo regular que fue abierto para escritura, o cuando una subcarpeta adquiere o pierde contenido.
 +
 +
== Ejemplo 2 ==
 +
Este ejemplo muestra como conectar SD mediante [[SPI]], con MOSI, MISO, CLK, CS (11,12,13,4). CS puede variar segun si es Arduino Shield (4), Adafuit (19, Sparkfun(8) o MKRZero. Es el mismo ejemplo de '''CardInfo''', solo que los mensajes se han traducido al español.
 +
 +
<syntaxhighlight lang="c++">
 +
#include <SPI.h>
 +
#include <SD.h>
 +
 +
Sd2Card card;
 +
SdVolume volume;
 +
SdFile root;
 +
 +
const int chipSelect = 4;  //Arduino Ethernet shield, o cambiar según corresponda.
 +
 +
void setup() {
 +
  Serial.begin(9600);
 +
  Serial.print("Inicializando SD...");
 +
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
 +
      Serial.println("Error con SD:");
 +
      Serial.println("* la trajeta SD esta puesta ?");
 +
      Serial.println("* los cables estan correctos ?");
 +
      Serial.println("* pin de chipSelect es correcto segun modelo ?");
 +
      while (1);
 +
  } else {
 +
      Serial.println("Conexiones correctas y tarjeta SD en su lugar.");
 +
  }
 +
 +
  Serial.println();
 +
  Serial.print("Tipo de tarjeta:        ");
 +
  switch (card.type()) {
 +
  case SD_CARD_TYPE_SD1:
 +
      Serial.println("SD1");
 +
      break;
 +
  case SD_CARD_TYPE_SD2:
 +
      Serial.println("SD2");
 +
      break;
 +
  case SD_CARD_TYPE_SDHC:
 +
      Serial.println("SDHC");
 +
      break;
 +
  default:
 +
      Serial.println("Desconocido");
 +
  }
 +
 +
  if (!volume.init(card)) {
 +
      Serial.println("No encuentro particion FAT16/FAT32. La tarjeta esta formateada ?");
 +
      while (1);
 +
  }
 +
 +
  Serial.print("Clusteres:          ");
 +
  Serial.println(volume.clusterCount());
 +
  Serial.print("Sectores por Cluster:  ");
 +
  Serial.println(volume.blocksPerCluster());
 +
  Serial.print("Total de sectores:      ");
 +
  Serial.println(volume.blocksPerCluster() * volume.clusterCount());
 +
  Serial.println();
 +
  uint32_t volumesize;
 +
  Serial.print("El tipo de volumen es:    FAT");
 +
  Serial.println(volume.fatType(), DEC);
 +
 +
  volumesize = volume.blocksPerCluster();
 +
  volumesize *= volume.clusterCount();
 +
  volumesize /= 2;                      // Bloques (sectores) de una SD son de 512 bytes (2 bloques abarcan 1 KB o 1024 bytes)
 +
  Serial.print("Tamanio del volumen (KB):  ");
 +
  Serial.println(volumesize);
 +
  Serial.print("Tamanio del volumen (MB):  ");
 +
  volumesize /= 1024;
 +
  Serial.println(volumesize);
 +
  Serial.print("Tamanio del volumen (GB):  ");
 +
  Serial.println((float)volumesize / 1024.0);
 +
 +
  Serial.println("\r\nArchivos en SD (nombre, fecha y tamanio en bytes): ");
 +
  root.openRoot(volume);
 +
 +
  // Mostrar contenido
 +
  root.ls(LS_R | LS_DATE | LS_SIZE);
 +
}
 +
 +
void loop(void) {
 +
  // Nada
 +
}
 +
</syntaxhighlight>
 +
 +
== Ejemplo 3 ==
 +
Data logger.
 +
 +
<syntaxhighlight lang="c++">
 +
#include <SPI.h>
 +
#include <SD.h>
 +
 +
const int chipSelect = 4;
 +
 +
void setup() {
 +
  Serial.begin(9600);
 +
  Serial.print("Inicializando SD...");
 +
  if (!SD.begin(chipSelect)) {
 +
      Serial.println("SD fallo, esta puesta ?");
 +
      while (1);
 +
  }
 +
  Serial.println("SD lista.");
 +
}
 +
 +
void loop() {
 +
  String data = "";
 +
  for (byte analogPin=0; analogPin<3; analogPin++) {
 +
      int sensor = analogRead(analogPin);
 +
      data += String(sensor);
 +
      if (analogPin < 2) {
 +
        data += ",";
 +
      }
 +
  }
 +
  File File = SD.open("datalog.txt", FILE_WRITE);
 +
  if (File) {
 +
      File.println(data);
 +
      File.close();
 +
      Serial.println(data);
 +
  }else{
 +
      Serial.println("Error abriendo datalog.txt");
 +
  }
 +
}
 +
</syntaxhighlight>
 +
 +
== Ejemplo 4 ==
 +
<syntaxhighlight lang="c++">
 +
</syntaxhighlight>
  
 
== Vea también ==
 
== Vea también ==
* [[SPI]]
+
<categorytree mode=all>Librerias</categorytree>
* [[Stream]]
 
  
 
== Referencias externas ==
 
== Referencias externas ==
* [https://www.arduino.cc/en/Reference/SD Documentación oficial de la librería (en inglés)]
+
* [https://www.arduinolibraries.info/libraries/sd SD] - All Libraries
 +
* [http://www.arduino.cc/en/Reference/SD SD]
 
* [https://en.wikipedia.org/wiki/8.3_filename Artículo de Wikipedia sobre los nombres SFN (en inglés)]
 
* [https://en.wikipedia.org/wiki/8.3_filename Artículo de Wikipedia sobre los nombres SFN (en inglés)]
 +
* [https://www.luisllamas.es/tarjeta-micro-sd-arduino/ Leer y escribir en tarjeta SD] - Luis Llamas
 +
* [https://naylampmechatronics.com/blog/38_Tutorial-Arduino-y-memoria-SD-y-micro-SD-.html Memorias SD y microSD] - Naylamp
 +
* [https://aprendiendoarduino.wordpress.com/category/sd/ SD] - Enrique Crespo
 +
* [http://manueldelgadocrespo.blogspot.com/p/biblioteca-sd.html Biblioteca SD] - Manuel Delgado
 +
* [https://www.prometec.net/sdcard/ SDcard] - Prometec
 +
* [https://eldesvandejose.com/2016/05/12/la-libreria-sd/ Libreria SD] - El desvan de José
  
 
[[Category:Librerias]]
 
[[Category:Librerias]]
 +
[[Category:Libreria SD]]

Revisión actual del 17:15 16 mar 2020

Descripción

La librería SD.h de SparkFun habilita un Arduino para la interacción con tarjetas SD (Secure Digital) mediante el protocolo Serial Peripheral Interface (SPI). A pesar de que existen tres tamaños distintos, todos operan exactamente de la misma manera (a determinada capacidad); por lo tanto, esta librería funciona con todos los tamaños.

Debido a que usa SPI, la conexión debe realizarse hacia los respectivos pines del Arduino; salvo SS/CS, este puede conectarse en cualquier pin digital libre.

Nota: La librería soporta tarjetas SD del estándar SC y HC; por esta razón, la capacidad máxima soportada es de 32 GB.


Tip: Librería oficial, es decir que está preinstalada junto con la IDE de Arduino, no es necesario descargarla.


Nota: debido a limitaciones de la librería y sistema de archivos compatibles, el tamaño máximo por archivo es de 4 GB o 4294967295 bytes.


Una mejor opción sera la libreria SdFat de Bill Greiman que soporta FAT16 y FAT32.

La clase SD

Es un objeto preinstanciado que se utiliza para iniciar la tarjeta y realizar operaciones generales sobre el sistema de archivos (FAT en sus versiones de 16 y 32 bits; exFAT no es compatible).

Sintaxis

#include <SPI.h>
#include <SD.h>
SD.begin(CS);

Parametros

CS
Puerto 4 si es Aduino Shield.

Metodos

Metodos de la clase SD
Método Descripción
SD.begin() Intenta inicializar la tarjeta SD al modo SPI, para así obtener de esta la información necesaria para empezar a usarla.
SD.end() Devuelve la librería al estado inicial; útil para remover, "en caliente" y de forma segura, la tarjeta.
SD.open() Abre un archivo o una carpeta.
SD.exists() Verifica si determinado archivo o carpeta realmente existe en la tarjeta.
SD.remove() Elimina un archivo.
SD.mkdir() Crea un directorio (carpeta).
SD.rmdir() Elimina un directorio (carpeta).

Nota: prácticamente ningún otro método funcionará si no se inicializa de antemano la tarjeta SD.


La clase File

Es un objeto que representa la instancia de un fichero (archivo) o directorio (carpeta) dentro de la tarjeta SD; con este se interactúa directamente sobre ellos. Se crea mediante las funciones SD.open() o File.openNextFile().

Sintaxis

File variable1 = SD.open(nombre, modo);
File variable2 = variable1.openNextFile();

Parámetros

variable
Nombre de variable a instanciar.
nombre
Nombre de archivo SFN (8.3). Ejemplo log12345.txt.
modo
Puede ser FILE_WRITE o FILE_READ

Metodos

Metrodos de la clase File
Método Descripción
File.read() Lee un byte/caracter o varios del archivo.
File.peek() Lee un byte/caracter del archivo sin avanzar al siguiente.
File.available() Retorna la cantidad de bytes/caracteres disponibles para leer (antes de llegar al final del archivo).
File.write() Escribe un byte/caracter o varios al archivo. Suele usarse para archivos binarios.
File.print() Escribe una representación textual de un dato (o una cadena de caracteres) al archivo. Suele usarse para archivos de texto plano.
File.println() Igual al anterior, excepto que agrega una nueva línea de texto.
File.flush() Fuerza la actualización de los datos en la tarjeta SD.
File.seek() Posiciona el "cursor" del archivo hacia una posición dada.
File.position() Retorna la posición actual del "cursor" del archivo.
File.size() Retorna el tamaño del archivo (en bytes).
File.name() Retorna el nombre del archivo/carpeta.
File.isDirectory() Verifica si esta instancia realmente representa un directorio (carpeta) y no un archivo regular.
File.openNextFile() Abre el siguiente archivo de la lista (válido si esta instancia representa un directorio/carpeta).
File.rewindDirectory() Devuelve la iteración de archivos al comienzo (válido si esta instancia representa un directorio/carpeta).
File.close() Cierra el archivo (o carpeta) para liberar correctamente cualquier recurso que esté siendo utilizado por esta instancia.

Nota: Prácticamente ningún método funcionará si la instancia es "vacía" o representa un archivo/carpeta sin abrir.


Formato de la ruta de acceso

Esta librería utiliza el formato de rutas de UNIX/Linux: el nombre de las carpetas involucradas se separa con / (barra inclinada o slash), siendo el destino final el último nombre de la secuencia. Por ejemplo:

datos.txt
ruta/hacia/el/archivo/que/buscamos.txt

Esto hará que el archivo datos.txt sea creado o buscado en el directorio raíz; en otras palabras, fuera de toda carpeta.

Nota: Para efectos del sistema de archivos, los nombres NO SON sensibles a las mayúsculas; por lo tanto, datos.txt, Datos.txt y DATOS.TXT hacen referencia al mismo archivo. Lo mismo aplica para directorios/carpetas.


Siendo ruta, hacia, el, archivo y que las carpetas que nos llevan al archivo buscamos.txt

Ese destino no tiene que ser solamente un archivo regular, también podría ser otra carpeta.

Si es algo que queremos colocar o ubicar en la "raíz", basta con simplemente colocar el nombre del elemento. Por ejemplo:

Nota: ya que el directorio de trabajo de la librería siempre es la raíz, da igual que la ruta sea absoluta (con / al comienzo) o relativa (sin / al comienzo).


Nombres SFN (8.3)

Parte de la escritura de rutas también involucra respetar el formato SFN (Short File Name o nombre corto de archivo), el cuál sigue estas reglas:

  • El nombre del elemento debe tener como mínimo 1 carácter, y como máximo 8 caracteres.
  • Si es un archivo regular, debe haber un único punto (.) justo después del nombre.
  • Si es un archivo regular, deben haber de 1 a 3 caracteres después del antes mencionado punto. Esto es lo que se conoce como la "extensión", una forma burda (pero muy usada en Windows) de etiquetar archivos por su contenido (ej.: txt para texto plano, jpg para imágenes codificadas en JPEG, mp3 para audio codificado en MPEG capa 3, etc.).

Nota: deduciendo de las reglas anteriores, los directorios/carpetas solo deben cumplir la primera; no debe haber punto (.) ni extensión en el nombre.


Caracteres permitidos

En el nombre puede venir cualquier carácter, excepto los siguientes:

  • " * + , / : ; < = > ? \ [ ] |
  • Los que tienen un valor decimal del 0 al 31
  • El carácter de valor decimal 127
  • El punto (.) sólo si no es antecedido por al menos un caracter permitido.

Técnicamente se permite el carácter espaciador y los del ASCII extendido (valor decimal del 128 al 255), sin embargo el primero no se recomienda porque puede hacer más complicado programar la generación de rutas; y lo segundo tampoco ya que la codificación no es estándar entre regiones geográficas (lo que puede llevar a una interpretación errónea del nombre en cuestión), eso sin contar que si el primer carácter es de valor 0xE5 (229 decimal), la librería podría después mal interpretarlo como un archivo eliminado (que no existe).

Curiosamente, las letras minúsculas están prohibidas; sin embargo esto no es cierto al crear e ingresar la ruta, ya que la librería automáticamente usa, en su lugar, las respectivas contrapartes mayúsculas al procesarla.

Comentarios

Hereda de la clase Stream, lo que quiere decir que prácticamente comparte la mayoría de métodos (y funcionalidad) de incluso la clase Serial. Que algunos no sean mencionados en esta página, no quiere decir que no se puedan usar.

Nota: a partir de la versión 1.0 del IDE de Arduino, se permite tener múltiples archivos abiertos a la vez.


Nota: El flujo de entrada y salida de esta clase se categorizan como por bloques.


Nota: Este objeto se puede introducir directamente en un contexto booleano (ej.: como condición de un if... else); se evaluará como verdadero o true si el archivo/carpeta que representa está abierto/a en dicha instancia, falso o false en el caso contrario.


Cómo agregar fecha y hora a los archivos

Todo archivo/fichero y directorio/carpeta tiene por atributos, además del nombre, la fecha y hora de creación, último acceso y última modificación (datos que se pueden mirar en las "propiedades" o "detalles" del elemento, en el explorador de archivos de tu PC). La librería por defecto no tiene definido cómo obtener dicha información; pero cuando es requerido, por defecto coloca el 01/01/2000 1:00:00 am aunque haya transcurrido un tiempo desde que el sistema ha arrancado.

Para definir la obtención de esos datos, primero debes crear una función con la siguiente "firma" (declaración):

void nombreFuncion(uint16_t* fecha, uint16_t* hora)

Quiere decir que dicha función DEBE retornar void y DEBE tener dos parámetros de tipo word o uint16_t*. El nombre no es importante.

En dicha función, programarás la obtención de la fecha y hora actualizados; que pueden provenir de un reloj generado por software, un reloj NTP o un RTC.

Luego, en el setup(), colocas esta línea para así otorgarle dicha definición a la librería:

SdFile::dateTimeCallback(nombreFuncion);

Advertencias

  • Para efectos del sistema de archivos, los nombres NO SON sensibles a las mayúsculas; por lo tanto, datos.txt, Datos.txt y DATOS.TXT hacen referencia al mismo archivo. Lo mismo aplica para directorios/carpetas.
  • Atento al uso de la SRAM, esta librería suele ser intensiva en ese recurso. Inicialmente consume alrededor de 700 bytes de este tipo de memoria.
  • La tarjeta debe estar formateada en FAT16 o FAT32 con SDFormatter (NUNCA CON LOS UTILITARIOS DEL SISTEMA OPERATIVO), de lo contrario no funcionará con la librería o podría tener un bajo rendimiento.

Ejemplo 1

void obtenerFechaHora(uint16_t* fecha, uint16_t* hora) {
  // Aquí llamas al reloj para que actualice los datos

  *fecha = FAT_DATE(anio, mes, dia);
  *hora = FAT_TIME(hora, minuto, segundo);
}

void setup() {
  // Todas las demás inicializaciones
  SdFile::dateTimeCallback(obtenerFechaHora);
}

void loop() {
  // El resto del programa
}

Siendo anio una variable de tipo unsigned int, mientras que el resto de componentes son de tipo byte. El año se agrega con un valor que tenga los cuatro dígitos.

El rango de fecha y hora válido es del 01/01/1980 12:00:00 am al 31/12/2107 11:59:59 pm.

Una vez "adjuntada", esta función se utiliza de manera similar a como ocurre en las interrupciones: implícitamente cuando la librería la necesite.

La fecha/hora de creación se aplica únicamente al momento de crear un archivo/carpeta que antes no existía; la de acceso con el simple hecho de lograr abrirla; y la de modificación cuando se cierra un archivo regular que fue abierto para escritura, o cuando una subcarpeta adquiere o pierde contenido.

Ejemplo 2

Este ejemplo muestra como conectar SD mediante SPI, con MOSI, MISO, CLK, CS (11,12,13,4). CS puede variar segun si es Arduino Shield (4), Adafuit (19, Sparkfun(8) o MKRZero. Es el mismo ejemplo de CardInfo, solo que los mensajes se han traducido al español.

#include <SPI.h>
#include <SD.h>

Sd2Card card;
SdVolume volume;
SdFile root;

const int chipSelect = 4;   //Arduino Ethernet shield, o cambiar según corresponda.

void setup() {
   Serial.begin(9600);
   Serial.print("Inicializando SD...");
   if (!card.init(SPI_HALF_SPEED, chipSelect)) {
      Serial.println("Error con SD:");
      Serial.println("* la trajeta SD esta puesta ?");
      Serial.println("* los cables estan correctos ?");
      Serial.println("* pin de chipSelect es correcto segun modelo ?");
      while (1);
   } else {
      Serial.println("Conexiones correctas y tarjeta SD en su lugar.");
   }

   Serial.println();
   Serial.print("Tipo de tarjeta:         ");
   switch (card.type()) {
   case SD_CARD_TYPE_SD1:
      Serial.println("SD1");
      break;
   case SD_CARD_TYPE_SD2:
      Serial.println("SD2");
      break;
   case SD_CARD_TYPE_SDHC:
      Serial.println("SDHC");
      break;
   default:
      Serial.println("Desconocido");
   }

   if (!volume.init(card)) {
      Serial.println("No encuentro particion FAT16/FAT32. La tarjeta esta formateada ?");
      while (1);
   }

   Serial.print("Clusteres:          ");
   Serial.println(volume.clusterCount());
   Serial.print("Sectores por Cluster:  ");
   Serial.println(volume.blocksPerCluster());
   Serial.print("Total de sectores:      ");
   Serial.println(volume.blocksPerCluster() * volume.clusterCount());
   Serial.println();
   uint32_t volumesize;
   Serial.print("El tipo de volumen es:    FAT");
   Serial.println(volume.fatType(), DEC);

   volumesize = volume.blocksPerCluster();
   volumesize *= volume.clusterCount();
   volumesize /= 2;                       // Bloques (sectores) de una SD son de 512 bytes (2 bloques abarcan 1 KB o 1024 bytes)
   Serial.print("Tamanio del volumen (KB):  ");
   Serial.println(volumesize);
   Serial.print("Tamanio del volumen (MB):  ");
   volumesize /= 1024;
   Serial.println(volumesize);
   Serial.print("Tamanio del volumen (GB):  ");
   Serial.println((float)volumesize / 1024.0);

   Serial.println("\r\nArchivos en SD (nombre, fecha y tamanio en bytes): ");
   root.openRoot(volume);

   // Mostrar contenido
   root.ls(LS_R | LS_DATE | LS_SIZE);
}

void loop(void) {
   // Nada
}

Ejemplo 3

Data logger.

#include <SPI.h>
#include <SD.h>

const int chipSelect = 4;

void setup() {
   Serial.begin(9600);
   Serial.print("Inicializando SD...");
   if (!SD.begin(chipSelect)) {
      Serial.println("SD fallo, esta puesta ?");
      while (1);
   }
   Serial.println("SD lista.");
}

void loop() {
   String data = "";
   for (byte analogPin=0; analogPin<3; analogPin++) {
      int sensor = analogRead(analogPin);
      data += String(sensor);
      if (analogPin < 2) {
         data += ",";
      }
   }
   File File = SD.open("datalog.txt", FILE_WRITE);
   if (File) {
      File.println(data);
      File.close();
      Serial.println(data);
   }else{
      Serial.println("Error abriendo datalog.txt");
   }
}

Ejemplo 4

Vea también


Referencias externas