Disminuir el Uso de memoria

Buenos dias compañeros

He realiazado este codigo para tomar el valor de un sensor y enviarlo por medio de una trama de dato utilizando XBee hacia una comutaodra.
El codigo que le presento a continuacion hace lo siguiente:

  • Primero Espera a recibir un simple mensaje, despues de recibirlo comienza a la programacion. Esto lo hice para sincronizar varios dispositivos, quiero colocarlos y que me envien informacion sin parar, una vez que le llegue el mensaje de inicio.

  • Luego llama al RTC para la hora y la fecha.

  • Siguiente Conforma el encabezado de la trama.

  • Luego toma del ADC el valor y lo convierte al valor real.

  • Luego empaqueta el hora y fecha y voltaje en la trama de datos .

  • Calcula el cheksum.

  • Envia la trama.

  • Guarda el voltage con la hora y la fecha.

#include <SD.h>
#include <SPI.h> 
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <SafeString.h>
#include <SoftwareSerial.h>

//DATALOGGER
const int chipSelect = 4;

//ADC
const int ADCpin = A0;
float voltPanel; 
cSF(sfVolts,6); 

//XBEE
SoftwareSerial XBeeSerial(2, 3); //(Rx, TX)
uint8_t Rx_nextByte;
#define START_DELIMITER 0x7E

//TRAMAS
byte packet1[] = {0x7E, 0x00, 0x27}; //Byte delimitador y Longitud de trama // variar por el voltaje
byte packet2[] = {0x10, 0x01, 0x00, 0x13, 0xA2, 0x00, 0x40, 0xD7, 0xAE, 0xAD, 0xFF, 0xFE, 0x00, 0x00,}; //Id de trama, tipo de trama, direccion de 64bits, opciones y numero de brincos.
// xx.xx,00:11:46,05/01/2021 == 25 chars
byte fullPacket[3 + 14 + 25 + 1];// (Delimitador + longitud )+ (tipo + direccion)+ voltage + fecha + checksum = 37
                                //  [3 + 14 + 4 + 19 + 1]
char buffer [27]; // 19 la fecha  +1  '\0', 25 bytes de tamaño // cambiar porque hay voltaje
#define DEBUG SafeString::Output

//SETUP
void setup() {
  Serial.begin(9600);
  delay(100);
  XBeeSerial.begin(9600);
  delay(100);
 // if (!SD.begin(4)) {
   // Serial.println("initialization failed!");
    //while (1);
  //}
  //Serial.println("initialization done.");
  delay(2000);
  Serial.println();
  Serial.println("CODIGO PRINCIPAL");
  Serial.println("-------------------");

  SafeString::setOutput(Serial);
  }
  
