0
Offline
Newbie
Karma: 0
Posts: 38
Arduino rocks
|
 |
« on: March 06, 2012, 01:45:21 pm » |
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
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Offline
Brattain Member
Karma: 314
Posts: 35518
Seattle, WA USA
|
 |
« Reply #1 on: March 06, 2012, 02:31:01 pm » |
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. 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.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 38
Arduino rocks
|
 |
« Reply #2 on: March 07, 2012, 06:16:00 pm » |
Thanks for the reply. Here is most of the code, with some omitted so it fits in the message size limit: #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
|
|
|
|
|
Logged
|
|
|
|
|
New Jersey
Online
Edison Member
Karma: 24
Posts: 2352
|
 |
« Reply #3 on: March 07, 2012, 07:34:36 pm » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 38
Arduino rocks
|
 |
« Reply #4 on: March 07, 2012, 10:51:10 pm » |
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 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
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 38
Arduino rocks
|
 |
« Reply #5 on: March 07, 2012, 10:57:15 pm » |
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?
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Offline
Brattain Member
Karma: 314
Posts: 35518
Seattle, WA USA
|
 |
« Reply #6 on: March 08, 2012, 08:24:03 am » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 38
Arduino rocks
|
 |
« Reply #7 on: March 08, 2012, 09:19:50 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 38
Arduino rocks
|
 |
« Reply #8 on: March 08, 2012, 09:32:10 am » |
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?
|
|
|
|
|
Logged
|
|
|
|
|
New Jersey
Online
Edison Member
Karma: 24
Posts: 2352
|
 |
« Reply #9 on: March 08, 2012, 09:44:42 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 38
Arduino rocks
|
 |
« Reply #10 on: March 08, 2012, 12:48:25 pm » |
Thank you! I didn't quite get your first response; you say that local variables persist 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: 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!
|
|
|
|
|
Logged
|
|
|
|
|
New Jersey
Online
Edison Member
Karma: 24
Posts: 2352
|
 |
« Reply #11 on: March 08, 2012, 01:01:04 pm » |
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: 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.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 38
Arduino rocks
|
 |
« Reply #12 on: March 08, 2012, 05:46:52 pm » |
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
|
|
|
|
|
Logged
|
|
|
|
|
New Jersey
Online
Edison Member
Karma: 24
Posts: 2352
|
 |
« Reply #13 on: March 08, 2012, 06:46:21 pm » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 38
Arduino rocks
|
 |
« Reply #14 on: March 09, 2012, 09:36:50 am » |
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: 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). 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?
|
|
|
|
|
Logged
|
|
|
|
|
|