[Solucionado] Problema de memoria?...

Hola, quería compartir con ustedes un problema que estoy teniendo. A mi parecer está relacionado con falta de memoria en tiempo de ejecución.
Me está pasando que al momento de arrancar el programa se cuelga / resetea.
Puse unos flags en la función setup para hacer algo de debuging pero... seguía interrumpiéndose.
¿Qué fue lo que hice?... mucho je :slight_smile: desde probar aisladamente las funciones del setup hasta comentar mucho código (esto último me dio una pista)
El .ino tiene muchos includes, muchas funciones propias y llega a tener unas 500 líneas. Probé de comentar la mayoría de las funciones declaradas en el .ino dejando las pocas que se llamaban desde la función setup y... FUNCIONÓ !!!
UPS, creo que estoy en problemas.

Para una mejor explicación les ejemplifico con este codigo:

Antes:
(esto no funciona ya que no muestra las banderas de debug)

void functionA()
{
// aca configuro el serial 
// y pongo las banderas de debuging
....
}
void functionB()
{
....
}
void functionC()
{
....
}
void functionD()
{
....
}

void setup()
{
   functionA();
}

void loop()
{
}

Después:
(Funciona, dejo solo la funcionA, el resto está comentado, empiezo a tener respuesta del debugin por Serial en la pc)

void functionA()
{
// aca configuro el serial 
// y pongo las banderas de debuging
....
}
/*
void functionB()
{
....
}
void functionC()
{
....
}
void functionD()
{
....
}
*/
void setup()
{
   functionA();
}

void loop()
{
}

Hice exactamente eso, comenté funciones del programa que nunca se llamaban ya que no tenía nada en el loop() y tampoco eran llamadas por la funcionA() que está en el setup.

Estuve leyendo sobre PROGMEM con el objetivo de usar la memoria flash y liberar un poco la SRAM, pero tengo miedo de solucionar parcialmente el problema y que luego de estar corriendo el programa por unas minutos/horas comience a resetearse por problemas de memoria.

¿Alguien me podría orientar en esto? ¿Por qué de este comportamiento?
¿Voy a tener que rediseñar/remodelar las clases que definí en mi programa? Posiblemente ¿no?
¿Como podría confirmar que es un problema de memoria?

Para ver si tienes algún problema con la memoria lo que puedes usar es el siguiente código.

// C runtime variables
// -------------------
extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

/*!
 @function   freeMemory
 @abstract   Return available RAM memory
 @discussion This routine returns the ammount of RAM memory available after
 initialising the C runtime.
 @param      
 @return     Free RAM available.
 */
static int freeMemory ( void ) 
{
   int free_memory;
   
   if((int)__brkval == 0)
   free_memory = ((int)&free_memory) - ((int)&__bss_end);
   else
   free_memory = ((int)&free_memory) - ((int)__brkval);
   
   return free_memory;
}

En la rutina setup puede usar esta función de la siguiente forma.

void setup ()
{
   Serial.begin ( 57600 );
   Serial.print ( F("Free mem: ") );
   Serial.println ( freeMemory () );

   // Tu código aquí
}

Varias cosas a tener en cuenta:

  • Solo tiene en cuenta el espacio que ocupa la BSS, e.d. toda las variables que se inicializan en RAM de tu programa y no tiene en cuenta el espacio que ocupa la pila.
  • Introduce las variables en RAM que pueda usar el objeto Serial.
  • Si estás usando el entorno Arduino 1.0 puedes usar la macro F() que viene en "Free mem", en caso contrario borra la F.

Dependiendo de la memoria que te quede libre, tu programa funcionará o no.

Si ves que la memoria está por debajo de los 300bytes, es muy posible que tengas problemas. Reemplaza todos tus cadenas de caracteres por F("mi cadena") siempre y cuando uses el IDE 1.0

Ya nos contarás.

Hola fm Muchas gracias por tu respuesta... te comento los resultados:

Comenté todo mi código de setup y dejé solo este:

void setup()
{
  Serial.begin ( 57600 );  
  Serial.print ( F("Free mem: ") );
  Serial.println ( freeMemory () );
}

Resultado=
Free mem: 685

Por suerte no era menos de 300 :slight_smile: pero de todas formas creo que estamos algo justos con el tamaña ¿no?

