Gestión e indexación de registros de una tabla en SD.

Hola, surbyte. De momento estoy con el esqueleto. Se trata de una librería con plantilla, ya que es la única forma que he encontrado para poder recibir de los sketch las dos piedras de toque en las que me voy a basar. Para ir abriendo boca: Básicamente, para crear un objeto, se deben definir una estructura de datos (con los datos que van a componer un registro) y una función que pueda comparar dos estructuras de ese tipo y devuelva si la primera es mayor, menor o igual que la segunda. La librería tomará esa estructura y esa función para realizar las búsquedas y ordenaciones. La tabla contendrá un header, un índice (según la función que le vamos a enviar) y los datos en sí. Espero poder poner en breve las primeras pinceladas, para ver qué se puede mejorar o agregar.

Menuda lata con la plantilla. Me estaba volviendo loco, porque me daba errores el compilador de cosas no encontradas. Finalmente, tras mucho buscar y devanarme los sesos di con el problema:

SI ALGUNA VEZ TRATÁIS CON PLANTILLAS NO IMPLEMENTÉIS SUS MÉTODOS EN UN ARCHIVO .CPP.

Se puede ubicar todo el código en el propio archivo.h o se puede realizar un #include desde el propio archivo de cabecera (osea al revés) de otro archivo con extensión distinta a cpp. Es la forma por la que opté finalmente.

Bueno; pasada la primera en la frente, pongo el código del esqueleto en el primer post (no esperéis que funcione ni de broma :slight_smile: ), y así comento mis primeros dilemas.

A TORTAS CON LA ESTRUCTURA

La verdad es que para la mayoría de las aplicaciones arduino que se suele utilizar un archivo de datos, bien no sería necesario utilizar un archivo indexado (si el número de registros no es elevado) o bien el propio archivo sería un índice en sí. Recuerdo, por ejemplo, un proyecto en el que había que cotejar un número de teléfono (remitente de SMS) para comprobar si estaba en la tabla. Tres opciones a priori:

  • Archivo de texto con los teléfonos desordenados. Cotejar secuencialmente hasta encontrar o final de archivo. ¿Con cuántos registros empieza a ser pesado? (No lo sé, ya que no lo he comprobado. Era una pregunta retórica :P ).
  • Archivo binario con los teléfonos desordenados. Cada teléfono se podría codificar como un unsigned int, con lo que en lugar de 9-10 bytes por teléfono usaríamos 4 (mitad de espacio, doble eficiencia)
  • Archivo binario ordenado. Necesitaremos más tiempo cada vez que debamos agregar un registro, pero la búsqueda de un registro existente se puede realizar con cierta velocidad, dividiendo la tabla sucesivamente en mitades.

Hasta aquí nos podríamos desenvolver para las situaciones más habituales, así que no sé si realmente tiene mucho sentido lo que he comenzado a desarrollar. No obstante, como ya metí las dos piernas en el barro, intentaremos llevarlo hasta el final.

