¿Cómo optimizar el código para que ocupe menos memoria ram?

Hola, he estado trabajando en un código para un sistema de monitoreo de cultivos que incluye sensores y datalogger, finalmente leído por un NFC App Android (que todavía estáen desarrollo).

El problema apareció cuando terminé el código y fui a compilar ...

Arduino me muestra este error: Poca memoria disponible, se pueden producir problemas de estabilidad.

Yo ya intenté optimizar el código, eliminando algunas líneas que sobraban y algunas variables y pude llegar al 36% de SRAM libre.

Pero aún con 726 bytes libres, el programa sigue inestable y el monitor serial muestra una confusión de caracteres ASCII como ESTOS.

Si ustedes, saben cómo resolver este problema sin tener que comprar un Arduino Mega (no tengo el dinero), por favor, ayúdenme, estaré muy agradecido!

// Codigo Datalogger con fecha y hora para sensores DHT11 y YL-69
/* Librerias NFC y SPI */
#include <SPI.h>                // Librerias NFC
#include "PN532_SPI.h"
#include "NfcAdapter.h"
#include "snep.h"
#include "NdefMessage.h"



/* Librerias Time, Sensores y SD */
#include <TimeLib.h>            // Libreria para el tiempo virtual
#include <DHT11.h>              // Libreria DHT11
#include <SD.h>                 // Libreria SD



/* Variables a usar */
File dataFile;                  // Archivo que creamos
DHT11 dht11(3);                 // Pin datos sensor



String dataString;              // Linea con datos a grabar
String UIDtag;
int err;                                  // Variable de error en la lectura
float temp, hum;                          // Variables del sensor 1
int humsuelo;                             // Variable convertida map porcentaje


    Sd2Card card;                 //Variables para la SD
    const int chipSelect = 4;

    uint8_t ndefBuf[128];         //variables para comunicación P2P
    String mensaje;


void setup(){
  PN532_SPI interface(SPI, 10);   // Se crea una interfaz serial para el chip PN532, en el pin 10
  NfcAdapter nfc = NfcAdapter(interface);
  
  Serial.begin(9600);                      // * Iniciamos comunicacion serial
  setTime(02,20,00,13,10,2016);            // * Configuramos la hora
  pinMode(chipSelect, OUTPUT);             // * Declaramos el pin ChipSelect como salida (modulo SD)
  
  pinMode(5, INPUT_PULLUP);                // * DHT11 GND
  pinMode(6, INPUT);                       // * Definimos como entrada el pin 8 del arduino para el switch
  pinMode(7, INPUT);                       // * Definimos como entrada el pin 8 del arduino para el switch
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  
  digitalWrite(10,LOW);                    // Desactivo SS del modulo NFC
  digitalWrite(4,HIGH);                    // Activo SS del modulo SD
  
  nfc.begin();
  
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.println(F("Fallo al iniciar"));

    return;
    } else {
        Serial.println(F("Inicio CORRECTO"));
    }
 }



void loop(){

    time_t t = now();                         // Variable libreria tiempo
  
    int humsueloraw = analogRead(A0);         // Variable en raw del sensor 2 en la entrada A0
    humsuelo = map(humsueloraw,0,1020,100,0);
    dht11.read(hum, temp);
    
    dataString=String(day(t)) + String("/") + String(month(t)) + String("/") + String(year(t)) + String(" ") + String(hour(t)) + String(":") + String(minute(t)) + String(", ") + String(temp) + String(" C, ") + String(hum) + String(" %") + String(", ") + String(humsuelo) + String(" %");    // Actualizamos la linea que guardaremos en el archivo de texto
    
    digitalWrite(10,LOW);                   // Desactivo SS del modulo NFC
    digitalWrite(4,HIGH);                   // Activo SS del modulo SD

    SD.begin(chipSelect);    
    File dataFile=SD.open("lectura.txt", FILE_WRITE);     // Creamos archivo de texto
    if(dataFile){
      dataFile.println(dataString);                       // Excribimos el contenido de "dataString" dentro del archivo de texto
      dataFile.close();                                   // Cerramos la escritura dentro del archivo
      Serial.println(dataString);                         // Mostramos en serial lo que se escribio
    }
    else{
      Serial.println(F("Escritura erronea... "));    // Texto informativo de error de escritura
      }
   delay(1000);
   digitalWrite(8, HIGH);
   digitalWrite(9, HIGH);  
     
  while(digitalRead(7)==LOW && digitalRead(6)==HIGH){
    NFCtags();
    }
  while(digitalRead(7)==HIGH && digitalRead(6)==LOW){
    NFCp2p();
  }
}
   


  
void NFCtags(){
  PN532_SPI interface(SPI, 10);   // Se crea una interfaz serial para el chip PN532, en el pin 10
  NfcAdapter nfc = NfcAdapter(interface);
  
  time_t t = now(); 
  nfc.begin();                                             // Inicia modulo NFC
  Serial.println(F("Acercar el Tag NFC a la antena"));     // Texto informativo en puerto serial

  if (nfc.tagPresent()){                              // Lectura por si hay un elemento NFC presente
    NfcTag tag = nfc.read();                          // Lee el Tag(nfc.read) y lo convierte en un objeto (NfcTag)
    //tag.print();                                    // Imprime el tipo de Tag NFC, UID (codifo unico de identificacion), y el mensaje grabado (si esta disponible)
    
    UIDtag = String(day(t)) + String("/") + String(month(t)) + String("/") + String(year(t)) + String(" ") + String(hour(t)) + String(":") + String(minute(t)) + String(" - ") + String(tag.getUidString());
    
    SD.begin(chipSelect);    
    File dataFile=SD.open("tag.txt", FILE_WRITE);     // Creamos archivo de texto
    if(dataFile){
      dataFile.println(UIDtag);                       // Excribimos el contenido de "dataString" dentro del archivo de texto
      dataFile.close();                                   // Cerramos la escritura dentro del archivo
      Serial.println(UIDtag);                         // Mostramos en serial lo que se escribio
    }
    else{
      Serial.println(F("Escritura erronea... "));     // Texto informativo de error de escritura
      }
    }
   delay(1000);
   digitalWrite(8, LOW);
   digitalWrite(9, HIGH);
}
 