//LOOP
void loop() {
  Serial.println("Espera a recibir señal");
  while (XBeeSerial.available()) {
    Rx_nextByte = XBeeSerial.read();
    if (Rx_nextByte == START_DELIMITER){
      //Inicia a mandar datos Si el primer Byte que recive de la PC es un 0x7E.
      Serial.println("Señal recibida");
      
      //RTC
      tmElements_t tm;
      if (RTC.read(tm)) {
        sprintf(buffer, "%02d:%02d:%02d,%02d/%02d/%04d", tm.Hour , tm.Minute, tm.Second, tm.Day, tm.Month, tmYearToCalendar(tm.Year));
        Serial.println(buffer); // Compruebo la fecha y hora actual.

        //Inicializo un sumador de caracteres
        size_t idx = 0;
        memmove(fullPacket + idx, packet1, sizeof(packet1));
        idx += sizeof(packet1);
        memmove(fullPacket + idx, packet2, sizeof(packet2));
        idx += sizeof(packet2);
        if ((idx + 6 + 19 + 1) > sizeof(fullPacket)) {
          Serial.println(F("1 Paquete completo esta mal."));
          }
       //float voltPanel= ((float(analogRead(ADCpin))*5.0/1023)*6+10, 2);//Obtengo del ADC el voltaje
       //Realizo una operacion y = mx + b para obtener el valor real del Panel Fotovoltaico.
       sfVolts.clear(); // remove last text //Voltage xx.xx == 5 y coma 1 == 6
       sfVolts.print((float(analogRead(ADCpin))*5.0/1023)*6+10, 2,2,5); // "xx.xx" o " x.xx" o "-x.xx" o "xxx.x"  
       sfVolts.print(',');
       memmove(fullPacket + idx, sfVolts.c_str(), 6); // Siempre van a ser 6 caracteres de largo
       idx += 6;
       if ((idx + 19 + 1) > sizeof(fullPacket)) {
         Serial.println(F("2 Paquete completo esta mal."));
         }
    
       memmove(fullPacket + idx, buffer, 19);
       idx += 19;
       //Dejo un ultimo byte para tener el Checksum.
       if ((idx + 1) != sizeof(fullPacket)) {
         Serial.println(F("3 Paquete completo esta mal."));
          }

       //Se le da el formato al paquete completo
       for (size_t i = 0; i < idx; i++) {
         if (fullPacket[i] < 16) {
           Serial.print('0'); 
          }
         Serial.print(fullPacket[i], HEX); 
         Serial.print(' ');
         }
         Serial.println();//Se comprueba que el formato de la Trama API.

         XBeeSerial.write (fullPacket , idx);//Se envia la trama del sensor a la PC.
         
         //DATALOGGER 
        String dataString = "";
        dataString += String(voltPanel);
        dataString += String(buffer);
        File dataFile = SD.open("TensionDePanel.txt", FILE_WRITE);
        if (dataFile) {
          dataFile.println(dataString);
          dataFile.close();
          // print to the serial port too:
          Serial.println(dataString);

         
         delay(5000);
       }
      }    
     }
    }    
   }               
void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

Pero tengo varios problemas.

  1. Se usa mucha memoria. Quisiera que me ayuden con eso.

  2. Cuando tomo el voltaje del ADC, a la hora de calcular el voltage real. No esta funcionado, me da un valor menor. Lo proble haciendo un codigo aparta y si funciona.

  3. Cuando corro el programa y lo corro, se queda esperando a recibir la señal. Todo bien hasta ahi. Pero cuando le envio la señal, me logra enviar la primer trama. Luego de eso el codigo vuelve a caer en el loop. Lo cual esta mal.

Puedes eliminarlo porque no lo usas.

Pregunta: para que usas SafeString si luego tmb usas String como al final

 String dataString = "";
        dataString += String(voltPanel);
        dataString += String(buffer);

Es un gran consumo de memoria.
Tiene alguna ventaja el uso de SafeString?

El no usar SafeString me generó esta mejora

El código esta incompleto pero te lo paso.

#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <SoftwareSerial.h>

//DATALOGGER
const int chipSelect = 4;

//ADC
const int ADCpin = A0;
float voltPanel;
String sfVolts = "";

//XBEE
SoftwareSerial XBeeSerial(2, 3); //(Rx, TX)
uint8_t Rx_nextByte;
#define START_DELIMITER 0x7E

//TRAMAS
byte packet1[] = {0x7E, 0x00, 0x27}; //Byte delimitador y Longitud de trama // variar por el voltaje
byte packet2[] = {0x10, 0x01, 0x00, 0x13, 0xA2, 0x00, 0x40, 0xD7, 0xAE, 0xAD, 0xFF, 0xFE, 0x00, 0x00,}; //Id de trama, tipo de trama, direccion de 64bits, opciones y numero de brincos.
// xx.xx,00:11:46,05/01/2021 == 25 chars
byte fullPacket[3 + 14 + 25 + 1];// (Delimitador + longitud )+ (tipo + direccion)+ voltage + fecha + checksum = 37
//  [3 + 14 + 4 + 19 + 1]
char buffer [27]; // 19 la fecha  +1  '\0', 25 bytes de tamaño // cambiar porque hay voltaje
#define DEBUG SafeString::Output