Tengo que decirte que puse muchos logs dentro de la aplicación. Y para ello estoy usando una librería que encontré en el playGround llamada SerialDebugger aquí el link:
http://arduino.cc/playground/Code/SerialDebugger

Quise seguir tu consejo de mandar mis cadenas usando la función F() ya que estoy trabajando con Arduino 1.0 pero... claramente no es posible porque esta librería no lo soporta.

SerialDebugger.debug(NOTIFICATION,F("setupDebugging"),"Apply configuration");

Error de compilación
error: no matching function for call to 'SerialDebug::debug(int, __FlashStringHelper*, const char [20])'

No importa, porque estuve tratando de modificar la librería internamente para que al recibir un puntero de char se use la macro ...

Entonces fui a la clase PrintCascade donde se ejecuta finalmente el println y la modifiqué de la siguiente manera

PrintCascade& PrintCascade::println(const char c[])
{
  printer->println(F(c));
  return *this;
}

Error de compilación
PrintCascade.cpp:109: error: initializer fails to determine size of '__c'
por lo que entiendo la macro no está pudiendo determinar la longitud de la cadena de char ya que solo tiene el puntero al primer elemento.

No insistí mas con los cambios de la librería... Tu que recomiendas? Hay algo que no estoy entendiendo del todo sobre el manejo de memoria (será porque vengo del mundo web u otro tipo de aplicaciones donde no se piensa mucho en eso)

¿Me podrías explicar un poco mas sobre las candenas char[] en relación a Serial?
Tu dices que a las cadenas las use con el macro F() para llevarlas a la flash. Eso lo entiendo perfectamente (que sería lo mismo que usar PROGMEM) pero... ¿Por qué? yo cuando estoy creando las cadenas rara vez mantengo esos punteros referenciados. ¿No debería liberarse la memoria al momento de perder el scope? Para ser mas gráfico supongamos en mi ejemplo que las funciones tienen muchas lineas de debug.
Algo similar a esto:

void functionB()
{
SerialDebugger.debug(NOTIFICATION,"functionB","____________________ Init");
....
SerialDebugger.debug(NOTIFICATION,"functionB","--------------------------- Finish");
}

¿Teniendo muchas de estas lineas podría tener problema de memoria?

Disculpa si me extiendo en la explicación pero trato de ser claro.

Graicas!

Market

Hola Market,

lo que estoy viendo es que andas muy justo de memoria, y dependiendo de lo que haga tu programa, se puede quedar sin memoria para la pila. Mi consejo es el siguiente:

  • tira al cubo de la basura la librería SerialDebugger y utiliza a pelo la librería Serial. SerialDebugger está muy bien, pero ocupa memoria y recursos de forma innecesaria.
  • Una vez tengas limpio el código pon F("xyz") en las cadenas que vayas a imprimir con Serial.

El problema que tiene la librería es que no está heredando de la clase Stream y por ende de la clase print que sí contiene las definiciones para imprimir un objeto de la clase __FlashStringHelper*.

La memoria que usas para las cadenas se crean en la BSS con su correspondiente reserva en memoria que se cargan durante el arranque del runtime de C. Por lo tanto esos strings no se se liberan jamás, no se encuentran en la pila, y como no los has creado de forma dinámica, tampoco en el heap.

Claramente estás teniendo un problema de memoria ya que de los 4KB que tiene el AVR te estás comiendo casi 7/8! Las cadenas esas ocupan mucha RAM, algo muy escaso en los microcontroladores.

Hola,

Yo el otro día me hice ésto, por si te sirve de referencia (he copiado y pegado de parte de mi código, pero creo que está todo).

Output es una variable del tipo String => String Output;
Tienes que tener incluido:
#include <avr/pgmspace.h>

Para usarlo hago:
AddOutput_P(PSTR("Error DWH\n\r"));

Lo que te ha comentado fm sobre la función F(), en mi código es PSTR (es lo mismo). En Arduino han creado un macro para que resulte más intuitivo ("F" de Flash).

void AddOutput_P(char *mystr)
{
char *buffer;
buffer=(char *)malloc(strlen_P(mystr)+1);
strcpy_P(buffer,mystr);
Output+=(buffer);
free(buffer);
}

