Go Down

Topic: Arduino script and SD card library? (Read 2 times) previous topic - next topic

nicholas.masson

Hello!
  I am working on a pretty big script using the SD card library and datalogging info from sensors.  At present, I have a working code, but when I try to add more code, it stops working.  That is, even when I add an empty: if(TRUE) {} to the end of my void loop(), then the program stops working when I upload it.  The only thing I could figure is that I have used up all my memory for the program??  But at the bottom of my arduino window, it says "Binary sketch size: 22582 bytes (of a 32256 byte maximum)", so it looks like I am not using it all...

Any ideas?  Any hidden reasons for this?  Does the SD library take it's SD buffer out of this memory, for example?  (I would think it RAM, but...)

Thanks!
-Nick

PaulS

There are three kinds of memory. The compiler is reporting on the use of one of them. You may well be running out of SRAM memory even though the sketch does not fill the Flash memory space.

Quote
Does the SD library take it's SD buffer out of this memory, for example?

Yes, the SD library uses a lot of SRAM (512 bytes+), of which you only have 2K.

There are ways to move stuff from SRAM to Flash, but we need to see your code to see if that will help.

nicholas.masson

Thanks for the reply.  Here is most of the code, with some omitted so it fits in the message size limit:

Code: [Select]

#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include "RTClib.h"
#include <DHT22.h>
#include <stdlib.h>


RTC_DS1307 RTC;

File logfile; //declare the name of the logging file
//char current_file[]="UPODXX/MMDDYYYY.txt";
char current_file[]="XXMMDDYY.txt";  //make sure to check first_date value
char model[]="UPOD00"; //UPOD model indicator