//SETUP
void setup() {
  Serial.begin(9600);
  delay(100);
  XBeeSerial.begin(9600);
  delay(100);
  Serial.println();
  Serial.println("CODIGO PRINCIPAL");
  Serial.println("-------------------");

  //SafeString::setOutput(Serial);
}

//LOOP
void loop() {
  Serial.println("Espera a recibir señal");
  while (XBeeSerial.available()) {
    Rx_nextByte = XBeeSerial.read();
    if (Rx_nextByte == START_DELIMITER) {
      //Inicia a mandar datos Si el primer Byte que recive de la PC es un 0x7E.
      Serial.println("Señal recibida");

      //RTC
      tmElements_t tm;
      if (RTC.read(tm)) {
        sprintf(buffer, "%02d:%02d:%02d,%02d/%02d/%04d", tm.Hour , tm.Minute, tm.Second, tm.Day, tm.Month, tmYearToCalendar(tm.Year));
        Serial.println(buffer); // Compruebo la fecha y hora actual.

        //Inicializo un sumador de caracteres
        size_t idx = 0;
        memmove(fullPacket + idx, packet1, sizeof(packet1));
        idx += sizeof(packet1);
        memmove(fullPacket + idx, packet2, sizeof(packet2));
        idx += sizeof(packet2);
        if ((idx + 6 + 19 + 1) > sizeof(fullPacket)) {
          Serial.println(F("1 Paquete completo esta mal."));
        }
        //float voltPanel= ((float(analogRead(ADCpin))*5.0/1023)*6+10, 2);//Obtengo del ADC el voltaje
        //Realizo una operacion y = mx + b para obtener el valor real del Panel Fotovoltaico.
        sfVolts = ""; // remove last text //Voltage xx.xx == 5 y coma 1 == 6
        float fv = (float(analogRead(ADCpin)) * 5.0 / 1023) * 6 + 10;
        sfVolts = String(fv)+',';
        //sfVolts.print((float(analogRead(ADCpin)) * 5.0 / 1023) * 6 + 10, 2, 2, 5); // "xx.xx" o " x.xx" o "-x.xx" o "xxx.x"
        //sfVolts.print(',');
        memmove(fullPacket + idx, sfVolts.c_str(), 6); // Siempre van a ser 6 caracteres de largo
        idx += 6;
        if ((idx + 19 + 1) > sizeof(fullPacket)) {
            Serial.println(F("2 Paquete completo esta mal."));
        }

        memmove(fullPacket + idx, buffer, 19);
        idx += 19;
        //Dejo un ultimo byte para tener el Checksum.
        if ((idx + 1) != sizeof(fullPacket)) {
            Serial.println(F("3 Paquete completo esta mal."));
        }

        //Se le da el formato al paquete completo
        for (size_t i = 0; i < idx; i++) {
          if (fullPacket[i] < 16) {
              Serial.print('0');
          }
          Serial.print(fullPacket[i], HEX);
          Serial.print(' ');
        }
        Serial.println();//Se comprueba que el formato de la Trama API.

        XBeeSerial.write (fullPacket , idx);//Se envia la trama del sensor a la PC.

        //DATALOGGER
        String dataString = "";
        dataString += String(voltPanel);
        dataString += String(buffer);
        File dataFile = SD.open("TensionDePanel.txt", FILE_WRITE);
        if (dataFile) {
          dataFile.println(dataString);
          dataFile.close();
          // print to the serial port too:
          Serial.println(dataString);
          delay(5000);
        }
      }
    }
  }
}
1 Like

Wow muchas gracias, el uso de memoria bajo considerablemente y el ADC funciona a la perfección. Te lo agradezco bastante.

const int SAMPLES = 20;
int readAnalog(int Apin = A0) {
    long analogValue = analogRead(Apin); // allow for sum
    // discard the first one
    analogValue = 0;
    for (int i = 0; i < SAMPLES; i++) {
      analogValue = analogValue + analogRead(Apin);
      }
      analogValue = analogValue / SAMPLES;
    return (int)analogValue;
 }

