sábado, 5 de enero de 2019

La Tarjeta SD

En esta entrada vamos a utilizar un componente importante para nuestros proyectos. Hasta ahora hemos hecho uso de la EEPROM para guardar o recuperar datos, aunque ésta, está muy limitada.
Con una tarjeta SD estas limitaciones pasarán a la historia. Para manejar una tarjeta SD nos conviene hacer uso de una librería adaptada para la ocasión y además debo añadir que posee métodos muy bien diseñados, que harán de su manejo algo sencillo y potente.
#include <SD.h> con esta instrucción cargamos la librería mencionada, que incorpora varios ejemplos de código.
Para el manejo de archivos y directorios será necesario hacer uso de funciones o métodos específicos que esta librería también soporta.
Tras incluir la librería SD.h debemos declarar un objeto de tipo FILE como sigue:
File dataFile
En la función setup() debemos inicializar y comprobar la conexión con la tarjeta con una instrucción mas o menos así:
if (!SD.begin(4)){
Serial.println( "SD no conectada");
}else{ Serial.println( "SD en Conexion "); }

Ya en el cuerpo principal del programa, para poder escribir un archivo antes hay que abrirlo asi:
  dataFile=SD.open("miArchivo", FILE_WRITE);

para escribir usamos la siguiente instrucción:
  dataFile.println("misDatos");

y siempre, siempre tras una escritura hay que cerrar el archivo con la instrucción:
  dataFile.close();

A continuación vamos a ver algunas de las funciones de la librería SD y de la librería File para entender mejor lo que hemos hecho hasta ahora.

Funciones de librería SD para el manejo de Archivos y directorios:
 
SD.begin(pinCS); // inicia tarjeta SD, "pin CS" es el nº de pin de la Arduino donde conectamos CS
 SD.exists(nombreArchivo); //si existe el fichero devuelve true o false en caso contrario
 SD.remove(nombreArchivo); // borra un archivo
 SD.open(nombreArchivo, modo); // abre un archivo, existen dos modos
  // modo: FILE_WRITE abre un archivo para escritura y lectura
  // modo: FILE_READ abre un archivo para sólo lectura
 SD.mkdir(directorio); // crea un directorio
 SD.rmdir(nombreDirectorio); // elimina el directorio indicado

Funciones incorporadas del objeto FILE:

 dataFile.size() // Obtiene el tamaño del archivo en Bytes
 dataFile.available() // Comprueba si quedan bytes por leer
 dataFile.read() // Lee un byte del archivo
 dataFile.write(dato) // Escribe un byte en el archivo
 dataFile.print(dato) // Escribe una variable en un fichero; por ejemplo una string
 dataFile.position() // Obtiene el punto de lectura/escritura actual
 dataFile.seek(pos) // Mueve el punto de lectura/escritura actual a la "pos" indicada
 dataFile.close() // Cierra el fichero

Cabe señalar que dataFile puede ser cualquier otro nombre que deseemos. Este nombre se lo hemos dado al declarar el objeto FILE, recordar que si indicamos otro nombre en la declaración también tendremos que cambiarlo en las funciones que vayamos a emplear.

A continuación vamos a ver como se conecta la SD a nuestra Arduino:



La conexión de los pines SCK(13), MISO(12) y MOSI(11) deben respetarse tal como se muestran en la imagen, mientras que el pin CS de la tarjeta puede ser conectado en otro pin de la Arduino(aqui el 4), eso si al iniciar la SD con la intrucción SD.begin(pinCS) el parámetro pinCS debe coincidir con el pin que hayamos elegido.

Una vez realizadas las conexiones y vistas las instrucciones básicas para su manejo ya estamos en disposición de realizar un programilla para comprobar su manejo.
Dados los ejemplos expuestos en este blog vamos crear un programa que guarde una serie de datos en la EEPROM (ver aquí Introducción a la EEPROM) y de ahí los traspasaremos a nuestra microSD.

En concreto guardaremos en cada posición de la EEPROM su valor equivalente, es decir en la posición cero guardaremos el valor 0, en la posición uno guardaremos el valor 1 y así sucesivamente. Sucederá que como tenemos 1024 posiciones y en cada posición solo podemos guardar un valor no superior a un byte (255) este valor se verá desbordado y comenzará de cero. Para ello crearemos una variable de control que almacenará el numero de desbordamientos, al traspasar los datos almacenados en la EEPROM hacia la microSD tendremos en cuenta los desbordamientos para poder escribir en la microSD el valor que nos interesa.

En fin, aquí os dejo un Sketck de ejemplo para practicar un poco:

