String memory issue

Hi

I am nearly finished my first arduino project. I am building a fridge controller that basically reads the temperature of a ds18b20 sensor sitting in fermenting beer and switches the fridge on and off appropriately. Alot of the settings can be controlled via serial port e.g. set fermentation temp, set hysteresis, report temps to then be imported into mysql db then charted on a webpage.

My code picks up that something has been entered into the serial port, puts it into a String object then runs through a series of if statements on the String object to determine what is to be done. My problem is the more if statements I add the more memory is used to the point it runs out then the arduino will just freeze up.

How can I write this function to not use up all the memory?

below is my code and a cut down version of my read serial commands function. I find even if I just keep comparing the string over and over removing any code that runs one of its functions it still uses more and more memory.

String readString;

// function combines multiple characters sent via serial into a global String
void serialRead() { 
  while (Serial.available()) {
  delay(10);  
  if (Serial.available() >0) {
    char c = Serial.read();
    readString += c;}
  }
}

// free ram function found here http://playground.arduino.cc/Code/AvailableMemory
int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

void printFreeRam() {
  Serial.print("Free Memory: ");
  Serial.print(freeRam());
  Serial.println(" Bytes");
}

// cut down version of my serial read function
void serialComms() {
      if (Serial.available() > 0) {
        readString = "";
        serialRead();
		if (readString == "clearfreezer") {
			Serial.println("clearfreezer>");
			Serial.println("Clearing saved hex address for freezer sensor from EEPROM");
			for (uint8_t i=0; i<8; i++) {
				EEPROM.write(EEPROM_FREEZERSENSOR+i,255);
				Serial.print("Cleared byte :");
				Serial.println(EEPROM_FREEZERSENSOR+i);
			}
			g_freezerSensor = 255;
			Serial.println("Running startup function");
			startup();
		}
		
		// clear all eeprom
        if (readString == "cleareeprom") {
			Serial.println("cleareeprom>");
            for (uint16_t z=0; z<1024; z++) {
				EEPROM.write(z,255);
				Serial.print("Erased byte: ");
				Serial.println(z);
			}
        }
         // sets fermentation temperature
         if (readString.startsWith("setf")) {
           Serial.println("setf>");
           String numStr = readString.substring(5,7);
           g_ftemp = numStr.toInt();
            if (g_ftemp > 3 && g_ftemp <= 30) {
               Serial.print("Setting fermentation temperature to ");
               EEPROM.write(0,g_ftemp);
               g_ftemp = EEPROM.read(0);
               Serial.println(g_ftemp);
                updateStatus("Temp set");
            }
        }
printFreeRam()
}

complete code #include <TimerOne.h>#include <OneWire.h>#include <DallasTemperature.h>#in - Pastebin.com

Thanks

How can I write this function to not use up all the memory?

Don't use the String class. Use shorter names. Use the F() macro around string literals.

ahhh the F() function. I did not know that serial.print(string) would use up RAM. Changed all of them over to Serial.print(F("string")) now working fine.

I'm sticking with the String class as it just makes working with the string easy. Ill deal with the overhead.

Thank You

To expand on PaulS's answer, the Arduino has flash memory and SRAM memory. Essentially, the flash memory stores the program while the SRAM stores the data that is being manipulated such as strings, variables, etc. Unless you tell it otherwise in your code, it also stores all the strings that you will be printing, so Serial.print("Free Memory: ") will use 14 bytes of memory. The problem is that this memory is much more limited (~2k for a Uno) and you can eat it up quickly.

For strings and variables that won't be changing, you can save memory by leaving it in Flash, rather than copying it over to SRAM. The easiest way to do this is with the F() function Paul mentioned. Change
Serial.print("Free Memory: ")
to
Serial.print(F("Free Memory: "))
This tells Arduino to leave "Free Memory: " in flash rather than copying it over to SRAM.

You can also use PROGMEM for other variables, but that gets a bit more complicated. See PROGMEM - Arduino Reference or http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=38003 for a tutorial on this.

Another easy way to save some bytes is to use #define for variables that aren't going to change in your program.

1 Like

Add a few more commands and you will run out of SRAM again. Switch to Arduino MEGA 2560 if you want to hang on to the String class. Otherwise, you should switch to C-strings and related functions. Your way of String usage is problematic. If the longest message you may receive is x characters long, you may potentially use up (1/2)*(x^2) bytes of SRAM, by adding one byte at a time. You should use the reserve method to reserve enough memory for your String object so it doesn't have to grow into a different memory location each time you add a byte.

Hi,

To save some RAM for a project I'm working on, I used the F() macro to store strings in flash.

But I wanted to save some flash too by storing strings only once in flash and reference is with Serial.print() every time I needed too.

I came up with this solution:

#include <MemoryFree.h>

const char htmlHeader[] PROGMEM = "<html><body>";          // Strings used multiple times
const char htmlFooter[] PROGMEM = "</html></body>";

void setup(void) {

  Serial.begin(115200);
  
}

void loop(void) {
 
  
  Serial.print(F("\n\nFree memory: "));
  Serial.println(freeMemory());

  Serial.println((__FlashStringHelper *)htmlHeader);
  Serial.println(F("<p style=\"color:red\">404 Page not found</p>"));                        // String used once
  Serial.println((__FlashStringHelper *)htmlFooter);
  Serial.println("");
  Serial.println((__FlashStringHelper *)htmlHeader);
  Serial.println(F("<p style=\"font-family:verdana; color:lime\">Hello world!</p>"));        // String used once
  Serial.println((__FlashStringHelper *)htmlFooter);
 
  delay(5000); 
  
}

The __FlashStringHelper is a class from WString.h used by the F() macro.

Enjoy

"If it fails use a bigger hammer"

Completely irrelevant to the OP as he's receiving bytes to store to not to recall from memory. On the other hand, I got to try your method. I've thought of finding a way to reuse F() strings but didn't go far enough to find the way. So thanks.

beniam:
Hi,

To save some RAM for a project I'm working on, I used the F() macro to store strings in flash.

But I wanted to save some flash too by storing strings only once in flash and reference is with Serial.print() every time I needed too.

I came up with this solution:

#include <MemoryFree.h>

const char htmlHeader[] PROGMEM = "";          // Strings used multiple times
const char htmlFooter[] PROGMEM = "";

void setup(void) {

Serial.begin(115200);
 
}

void loop(void) {

Serial.print(F("\n\nFree memory: "));
 Serial.println(freeMemory());

Serial.println((__FlashStringHelper *)htmlHeader);
 Serial.println(F("<p style="color:red">404 Page not found

"));                        // String used once
 Serial.println((__FlashStringHelper *)htmlFooter);
 Serial.println("");
 Serial.println((__FlashStringHelper *)htmlHeader);
 Serial.println(F("<p style="font-family:verdana; color:lime">Hello world!

"));        // String used once
 Serial.println((__FlashStringHelper *)htmlFooter);

delay(5000);
 
}




The __FlashStringHelper is a class from WString.h used by the F() macro.

Enjoy

"If it fails use a bigger hammer"

liudr:
Completely irrelevant to the OP as he's receiving bytes to store to not to recall from memory. On the other hand, I got to try your method. I've thought of finding a way to reuse F() strings but didn't go far enough to find the way.

In the AVR_LibC pages, Program Space Management library, PROGMEM.
http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html

But read PROGMEM means copy so you want to copy directly to the serial out buffer via pointer and save an array?

And just of references, AVR LibC is the horse's mouth for Arduino. It's where to go when your code says nay!

The libraries page:
http://www.nongnu.org/avr-libc/user-manual/modules.html

And the C text manipulation functions, <string.h>! Simplicity and stability go hand in hand.
http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html