void NFCp2p(){
  PN532_SPI pn532spi(SPI, 10);
  SNEP nfc(pn532spi);

  Serial.println(F("Acerque el dispositivo a la antena")); 
  
    mensaje=String("Este es el mensaje de prueba");
    NdefMessage message = NdefMessage();
    message.addUriRecord(mensaje);
    int messageSize = message.getEncodedSize();

      message.encode(ndefBuf);
      nfc.write(ndefBuf, messageSize);

      delay(3000);    
}
  • La instancia de Sd2Card no se utiliza.
  • Si sabes que un valor nunca será mayor a 255 (o fuera del rango -128 127), entonces no lo guardes en un int.
  • Al objeto String se le suele acusar de consumir más espacio del que debe, así que mejor evítalo. Además de que su consumo de memoria no se puede determinar en tiempo de compilación, así que posiblemente esos 726 bytes libres sean los calculados con Strings vacíos.
  • El compilador no calcula cuanta memoria se va a consumir en tiempo de ejecución, así que eso hay que tomarlo en cuenta a la hora de crear funciones. Sólo calcula el espacio libre, basado en las constantes y variables globales.
  • Usas la comunicación serial solo para depurar? Cuando no haga falta, no lo uses (ni siquiera hagas Serial.begin())
1 Like

Hay que tener en consideración siempre que hoy es mas cara el tiempo de programación que un Arduino con mas memoria. No digo que no optimices como bien te dijo Lucario pero considera usar un MEGA o DUE.
De lo contrario debes analizar las librerías y quitar cosas superfluas.
No seré redundante, veamos que resulta de lo que Lucario te aconsejó.

Hola.
Te digo cosas sueltas y me vas comentando el resultado.
En primer lugar con inicializar una sola vez SD bastaría; así que la línea

    SD.begin(chipSelect);

ponla en el setup y quítala de los demás sitios.

En segundo lugar, creo que puedes prescindir de los Strings dataString y UIDtag. Elimina sus declaraciones y escribe directamente en el File. Algo así como:

    File dataFile=SD.open("lectura.txt", FILE_WRITE);     // Creamos archivo de texto
    if(dataFile){
      // dataFile.println(dataString); // quitamos esta línea e imprimimos directamente
      datafile.print(day(t));
      datafile.print("/");
      datafile.print(month(t));
      datafile.print("/");
      datafile.print(year(t));
      datafile.print(" ");
      datafile.print(hour(t));
      datafile.print(":");
      datafile.print(minute(t));
      datafile.print(", ");
      datafile.print(temp);
      datafile.print(" C, ");
      datafile.print(hum);
      datafile.print(" %");
      //etcétera
      dataFile.close();                                   // Cerramos la escritura dentro del archivo
      Serial.println(dataString);                         // Mostramos en serial lo que se escribio
    }

En la función NFCtags sustituiremos de forma similar el dataFile.println(UIDtag);
Quita también la definición global FILE dataFile (la primera vez que aparece, para entendernos), puesto que la estás redefiniendo localmente tanto en loop como en NFCtags.

Y cuenta si se libera algo de memoria.

Lucario, apliqué tus concejos junto con la descripción de Noter, cambié los [dataString=String(day(t))...] a [dataFile.print(day(t))...] y no rerpesentó un gran cambio en la RAM usada.

Entonces la solución por la que opté fue pedir prestado un Mega 2560 a un amigo mientras consigo cómo comprar uno.

Y lo que dice surbyte es muy cierto,

...hoy es mas cara el tiempo de programación que un Arduino con mas memoria.

Cuando se trata de algo urgente, el tiempo es más que oro.

Muchas gracias a todos por responder, que tengan un buen día.

Me alegro que te dieras cuenta que es como luchar contra lo mas difícil.
Pero eso te sirve de experiencia, si el proyecto es grande nunca uses un UNO, Mega o DUE. yo el MEGA ya no lo uso. El DUE no se porque he visto que esta descontinuado pero se seguirá consiguiendo NO ORIGINAL asi que es mi primera opción en general. Además de que amo su AD de 12 bits.