Yo intente realizar esto al ADC ya que lei en la teoria que las Hojas de datos de Atmel para los microprocesadores que usa Arduino para Uno / Mega2560 aconseja
el usuario que la primera lectura A / D después de cambiar el voltaje de referencia puede ser incorrecta y debe ignorarse. 20 muestras tardan aproximadamente 2 mS en completarse

Pero a la hora de implementar esa funcion me tiro error. La coloque ahi mismo donde se hace la medicion. Y de igual forma me tiraba esto:

image

No se si es una mala implementacion de la funcion.

Está mal definida la función.
Pusiste

void readAnalog(Apin) {

y debería ser

int readAnalog(int Apin = A0) {

Hay dos errores.
Primer error, la función retorna un valor int según
return (int)analogValue;
pero void dece que la función no devueve nada.
Segundo error, no tipificaste el parámetro Apin, se puede obviar asignarle un valor a un parámetro pero siempre hay que definir de que tipo de dato se trata.

Saludos

Ok Te entiendo.

Ya lo corrigí.
Mira lo que ha pasado

image

No, no entendiste.

int readAnalog(int ADCpin = A0) {

define una función que se llama readAnalog, que tiene un parámetro int de nombre ADCpin con valor por defecto 10 y que devuelve un valor int.
No puede ir en cualquier lado como lo has hecho.
Va antes o después de setup() y loop().

Además, más allá de que está mal donde lo pusiste, sigues sin poner el tipo de ADCpin.

Pones
int readAnalog(ADCpin) {
cuando debe ser
int readAnalog(int ADCpin = A0) {
o
int readAnalog(int ADCpin) {

Pero puedes evitar el uso de la función si solo la llamas 1 vez. Elimina la linea que está resaltada en la captura de pantalla, elimina la linea del return y la llave que está debajo porque queda huérfana y listo

1 Like

Listo ya lo corrigi, muchas gracias Gatul.

Te aconsejo que no hagas un promedio de 20 muestras como el que estas haciendo y mejores todo usando un promedio móvil. El promedio móvil toma la nueva muestra y descarta la última, reduciendo considerablemente el tiempo de muestreo.
De 20 muestras ahora solo pasas a 1 muestreo por vez, porque descartaste la mas vieja y solo sumas la nueva. En realidad el promediador se va llenando y mejora su respuesta hasta completar las 20 muestras y de ahí en adelante hace lo que te he indicado.
Es significativamente rápido. Tiene el costo de mantener 20 registros pero como son de 2 bytes tampoco es la gran cosa. 20x2 = 40 bytes. No te cambia mucho.

Luis Llamas tiene una librería muy bien implementada y te aconsejo la uses Filtro de media móvil de Luis Llamas

Ahora estadísticamente el filtro anterior funciona bien en tanto y en cuento no haya espúreos o cosas raras. En ese caso falla.
Por eso este otro filtro de mediana móvil es aún mejor. Lo dejo aqui por si te interesa Filtro mediana Rapido

Hay una cosa que me llama la atención. En algunas líneas utilizas la macro F():

          Serial.println(F("1 Paquete completo esta mal."));

Y en otras no:

  Serial.println("Espera a recibir señal");

La macro F() hace que esas cadenas no se carguen en la memoria dinámica, que es la que tienes poca, sino que se tomen los datos diréctamente de la memoria de almacenamiento de programa, que igualmente van a estar ahí almacenadas junto al resto del programa. Tan sólo es un poquito más lento al "imprimirlo". Así que si se pone la línea anterior tal que así:

  Serial.println(F("Espera a recibir señal"));

Te deberías de ahorrar unos veinte bytes de memoria dinámica sólo con ese cambio.

Bien, con eso que aportó @IgnoranteAbsoluto tienes la mitad de los elementos del filtro móvil.