void setup()

 {
 
  //attachInterrupt(1,calibrate,RISING);  //signal interrupt for calibration button
 
  pinMode(CS_Uno, OUTPUT);
  pinMode(CS_ADC_1, OUTPUT);
  digitalWrite(CS_ADC_1,HIGH); //deselect ADC initially
  pinMode(CS_ADC_2, OUTPUT);
  digitalWrite(CS_ADC_2,HIGH); //deselect ADC initially
 
  pinMode(red_led,OUTPUT);
  pinMode(green_led,OUTPUT);
  pinMode(flag_led,OUTPUT);
 
  Serial.begin(9600);

  Wire.begin();
  RTC.begin();
 
  SPI.begin();
  SPI.setDataMode(0);
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  //delay(200); //allow initialization
 
  if (RESET_LOCAL_TIME)
   {
    RTC.adjust(DateTime(__DATE__, __TIME__));
   }
 
 
  if (! SD.begin(CS_Uno))
   {
     if(ECHO_DEBUG)
     {
       Serial.println("*Card initialization failed*");
     }
     
     //while(1) //lock into loop with red/green sync flashing at 1Hz for 10 sec, then reboot system
     for(int i=1;i<=10;i++)
     {
      digitalWrite(green_led, HIGH);
      digitalWrite(red_led,HIGH);
      delay(500);
      digitalWrite(green_led,LOW);
      digitalWrite(red_led,LOW);
      delay(500);
     }
     soft_reset();
   }
   
  if (! RTC.isrunning())
  {
      if(ECHO_DEBUG)
     {
       Serial.println("*RTC is not working*");
     }
 
      while(1) //Flash red/green in alternate at 1Hz if RTC not working
      {
       digitalWrite(green_led,HIGH);
       digitalWrite(red_led,LOW);
       delay(500);
       digitalWrite(green_led,LOW);
       digitalWrite(red_led,HIGH);
       delay(500);
      }
   }    
   
   DateTime now=RTC.now();
   
   if(ECHO_DEBUG)
   {
    Serial.println();
    Serial.print("Current File: ");
    Serial.println(current_file);
   }
   
    if(! SD.exists(current_file))
   {
     logfile=SD.open(current_file, FILE_WRITE);
     char Header[]= "Analog2  Analog3  Analog4  Analog5";
     logfile.println(Header);
     logfile.flush();
   }
   //if(SD.exists(current_file))
   else
   {
     logfile=SD.open(current_file, FILE_WRITE);
   }
   
   if(!logfile)  
   {
    if(ECHO_DEBUG)
     {
       Serial.println("*File cannot be found or opened on card*");
     }
     
     //while(1) //lock into loop with red/green sync flashing at 1Hz
     for(int i=1;i<=10;i++)
     {
      digitalWrite(green_led, HIGH);
      digitalWrite(red_led,HIGH);
      delay(500);
      digitalWrite(green_led,LOW);
      digitalWrite(red_led,LOW);
      delay(500);
     }
     soft_reset();
   }
 }
 
 void loop()
 {
   
   DateTime now=RTC.now();
   
   if(ECHO_DEBUG)
   {
    Serial.println("Sampling Sensors...");
   }
   
   digitalWrite(green_led,HIGH);  //flash green light when sampling
   delay(50);
   digitalWrite(green_led,LOW);
   
   //Sample sensors and write to buffer
   ADCcall(ADC1, CS_ADC_1);
   ADCcall(ADC2,CS_ADC_2);
   
   myRHT03.readData(); //read the RHT03 T and Rh
   
   //add values to space delimited data line
   logfile.print(model); //model name
   logfile.print("  ");
   logfile.print((int)now.year()); //current year
   logfile.print("/");
   logfile.print((int)now.month()); //current month
   logfile.print("/");
   logfile.print((int)now.day()); //current day
   logfile.print("  ");
   logfile.print((int)now.hour()); //curent hr
   logfile.print(":");
   logfile.print((int)now.minute()); //current minute
   logfile.print(":");
   logfile.print((int)now.second()); //current second
   logfile.print("  ");
   logfile.print(now.unixtime()); //current unixtime; time since midnight 1/1/1970 (seconds)
   logfile.print("  ");
   logfile.print(ADC2[1]); //BAT voltage
   logfile.print("  ");
   logfile.print(analogRead(A1)); //Baseline VOC sensor
   logfile.print("  ");
   logfile.print(analogRead(A0)); //CO2 sensor
   logfile.print("  ");
   logfile.print(analogRead(A2)); //CO electrolytic sensor
   logfile.print("  ");
   
   dtostrf(myRHT03.getTemperatureC(),6,2,RHT03_buffer); //Temp internal
   logfile.print(RHT03_buffer);
   logfile.print("  ");
   dtostrf(myRHT03.getHumidity(),6,2,RHT03_buffer); //Rh internal
   logfile.print(RHT03_buffer);

   
   logfile.println();
   flush_crit++; //index counter up for flus criteria
 
   if(flush_crit>=cycles_b4_flush)
   {
     digitalWrite(red_led,HIGH);
     logfile.flush();
     
     flush_crit=0;
     delay(200);
     digitalWrite(red_led,LOW);
     
     if(ECHO_DEBUG)
     {
      Serial.println("Flushing SD card buffer");
      Serial.println("Closing and reopening file");
      Serial.println();
     }
     
       logfile.close(); //close and reopen logfile to check SD card presence
       current_file[(first_date+2)]=now.day()/10+'0';
       current_file[(first_date+3)]=now.day()%10+'0'; //edit current file
       
       
       if(! SD.exists(current_file))
       {
        /* logfile=SD.open(current_file, FILE_WRITE);
         char Header[]="Model  YYYY/MM/DD  Hour  Minute  Second  UnixTime  "
         "Op_voltage  Baseline  CO2  CO Fig1  Fig2  e2v_O3  e2v_NO2  e2v_1  "
         "e2v_2  e2v_3  e2v_4  Temp_outside  Temp_inside  Rh  Light  Analog1  Analog2  Analog3  Analog4  Analog5";
         logfile.println(Header);
         logfile.flush();  */
         soft_reset();
       }
       
       //if(SD.exists(current_file))
       else
       {
         logfile=SD.open(current_file, FILE_WRITE);
       }
       
       if(!logfile)
       {
        soft_reset();
       }
           
   } //end 'flush' loop
   
   //if(calibrate_flag) //enter calibration procedure if calibration button is hit; note: 'calibrate_flag' set with signal interrupt
  // {
     /*for(int i=1; i<=int(CALIBRATION_DURATION*60000./500.); i++)
    {
     digitalWrite(red_led,LOW);
     digitalWrite(green_led,LOW);
     digitalWrite(flag_led,HIGH);
     delay(150);
     digitalWrite(HIGH,LOW);
     digitalWrite(green_led,LOW);
     digitalWrite(flag_led,LOW);
     delay(150);
     digitalWrite(red_led,LOW);
     digitalWrite(green_led,HIGH);
     digitalWrite(flag_led,LOW);
     delay(150);
    }*/
   
  // }
   
   delay(1000/SAMPLE_RATE);
 }

 
 
 
 void ADCcall(int Figaro[], int pin)
 {
   const byte ADC_ch[8]={B1000,B1001,B1010,B1011,B1100,B1101,B1110,B1111};  //channel designations
   const byte start_byte= B00000001; //initial byte to send to start ADC transmission
 
   digitalWrite(pin,LOW);
   delay(10);
   digitalWrite(pin,HIGH); //cycle ADC to reset if it was in LOW initially
   delay(10);
   
   
   for (int i=0; i<8; i++)
     {
     digitalWrite(pin,LOW); //activate ADC chip
   
     byte channel_byte=ADC_ch[i]<<4; //determine channel to read from
   
     SPI.transfer(start_byte);
     byte byte1=SPI.transfer(channel_byte);
     byte byte2=SPI.transfer(B00000000);
   
     Figaro[i]= int(((byte1 & B00000011)<<8 | byte2) ); //mask first 6 bits of 'byte1' and concatenate w/ 'byte2'
     
     digitalWrite(pin,HIGH); //deselect ADC
     }
 }
 