Supongamos que queremos guardar una serie de datos con un campo clave, por ejemplo DOCUMENTO (campo clave), NOMBRE, TELÉFONO, DIRECCIÓN. Evidentemente, podríamos guardarlos ordenadamente según su número de documento en la tabla pero, suponiendo que la longitud de registro sea de 100 bytes y tengamos almacenados 1000 registros y queremos insertar uno que (oh, cielos) le corresponde el principio de la tabla, tendremos que mover 100 k de datos para hacerle hueco al nuevo registro. ¿Alguien podría calcular o comprobar directamente (en estos momentos no tengo a mano mi arduino) con qué velocidad de escritura contamos? (Según mi cálculo con los dedos medio mega por segundo) Lo digo por tomar esta vía o complicarlo un poco más (crear un índice separado de los datos.

Bien lo que he visto Serafín que el tema de la base de datos se esta volviendo una pregunta muy frecuente asi que este post luego tendra que convertirse en un tutorial para estos casos. Un FAQ.

NO se si te entendí bien, pero aca hay un post que discute velocidades de escritura en SD. van de 25kb/seg como muy bueno.
Yo lei en otro post 3kb/seg
A 25k eso implica 4 segundos para reescribir los 100k
A digamos 4k para hacer fáciles las cuentas son 25 seg para mover misma cantidad de datos.

Bien; entonces creo que merecerá la pena el jaleo que estoy montando, de integrar en el archivo un índice y datos separados doblemente enlazados. ¿Sabéis qué creo qué me vendría muy bien, y a buen seguro que a muchos también? Un tutorial, o más bien una discusión con un post inicial dinámico con las cosas que saquemos en común sobre recomendaciones de estilo, porque cada vez que retomo un programa me doy cuenta de que cambio totalmente la "ortografía y gramática de la programación" y me quedan ilegibles. Paso de crear , por ejemplo variables en estilo MiVariable a miVariable, mi_variable, ulMiVariable... y al final acabo mezclando todo, con lo que me quedan unas chapuzas visuales... A buen seguro que a esta librería va a necesitar de una buena capa de maquillaje cuando la acabe para poder ser legible. Bueno. Voy a seguir con ella. Creo que voy estando cerca de finalizar (seguro que con muchos fallos) la primera versión. Cuando la suba intentaré explicar cómo la estoy intentando desarrollar, a ver qué ideas podemos recabar para mejorarla. Saludos.

Hola de nuevo. Llevo un par de días pegándome con el código y sin hacer una sola prueba con mi arduino, y ahora que parece que tengo algo con lo que empezar a hacer pruebas, no tengo el arduino a mano hasta el miércoles. La verdad es que tampoco me llevo muy bien con los módulos SD (no tengo shields) pues hasta ahora, no sé si por cableado, alimentación o qué, no consigo trabajar con la tarjeta con la librería SD, a no ser que baje la velocidad SPI. Actualizo el proyecto al estado actual. He cambiado algunos conceptos de enfoque y nombres de métodos para intentar hacerlos un poco intuitivos. Para un primer vistazo a la librería, lo mejor es echar un vistazo al archivo FDB.h, y sobre todo a los métodos públicos de la clase (a ver si los entendéis, y si creéis que habría que cambiar algo).

template 
class FDB {
public:
     // Constructor. Hay que pasarle una función que compare dos estructuras del tipo que vamos a registrar en BBDD
     // y devuelva si el índice del primero es mayor, menor o igual que el segundo.
     FDB( CompResult (*fcomparadora) (const ESTRUCTURA&, const ESTRUCTURA&) ):  _Compara(fcomparadora) { };

     // Devuelve número de registros en el índice (no borrados)
     IdReg count() {return(_head.filledRecords);};

     // Apertura de base de datos. 
     Status open(char *nombreArchivo);

     // Si su index ya está en base, devuelve error; si no, lo da de alta.
     Status NuevoRegistro(const ESTRUCTURA&);

     // Lee el registro actual, y lo carga en la estructura registro espacio
     Status LeeRegistro(ESTRUCTURA &espacio); 
     
     // Actualiza el registro actual con los datos dados. Si cambian datos de index, no debe permitir un índice existente.
     Status ActualizaRegistro(const ESTRUCTURA &espacio); 

     // Elimina el registro actual
     Status BorraRegistro();

     // Busca el índice en una determinada posición. Si pedimos una posición fuera de rango ubica en EOF y devuelve ERR.
     Status IrRegistro(const IdReg &);
     
     // Busca el índice dado en una estructura y lo guarda en un IdReg dado. Si se localiza, la coincidencia devuelve ok. 
     // Si no, guarda posición del primer registro mayor (posición de inserción) y devuelve ERR.
     Status EncuentraIndex(const ESTRUCTURA &, IdReg &);

Pienso que tal vez estoy abusando de parámetros por referencia. Claramente los datos de registro sí deben pasarse por referencia o puntero, pero los de índice (unsigned long) tal vez sea más práctico pasarlos por valor. No obstante, de momento me interesa saber si hace algo, aunque seguro que habrá errores. Si alguno probáis cosas (el archivo .ino es muy básico, pero si por casualidad funciona, se puede inentar probar modificaciones o borrados) me contáis. Saludos.

He modificado un poco el método NuevoRegistro, para que si existe previamente un index de registro eliminado, aproveche tanto el propio index como el espacio que ocupaba el viejo registro. Básicamente

  // Si existen registros borrados, aprovecharemos la posición de datos que apuntaba el índice anterior.
  if ( _head.deletedRecords > 0 ) {
    _LeerIndex(_registroActual, posDataInsertar);
    _head.deletedRecords--;
  } else {
    // Si no aprovechamos un borrado, los datos irán al final del archivo
    posDataInsertar = _dbFile.size();
    _EscribirIndex(_registroActual, posDataInsertar);
  }

Subo las modificaciones al primer post.

Sigo dirimiendo si utilizar paso y retorno de valores por valor, en lugar de mediante referencias, para los IdReg y FilePointer (ambos son tipo unsigned long). Tal vez resulte más intuitivo y menos propenso a posibles errores (se podrían pasar valores inmediatos).

Noter: voy a hacer una simulación en Proteus 8.1 con un Arduino UNO y un Shield o una aproximación que permita leer un microSD.

Este es el mejor ejemplo que veo por ahora Esquema SparkFun microSD Shield.

/** SD card datalogger
** SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4
*/

Lo hice mas simple y esta como dice la lista de arriba.

SD.zip (17.3 KB)

Hola. Llevo un par de días intentando hacer funcionar un programa básico de SD en mi arduino (ReadWrite, y no hay forma. No sé si las dos lectoras están mal, o puede ser problema del IDE o la librería SD (estoy con el IDE 1.5.8 y la propia librería que ya trae el IDE). El caso es que consigo detectar SD y volumen (bajando a QUARTER_SPEED el SPI), pero no me lee ni escribe ningún archivo. Tal vez tenga que modificar la librería (igual en algún punto me vuelve a establecer la velocidad a HALF_SPEED). ¿Alguien podría decirme si para un slot de estos estilos

conectado al arduino por cables de unos 10cm ¿sería necesario algún condensador o similar para poder trabajar a velocidad normal (SPI_HALF_SPEED) con la SD?

Por otro lado, surbyte, ¿has conseguido simular la jodida dichosa SD en proteus? Tampoco he sido capaz de hacer funcionar ni un ejemplo. No sé si probar a retroceder de IDE, anque me da pereza.

Si claro que si. Funciona perfecto. Enviame un email y lo conversamos.

Noter dame un código para probar con el simulador.

Bueno. Al menos ya puedo probar sobre la placa real aunque, como dije, he tenido que modificar mi librería para que en lugar de SPI_HALF_SPEED trabaje en SPI_QUARTER_SPEED. Así que he comenzado a depurar (de momento el código no rula). A ver si estos días consigo avanzar un poco, aunque el trancazo que he pillado no permite que mi mente funcione con mucha lucidez. Lo que no he sido capaz de hacer funcionar ha sido la SD en proteus. Mismamente el ejemplo ReadWrite del IDE, asignando un fichero de imagen a la tarjeta (un txt vacío) y las conexiones creo que bien realizadas (si modifico alguna, no hay comunicación ni errores SPI) lo único que consigo es un montón de errores "mmc command unsupported". ¿A tí te funciona correctamente, surbyte? ¿Alguien sabe a qué puede ser debido ese tipo de error?

Como te dije antes, dame algo para probarlo asi te respondo seriamente.

Pues me conformaría con hacer funcionar este código de ejemplo:

/*
  SD card read/write

 This example shows how to read and write data to and from an SD card file
 The circuit:
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4

 created   Nov 2010
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe

 This example code is in the public domain.

 */

#include 
#include 

File myFile;

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  Serial.print("Initializing SD card...");
  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin
  // (10 on most Arduino boards, 53 on the Mega) must be left as an output
  // or the SD library functions will not work.
  pinMode(10, OUTPUT);

  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  myFile = SD.open("test.txt", FILE_WRITE);

  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");
    // close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }

  // re-open the file for reading:
  myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");

    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}

void loop()
{
  // nothing happens after setup
}

Porque de momento no he conseguido que la MMC me devuelva nada salvo errores. De momento sólo puedo probar con "fuego real" (encima a medio gas, que es otra) y los avances son un poco penosos, pues cada modificación requiere volver a compilar y subir código a Arduino, pero pasito a pasito voy avanzando. Espero en breve actualizar el post #1 con una versión que ya se pueda probar (de momento no hace nada útil).

Bien, somos dos, tampoco puedo pasar de initialization failed!

Bueno no hay caso. No funciona en el simulador. Intentando poner en práctica sugerencias de este link

Posible Solución1: El archivo que tiene la tarjeta sd debe tener extención .mmc Asi que editar .txt por .mmc.

No me funcionó.

Posible 2:

Con respecto lo que dice nuestro amigo yo hace tiempo use un truco, el proteus para la mmc necesita el fichero disk.bin lo puden encontrar en Labcenter Electronics\Proteus 7 Professional\SAMPLES\VSM for USB\PICDEM FS USB\MSD

si lo cargas en tu mmc creo que re rulara, a mi me funciono.

No me funcionó tampoco.

Buenas!

Mi primer post en este foro... aunque lleve años visitándolo...

Estoy con la idea de hacerme una "maquinita"... tengo algunos componentes comprados, pero me interesa mucho trabajar emulando antes de coger la protoboard o el soldador.

He probado todo lo probable en Proteus y no salgo del consabido "Command unsupported".

Leo en todas partes que no hay forma.

Estoy por probar otras librerías tipo SDFat.h a ver si ésta soluciona el problema.

¿alguna idea y/o pista de como solventar el uso de SD's con Proteus?

Y si no es posible con proteus... ¿Qué otro software me recomendais para emular un ATMEGA+display Nokia+SD

Gracias anticipadas ;-)

Tambièn he probado a crear un archivo de imagen con winimage (como he visto por ahí googleando que recomendaban) y nada. No he sido capaz de encontrar demasiada información del error. De todas formas el fallo (command unsupported) no parece que tenga que ver con el archivo de imagen, sino que sencillamente la tarjeta no entiende los comandos SD. Con lo mío voy avanzando poquito a poco compilando/grabando, prueba/error. Espero no agotar los ciclos de borrado de mi mega ;D

Lo curioso es que hay un demo con un pic en Proteus y funciona perfecto. Tomé la imagen como sugirió alguien por Internet pero tampoco funcionó, asi que ya sospecho que algo malo tiene nuestra librería. Es así?

surbyte: Lo curioso es que hay un demo con un pic en Proteus y funciona perfecto. Tomé la imagen como sugirió alguien por Internet pero tampoco funcionó, asi que ya sospecho que algo malo tiene nuestra librería. Es así?

Exacto! Con un PIC18F452.... lo encontré Youtube.... viene el HEX pero no el SRC.... :(

SI. correcto. Seguimos trabajando. Mucha, mucha gente habla de problemas para hacer funcionar la SD en Proteus. Debe tener un truco simple porque Proteus anda muy bien en todos los frentes electrónicos.