SOLUCIONADO: liberar memoria al obtener datos de SD

Hola, he estado trabajando en un código que obtiene los datos desde un archivo en la tarje SD y luego los envía a un servidor, el problema es que estaba haciendo mal la concatenación de los datos recibidos desde la SD, lo que yo hacía era algo más o menos así:

#include <SD.h>

File myFile;
unsigned int stringIndex = 0;
unsigned int cursorPosition = 0;
char inputString [100];
char inputChar;

void setup()
{
  Serial.begin(9600);

  Serial.print("Initializing SD card.");
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);

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

void loop(){
  memset(inputString, 0, sizeof(inputString));
  
  myFile = SD.open("file.txt");
  myFile.seek(cursorPosition);

  if(myFile){

    while(myFile.available()){
      inputChar     = myFile.read();
      if(inputChar == '*'){
        while(inputChar != '\n'){
          inputChar                 = myFile.read();
        }
      }else{
        while(inputChar != '\n'){
          inputString[stringIndex]  = inputChar;
          inputChar                 = myFile.read();
          stringIndex++;              
        }
      }
      cursorPosition = myFile.position();
      break;
    }

    myFile.close();
    stringIndex = 0;
  }
  Serial.println(inputString);
}

con eso ahora no tengo problemas, pero tengo algunas dudas.

el archivo en la SD es algo como esto:

*/-12/-42/-47/
 /67/-20/44/
 /19/-31/0/
 /-16/-14/-48/
 /-45/-34/-71/

estos datos son obtenidos desde una acelerógrafo, los datos con un asterisco (*) son los datos ya enviados.

- No sé como poder eliminar los espacios en blanco (como el trim de String) desde el array final, yo lo he definido con un tamaño de 100, porque los datos del acelerógrafo varían, por lo que me gustaría poder dejar sólo los datos con información para luego enviarlos.

¿alguien puede explicarme como puedo hacer esto?

he modificado el título debido a que esas dudas las he corregido, sin embargo el problema principal con mi código, continúa (la memoria)

escribí más del problema aquí: http://forum.arduino.cc/index.php?topic=279679.msg1968204#msg1968204

file.txt (6.67 KB)

sube un archivo de la SD que no supere 1MB o lo comprimes y lo subes usando Attachment.
Y veré que ocurre.

he agregado un archivo en el primer post surbyte.

No lo vi, disculpa. Ya lo pruebo.

Con que shield tienes la SD o mejor dicho recuerdame los pines a los que esta conectada la SD.

tengo la shield de arduino, los pines son:

** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - Pin 4

(estoy usando arduino Mega)

Primero me da este error mi IDE en esta línea.

return String(inputString);

SD.ino: In function 'void loop()':
SD.ino:53: error: return-statement with a value, in function returning 'void'

lo comento y funciona.
Esta bien lo que dice. No puedes terminar un void loop con un return algo.

lo copiaste de otro código mas grande?

Otro error falta <SPI.h>

Agregado y compila con IDE version 1.5.4
Simulando con Proteus, ya casi funciona.
Tengo algunos errores pero es cosa de 1 momento y compartimos experiencias.

si se me ha pasado ese error, en el loop va de esa forma

Serial.println(inputString);

es un código mucho más largo y esa parte va dentro de una función, me he olvidado de quitar el return, cuando ya solucione todos los problemas dejaré el código completo para ver si a alguien más le sirve.

yo uso sublime text con el plugin para arduino que utiliza el compilador de del IDE (1.0.6) y hasta ahora no he necesitado de incluir la librería SPI

Mira el tiempo que llevo luchando con la simulación por dios....
para CS usas pin 10 no pin 4.
Pero ya estamos, se me queda inicializando SD y no avanza.
cuando demora eso en tiempo real?

Adjunto el esquema que tengo. Claro que el tuyo esta armado en el shield.
No logro que funcione el código.
se me queda inicializando y puede ser la simulación, no se.

yo siempre he podido inicializar la shield de esta forma

  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);

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

cualquier otro parámetro me da error.
Cuando ejecuto el código, la lectura es muy rápida, una linea demora solo un par de milisegundos
el archivo completo ese que adjunté, demorará unos 4 o 5 segundos.

Hay una incoherencia en ese código.
Mira.

 pinMode(10, OUTPUT);
 digitalWrite(10, HIGH);

Inicializas el pin 10 para e Chip Select de la SD

Y acá le pides que inicialices usando otro Chip Select, el 4... entonces cual usas, el 4 o el 10.

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

si, lo sé, pero si saco o modifico los parámetros, en ocasiones mi shield deja de funcionar.
De todas maneras, yo consigo leer y escribir correctamente los datos de la SD.
Lo tengo así porque en los sketch de ejemplo de la librería SD dice:

// 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);
  digitalWrite(10, HIGH);

Lo que entiendo de eso, es que SD.begin() inicializa el pin CS
y el pinMode y digitalWrite el pin SS