#include <SD.h> // Incluye la libreria para la SD

//#include <SPI.h> // incluye la libreria para el protocolo de comunicacion SPI usado por la SD. ATT. ESTA COMENTADO

#include <EEPROM.h> // incluye la libreria para el manejo de la EEPROM

File dataFile;// creamos un objeto de tipo FILE

byte desbord; // variable para el control del desbordamiento

void setup() {

  Serial.begin(9600);

  if (!SD.begin(4)){ // inicia la tarjeta SD, el pin CS conectado al pin 4 de la Arduino

    Serial.println( "SD no conectada"); // si NO detecta la tarjeta imprime por consola el mensaje

  }else{ Serial.println( "SD OK");} // si la detecta la tarjeta imprime por consola el mensaje

  if (SD.exists("myFile")){ // comprueba que el archivo llamado "myFile" se encuentra en la microSD

     Serial.println( "myFile Existe"); // si existe imprime el mensaje

      dataFile=SD.open("myFile", FILE_WRITE);// Abrimos el archivo "myFile" en modo Lectura-Escritura

        Serial.println("Escribiendo en SD"); // imprime el mensaje 

        dataFile.println("PosicionEEPROM  Valor Guardado  Valor Calculado");// imprime en el archivo la cabecera

        dataFile.println("--------------  --------------  ---------------");// imprime en el archivo el subrayado de la cabecera

        for (int i=0;i<1024;i++){ // creamos un bucle de 1023 iteraciones, una por cada posicion de la EEPROM

          EEPROM.write(i,i); // escribe en la posicion de la iteracion el mismo valor

          if (i <=255){desbord=0;} // determina el  

          if (i >255){desbord=1;} // valor de la 

          if (i >511){desbord=2;} // variable "desbord" en cada rango de bytes

          if (i >767){desbord=3;}// para cada rango de bytes

          // Los siguientes condiconales imprimin en el archivo "myFile" los valores de la iteracion  

          if (desbord==0){dataFile.print(i);dataFile.print("\t\t");dataFile.print(EEPROM.read(i));dataFile.print("\t\t");dataFile.println(EEPROM.read(i));}

          if (desbord==1){dataFile.print(i);dataFile.print("\t\t");dataFile.print(EEPROM.read(i));dataFile.print("\t\t");dataFile.println(EEPROM.read(i)+256);}

          if (desbord==2){dataFile.print(i);dataFile.print("\t\t");dataFile.print(EEPROM.read(i));dataFile.print("\t\t");dataFile.println(EEPROM.read(i)+512);}

          if (desbord==3){dataFile.print(i);dataFile.print("\t\t");dataFile.print(EEPROM.read(i));dataFile.print("\t\t");dataFile.println(EEPROM.read(i)+768);}

        }

        dataFile.println("Fin del Archivo");//imprime en el archivo el mensaje de final de archivo

        if (dataFile.size()>0){Serial.println("Escritura Finalizada con exito");}// si el archivo tiene mas de cero bytes es que la escritura a sido exitosa

        Serial.print("myFile tiene un volumen de ");Serial.print(dataFile.size());Serial.println(" bytes");// imprime el volumen del nuevo archivo

        dataFile.close(); // cierra el archivo

  }else{Serial.println("El Archivo no Existe");}// si el archivo no se halla en la tarjeta imprime el mensaje, saltandose todo el codigo anterior

}

void loop() {}


El programa esta escrito en la función setup() de manera que solo se ejecutará una vez, si pulsamos el botón reset de la Arduino al finalizar la ejecución, descubriremos una grata sorpresa, y es que la función FILE no sobre escribe el archivo si no que sitúa el puntero al final del archivo (si hubiera algo) y partir de ahí comienza la nueva escritura.


Es importante conocer bien todas sus funciones por lo que insto al lector a indagar sobre ellas, 
A continuación os indico algunos links interesantes:

He comprobado que en algunas compilaciones el nombre del archivo esta limitado a ocho caracteres, si indicamos un valor superior el archivo ni se creará, ni obtendremos un mensaje de error, así que no sabremos lo que está sucediendo.
Sin embargo no he encontrado ninguna información al respecto!!!

Además, en todos los tutoriales que he visto por ahí, te hacen incluir la librería SPI (la tarjeta emplea este protocolo) sin embargo con la compilación 1.8.7 (la que uso en estos momentos) no necesito incluirla!!!. En cualquier caso yo la incluyo en el Sketch que os dejo de ejemplo aunque esta comentada.

y por último os indico que en sitios web como AliExpress o Banggood la podemos encontrar por un euro.