void soft_reset() //software reset; bring script to beggining of reset loop; internal watchdog on atmega
 { //http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1273155873/all
   asm volatile ("  jmp 0");
 }
 

/*void calibrate()
{
 calibrate_flag=true;
Serial.println("CALIBRATE");
}
*/


I'll have to look in to the different types of memory, and how different parts of the sketch are partitioned/allocated/using different types of memory.  Intuitively, wouldn't the if() and other logical loops be compiled and stored in the machine code and not RAM?  It doesn't make sense to me that adding an extra if() loop to my code would use up all the RAM.  

The if(calibrate_flag) {} loop is the one that causes the system to stop working.  Even with the inside of the {} commented out, it still messes up the operations.  

The code will also stop working if I attach a signal interrupt to one of the pins; best I know this is part of the register?

Thanks,
-Nick

wildbill

You have quite a few strings in there that are using RAM. Look at progmem and the F() macro to see how to move them to flash memory. Also, grab one of the freemem functions posted often in the forums or the playground. It'll tell you how much of your RAM you're using.

nicholas.masson

Great, thanks for the info!  I'll look in to shifting some of the data to flash memory rather than RAM.  If it is not too much trouble, would someone mind pointing out some examples of
Quote
a few strings in there that are using RAM
.  Presumably when I use the function logfile.print(), the data is stored in RAM until it is flushed from the buffer, right?  (I assume the 'buffer' must be made of RAM..,?).  But what other 'strings' exactly would be using RAM?  To the best of my knowledge, the character strings (if that is what is meant by 'strings'), will be stored in a register, unless they are qualified as 'volatile', right?  I'll definitely read in to this to expand my understanding of it all, but some quick insight so I can get back on the road would be really appreciated.
Thanks,
-Nick

nicholas.masson

Ah... it appears that any variable will be in SRAM as it is manipulated throughout the program execution?  Correct?  So perhaps I could also save some SRAM by using #define and const on some of my constants?

PaulS

Quote
Ah... it appears that any variable will be in SRAM as it is manipulated throughout the program execution?  Correct?

Yes, but, as was mentioned, you have a lot of constant strings, in the Serial.print() functions. Those can be used straight out of Flash, without ever being transferred to SRAM, with the F() macro.

For instance, change:
        Serial.println("*Card initialization failed*");
to
        Serial.println(F("*Card initialization failed*"));
and watch your SRAM needs drop.

nicholas.masson

Okay, great!  I've been reading a little bit about variable memory allocation, etc... and it isn't clear to me exactly how variables stored in flash are manipulated.  If I use F(), and the string/integer/whatever is constant, then the Serial.print(F("...")) will just write the string from flash memory.  If, however, it is a variable, and I use the progmem functions, then doesn't the variable (stored in Flash) have to be 'loaded' in to RAM to be manipulated, then stored back in flash after manipulation?  Is this how the controller/flash program/machine code carries out the process? (or does a progmem stored variable never "leave" the flash memory?).

I only ask because, if that is the case, then the amount of RAM being used will vary depending on the process and which variables are being read/written from flash to RAM.  If so, then wouldn't your RAM needs change throughout a program execution?  I also haven't looked in to the 'freemem' functions yet, but will they tell you how much RAM is being used THROUGHOUT a program execution (so you can inspect the points in the code when max RAM is being used, and make sure the max is not being exceeded?)

If anybody has a link to some literature on how variables/data are treated (passed from flash to RAM, manipulated etc...) on a low down level, i'd really appreciate to read some more.  It's hard to find some of the fundamentals well laid out on the web; still trying to wrap my head around the whole machine level process.
Thanks alot, -Nick

nicholas.masson

To lay out a few questions for succinctly:

1. when a variable is declared locally in a function, is it written from flash to RAM, used, then wiped out afterwards
2. do global variables reside in RAM whenever the program is running
3. all variables must be written in flash somewhere, right?  RAM does not persist when the power is off, so I assume that at bootup variables are written from flash to RAM
4.  When you use a function like Serial.print("Hello World"); is the string "Hello World" created as a global variable string that persists in RAM?  Or is it created when the function is called, then wiped out of RAM after it is used?

wildbill


To lay out a few questions for succinctly:

1. when a variable is declared locally in a function, is it written from flash to RAM, used, then wiped out afterwards
2. do global variables reside in RAM whenever the program is running
3. all variables must be written in flash somewhere, right?  RAM does not persist when the power is off, so I assume that at bootup variables are written from flash to RAM
4.  When you use a function like Serial.print("Hello World"); is the string "Hello World" created as a global variable string that persists in RAM?  Or is it created when the function is called, then wiped out of RAM after it is used?


1. No. It occupies space on the stack, which is in RAM, for the life of the function call.
2. Yes.
3. Yes.
4. The string "Hello World" is created as a global variable string that persists in RAM

nicholas.masson

Thank you!  I didn't quite get your first response; you say that local variables persist
Quote
for the life of the function call
.  Is that to say that the RAM to which local variables are stored is freed up when the function has completed (I assume so otherwise risk memory leaks...).  If this is true, I would assume it also applies to the main 'void loop()'; for example:
Code: [Select]

int first_var;

void setup()
{
}

void loop()
{
function1();

function2();

Serial.print("Hello World");

function3();
}


Correct me if i'm wrong, but I believe the following is true:
1. loop begins, string "Hello World" does not exist in RAM memory
2. function1() and function2() are executed, "Hello World" still not occupying RAM
3. THEN the string HELLO WORLD get created and enters RAM
4. function3() executes, HELLO WORLD still in RAM
5. loop returns, all variables (including HELLO WORLD) wiped out, and function begins again

If this is not the case, and local variables exist after their parent functions finish, then perhaps my code would benefit from dynamic allocation and 'delete *pointer' to clear up memory that would be used by a function later down the line?  I've never worried much about memory, but this seems like a good opportunity to learn about memory allocation and become a better programmer!

wildbill

For your points 1 to 5 above: No.

Unless you move the string "Hello World" to progmem it will occupy RAM throughout the running of your sketch - it is not a local variable to loop, it is a constant string occupying precious space. Consider this though:
Code: [Select]

void loop()
{
char* str="Hello World";
int len=strlen(str);
Serial.println(str);
}

When loop is invoked, space is set aside on the stack for str and len; str is set to point to the already permanently allocated "Hello World". len takes the length of the string. When loop exits, the stack pointer moves back and len and str are gone. Next time loop runs, the stack pointer will again advance to make space for them. When main calls another function, its local variables will use the same space on the stack that those in loop used. So local variables go out of scope, but string constants are forever. Put them in progmem.

nicholas.masson

Why is it that locally declared strings are not wiped from RAM, whereas locally declared ints, etc... are cleared?  (This also applies to arrays, right?  so character arrays, i.e. strings are not cleared, whereas integer arrays locally declared are cleared after the function has exited?). Is this just a facet of the C programming language?

Thanks,
-Nick

wildbill

A locally declared character array would not take up any memory after its containing function exited. In my example though, I've declared a pointer and pointed it to a constant string. That string data is inherently global. Every time loop runs, str will be pointed to that string - it has to exist in RAM.

nicholas.masson

Ah, okay... I see how the pointer will keep pointing to a permanent place in memory regardless of the local function etc...

Why then would the following string not be deleted when the void loop() exits and returns:
Code: [Select]

void loop()
{
Serial.print("Hello World");
}


It seems like from what you're saying (in previous comments), the Serial.print function will create a "hello world' string, and even after it has printed, and loop has exited and returned to the beginning, the "hello world" string is still in memory...  If the program calls Serial.print("Hello World") a second time, will it create another "hello world" string in RAM, or go back to the first one, or is the string "hello world" indeed deleted and recreated in memory each iteration?  I'm just confused about how the Serial.print() function operates when you give it a string directly.  So would there be a difference between the above code, and the following code concerning how the 'string' array is saved (or deleted from memory).
Code: [Select]

void loop()
{
char to_print[]="Hello World";
Serial.print(to_print);
}


Is this code treated differently (in as far as memory) than if I pass "Hello World" directly to Serial.print()?  Would this code clear the character array to_print[] from memory after the loop() function has finished executing?

Go Up