Hola.
Efectivamente, como indica gepd en el último post, para que el SPI pueda funcionar, el pin SS digamos, general (10 en UNO y 53 en el Mega) debe establecerse como salida, independientemente de que luego utilicemos otro pin SS para un dispositivo determinado (en este caso el 4). En este caso, tratándose del MEGA, lo de poner el pin 10 en OUTPUT no parece tener ningún sentido. En tal caso debería ser el pin 53, pero tal vez la propia inicialización de SD esté realizando ya esa tarea.
En resumen, que deberías modificar las dos líneas que hacen referencia al pin 10 para que hagan referencia al pin 53.

si, el asunto es que a mi me funciona bien la shield y mi "problema" es otro, como expliqué en el primer post :smiley:

Ya se que tu problema es otro, pero no tenia el shield porque estuve todo el dia de ayer en mi casa y queria simularlo pero no pude superar la inicialización y me cansé.
Hoy lo pruebo con el shield.
Tengo 3 variantes.

He encontrado una solucion a mi problema, la explicaré para cuando alguien más tenga:

Lo primero, los espacios en blanco lo solucioné agregando \0 al final del array, esto le indica al compilador donde debe terminar de leer, lo hice después de haber agregado todos los datos dentro de la variable inputString, esta última variable tiene un máximo de caracteres a agregar, definido en el comienzo del código, por lo tanto hay que asegurarse que el array tenga el espacio suficiente para para almacenar la información que necesitamos y \0.

while(inputChar != '\n'){
   inputString[stringIndex] = inputChar;
   inputChar = myFile.read();
   stringIndex++;
}

inputString[stringIndex - 1] = '\0'; // Agrego \0

en mi caso he modificado el máximo de caracteres a 25

char inputString [25];

Es una buena costumbre también comprobar si no se están agregando más caracteres de los que acepta el array para no tener un desbordamiento de datos.

if(stringIndex < 25)

Con esto me aseguro que solo agregue 24 caracteres y le de el espacio al \0
se puede agregar un mensaje para avisar que hubo un desbordamiento, pero en mi caso no es necesario

así quedaría finalmente

char inputString [25];
char inputChar;
int stringIndex = 0;

while(inputChar != '\n'){
   if(stringIndex < 25){
      inputString[stringIndex] = inputChar;
      inputChar = myFile.read();
      stringIndex++;
   }
}

inputString[stringIndex - 1] = '\0'; // Agrego \0

Este código lo estoy haciendo para poder enviar linea por linea datos a un servidor remoto. Una vez que los datos se envían, y se obtiene el mensaje que se han recibido correctamente, se "comentan" para no enviarlos nuevamente.

cuando termine de realizar todo dejaré el código por aquí.

Luego de haber aclarado mis dudas con lo anterior, haciendo las pruebas correspondientes, me he dado cuenta que mi problema principal aún no está resuelto. Este es que, cada vez que extraigo una linea desde la SD queda en la memoria, obviamente, pero mi duda es como liberar esa memoria, cuando extraigo una nueva linea.

he agregado la librería para visualizar la memoria disponible y me di cuenta que la memoria no se está liberando.

este es el código que tengo en este momento:

#include <SD.h>
#include <MemoryFree.h>

File myFile;
int stringIndex = 0;
int cursorPosition = 0;
char inputString [25];
char inputChar;
boolean endOfLine;
void setup()
{
  Serial.begin(9600);

  Serial.print("Initializing SD card.");
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);

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

void loop(){
  
  Serial.println(getDataSD());
  Serial.print("freeMemory()=");
  Serial.println(freeMemory());
}

String getDataSD(){

  memset(inputString, 0, sizeof(inputString));
  
  myFile = SD.open(getFileNameSD());
  myFile.seek(cursorPosition);

  if(myFile){

    endOfLine = false;    

    while(myFile.available() && endOfLine == false){
      inputChar = myFile.read();
      
      if(inputChar == '*'){
        while(inputChar != '\n'){
          inputChar = myFile.read();
        }
      }else{
        cursorPosition = myFile.position()-1;

        if(inputChar == 10 || inputChar == ' ') inputChar = myFile.read();

        while(inputChar != '\n'){
          if(stringIndex < 25){          
            inputString[stringIndex] = inputChar;
            stringIndex++;
            inputChar = myFile.read();
          }
        }
        inputString[stringIndex-1] = '\0';        
        stringIndex    = 0;
        endOfLine      = true;        
      }      
    }
    myFile.close();    
  }
  return inputString;
}

char* getFileNameSD (){
  
  char directory[] = "/ACC";

  checkFolderSD(directory);  

  File file = SD.open(directory);
  file = file.openNextFile();
  
  if(!file) file.rewindDirectory();
  
  if(file){
    String fileName;
    fileName = file.name();
    fileName = '/'+ fileName;
    fileName = directory + fileName;
    
    char fullPath[fileName.length()+sizeof(directory)-1];
    fileName.toCharArray(fullPath, sizeof(fullPath));

    return fullPath;
  }
}