Lo puedes modificar para que te imprima directamente con Serial.print(buffer) ó lo que necesites. Yo lo tengo así porque imprimo después de hacer varias cosas el resultado ( y lo tengo para poder redirigir el resultado por dónde me interese)...Podrías hacerlo directamente como te ha indicado fm, te lo pongo por si necesitas modificar la librería que estabas utilizando o lo que sea.

Salu2

:wink:

Gracias fm, entendí perfectamente lo que me explicaste...
Claramente las cadenas "xyz" en algún lugar tienen que estar guardadas :smiley:
Estaba justo haciendo lo que me proponés... sacando Serial.Debugger y usando el clásico mas el F("...") para las cadenas.
En la Flash Memory tengo mucha mas memoria. // 32 KB (ATmega328) of which 0.5 KB used by bootloader.

Igor R, gracias por sumarte... estuve analizando lo que me pasaste, leí algo sobre malloc y vi lo que comentaste sobre el marco.

Vi el PSTR acá:

class __FlashStringHelper;
#define F(string_literal) (reinterpret_cast<__FlashStringHelper *>(PSTR(string_literal)))

Ahora bien... viendo la función que me pasaste hice un pequeño ejemplo (puse comentarios) y el manejo de Serial.print que me sugeriste

#include <avr/pgmspace.h>

String output;

void setup()
{
  Serial.begin(9600);
  
  AddOutput_P(PSTR("Estoy en "));   // @1
  AddOutput_P(PSTR("Setup \n\r"));

  Serial.print(output);

  output = "";
}

void loop()
{
  AddOutput_P(PSTR("Estoy en "));
  AddOutput_P(PSTR("loop \n\r"));

  Serial.print(output);
  
  while(true){};
}


void AddOutput_P(char *mystr)
{
  char *buffer;

  // reservo un espacio del mismo tamaño que mystr
  buffer=(char *)malloc(strlen_P(mystr)+1);

  // copio el contenido de mystr al buffer
  strcpy_P(buffer,mystr);
  
  // vuelvo a copiar del buffer al outpu
  output+=(buffer); 

  // libero el espacio del buffer
  free(buffer);
}

Output:
Estoy en Setup
Estoy en loop

Tengo dudas sobre el uso de la función AddOutput_P y la variable output.
¿En ejecución no estaría duplicando el espacio?

La primera vez que llamo a AddOutput_P ( // @1)
Tendría en la memoria flash "Estoy en " y en la memoria BSS el objeto output con lo mismo "Estoy en " que se copió del buffer

¿Es correcta mi apreciación?

Gracia a ambos !

Market

El objeto de la clase Sring lo tienes declarado en la bss, pero lo estas usando sin inicializar, ni lo estas inicializando a nada. El siguiente bloque de la función que te han puesto hace una copia de la flash al heap, lo serializa y cuando termina, lo libera.

En serio, si no te quieres complicar la existencia, la forma mas sencilla de depurar es usar la forma quete he comnetado antes, es simple, consume poco y es rapida. Si no necesitas usar otra salida que no sea la linea serie para visualizar la informacion, todo bien. Sino, puedes create un controlador de dispositivo que herede de la clase stream y simplemente escribiendo el metodo write ya lo tienes andando con print.

Por cierto, revisa en tu codigo si utilizas alguna variable u objeto sin inicializar.

[Solucionado]
Definitivamente tengo problemas de memoria, voy a resumir un poco para dejarlo solucionado en el foro.

Manifestación del problema: El programa no respondía durante el inicio, y tampoco se visualizaban logs.
Test: De manera aislada se podían probar los componentes y funcionaban correctamente.

Causa: Falta de memoria para el micro, posiblemente

Solución: Incorporación de la macro F() para las cadenas, elimino el uso de la librería String.Debugger (para no sobrecargar la memoria). Planteo nuevamente el programa (diseño / modelado de objetos)

Esto me motivó a empezar nuevamente la aplicación... verificando minuciosamente el uso de log, inicialización de variables y la pila de ejecución que se podría llegar a generar entre llamadas y... en definitiva simplificando un poco las cosas.

Gracias por tu ayuda !

Hola alguien que me ayude a compactar mi codigo, me falta 1000kb para que pueda compilar en arduino uno obvio en un mega funciona perfectamente

Y no se te ocurrió leer que la gente que conversaba lo hizo en el 2012 no?
Hilo cerrado.
Crea un nuevo hilo y vuelve a leer las normas porque evidentemente no recuerdas que un hilo viejo no se reabre.