void checkFolderSD(char folder[]){
  if(!SD.exists(folder)){
    if(SD.mkdir(folder)){
      Serial.println("Directorio creado");
    }
  }
}

Lo que hace el código es abrir automáticamente un archivo (hay uno de muestra en el primer mensaje) en la carpeta ACC y comenzar a leer su contenido linea por linea, omitiendo aquellas que comienzan por un *

no puedo ver donde está mi error que hace que la memoria no se sobre escriba o libere.

EDIT: al parecer el problema está en la función getFileName();

tocando de oido completamente y basado en lo que dices de que puede ser getFileName la rutina responsable del problema observo que abres archivo pero nunca lo cierras.
Si lees la recomendación de la librería sugiere que siempre cierres el archivo abierto.
La acumulacion de archivos abiertos implica RAM. Y la RAM es limitada.

Entonces dos sugerencias: 1) intenta leer la RAM en las rutinas en las que sospechas. Otra opcion
2) Cierra el archivo en sea rutina.

Mirá como en este sketch MP3 FilePlayer leen archivos
que hacen al final

       while (file.openNext(sd.vwd(),O_READ))
  178       {
  179         file.getFilename(filename);
  180         if ( isFnMusic(filename) ) {
  181 
  182           if (count == fn_index) {
  183             Serial.print(F("Index "));
  184             SerialPrintPaddedNumber(count, 5 );
  185             Serial.print(F(": "));
  186             Serial.println(filename);
  187             Serial.print(F("Playing filename: "));
  188             Serial.println(filename);
  189             int8_t result = MP3player.playMP3(filename);
  190             //check result, see readme for error codes.
  191             if(result != 0) {
  192               Serial.print(F("Error code: "));
  193               Serial.print(result);
  194               Serial.println(F(" when trying to play track"));
  195             }
  196             char title[30]; // buffer to contain the extract the Title from the current filehandles
  197             char artist[30]; // buffer to contain the extract the artist name from the current filehandles
  198             char album[30]; // buffer to contain the extract the album name from the current filehandles
  199             MP3player.trackTitle((char*)&title);
  200             MP3player.trackArtist((char*)&artist);
  201             MP3player.trackAlbum((char*)&album);
  202 
  203             //print out the arrays of track information
  204             Serial.write((byte*)&title, 30);
  205             Serial.println();
  206             Serial.print(F("by:  "));
  207             Serial.write((byte*)&artist, 30);
  208             Serial.println();
  209             Serial.print(F("Album:  "));
  210             Serial.write((byte*)&album, 30);
  211             Serial.println();
  212             break;
  213           }
  214           count++;
  215         }
  216         file.close();
  217       }

Si, se me ha pasado eso y lo he corregido, además he eliminado la variable de tipo String de esa misma función quedando así

char* getFileNameSD (){
  
  char folder[] = "/ACC";

  myFile = SD.open(folder);
  myFile = myFile.openNextFile();
  
  if(!myFile) myFile.rewindDirectory();
  
  if(myFile){
    char fullPath[sizeof(folder)+ sizeof(myFile.name())];
    strcpy(fullPath,folder);
    strcat(fullPath,"/");
    strcat(fullPath,myFile.name());
    myFile.close();
    return fullPath;
  }
}

ahora consume menos memoria, pero continua sin liberar algo.

cuando en getDataSD() abro el archivo manualmente:

myFile = SD.open("/ACC/11001");
freeMemory()=6592
/-27/-26/-56/
freeMemory()=6592
/-27/-26/-56/
freeMemory()=6592
/-27/-26/-56/
freeMemory()=6592
/-27/-26/-56/
freeMemory()=6592
/-27/-26/-56/
freeMemory()=6592
/-27/-26/-56/

No hay un consumo progresivo de memoria:

cuando obtengo el nombre desde la función, si hay consumo:

myFile = SD.open(getFileNameSD());
freeMemory()=6571
/-27/-26/-56/
freeMemory()=6540
/-27/-26/-56/
freeMemory()=6509
/-27/-26/-56/
freeMemory()=6478
/-27/-26/-56/
freeMemory()=6447
/-27/-26/-56/
freeMemory()=6416
/-27/-26/-56/
freeMemory()=6385
/-27/-26/-56/
freeMemory()=6354
/-27/-26/-56/
freeMemory()=6323
/-27/-26/-56/
freeMemory()=6292

es mucho menor que antes, pero debería ser 0, no sé que más debería optimizar.

revisaré el sketch que me dices.

Hola.
Creo que tal vez podría ser debido a que reabres myFile sin haberlo cerrado antes. Aunque parezca contraproducente, utiliza un File adicional para el directorio (por ejemplo myDirectory), y cierra ambos File al salir de la función.
Prueba a ver y cuenta.
Saludos