Sketch size killing SD writes

Hey all, I’ve spent the last 6 hours banging my head on this one. It seems as soon as my sketch gets above ~21,000 bytes, the SD write function starts failing. I’ve got it to the point where I can literally comment out two lines to get it under 21,000, and it works perfectly. Uncomment the lines, and it fails. The two lines are simply calling the function to print to serial and write to SD. Help!!!

Here’s the code, the lines to comment are under the header “TEST”:

/***********************************************************************************************************/
/* GLOBALS            **************************************************************************************/
/***********************************************************************************************************/
// includes
#include <Wire.h>
#include <SD.h>
#include "RTClib.h"

// pins
const int _pinSdChipSelect = 4;
const int _pinPowerLED = 13;
const int _pinRtcGround = 8; //23;
const int _pinWellPumpPwr = 7; //25;
const int _pinChlorinatorPwr = 6; //39;
const int _pinFillStart = A0;
const int _pinFillStop = A1;

// real time clock
RTC_DS1307 _rtc;
boolean _blResetClock = false;
boolean _blClockSet = false;

// sd logging
boolean _blLogToSD = true;
boolean _blLogToSD_Ready = false;

// tank fill
boolean _blTankIsFilling = false;
DateTime _dtStopFill;



/***********************************************************************************************************/
/* LOGGING            **************************************************************************************/
/***********************************************************************************************************/
void statusUpdate()
{
  Serial.println();
  
  if (_blLogToSD && _blLogToSD_Ready)
    writeToSd("");
}
void statusUpdate(String Message)
{
  if (_blClockSet)
  {
    Message = getDateTimeStamp(_rtc.now()) + "     " + Message;
  }
  
  Serial.println(Message);
  
  if (_blLogToSD && _blLogToSD_Ready)
    writeToSd(Message);
}
void writeToSd(String Message)
{
  File output = SD.open("20141010.txt", FILE_WRITE);
  
  if (output)
  {  
    output.println(Message);
    output.close();
  }
  else
  {
    Serial.println("  (save FAILED)");
  }
}



/***********************************************************************************************************/
/* RTC FUNCTIONS      **************************************************************************************/
/***********************************************************************************************************/
void setTimeFromPC()
{
  _rtc.adjust(DateTime(__DATE__, __TIME__));
  Serial.println("RTC clock has been set to the PC's time clock");
}

String getDateTimeStamp(DateTime Time)
{
  String dtStamp;
  int nYear = Time.year();
  int nMonth = Time.month();
  int nDay = Time.day();
  int nHour = Time.hour();
  int nMin = Time.minute();
  int nSec = Time.second();
  
  dtStamp = (String)nYear;
  dtStamp += "-";
  
  if (nMonth < 10)
    dtStamp += "0";
  dtStamp += (String)nMonth;
  dtStamp += "-";
  
  if (nDay < 10)
    dtStamp += "0";
  dtStamp += nDay;
  dtStamp += " ";
  
  dtStamp += nHour;
  dtStamp += ":";
  
  if (nMin < 10)
    dtStamp += "0";
  dtStamp += nMin;
  dtStamp += ":";
  
  if (nSec < 10)
    dtStamp += "0";
  dtStamp += nSec;
  
  return dtStamp;
}



/***********************************************************************************************************/
/* TEST                 ************************************************************************************/
/***********************************************************************************************************/
void notEvenCalled()
{
  statusUpdate("Hey, why is this breaking everything??");
  statusUpdate("I dunno, let's take it to the forums!");
  statusUpdate("If you comment out this line, and the line above, everything works again! Is it the size?");
}



/***********************************************************************************************************/
/* SETUP & LOOP       **************************************************************************************/
/***********************************************************************************************************/
void setup() 
{
  // turn on power LED
  pinMode(_pinPowerLED, OUTPUT);
  digitalWrite(_pinPowerLED, HIGH);
  
  // provide ground for rtc
  pinMode(_pinRtcGround, OUTPUT);
  digitalWrite(_pinRtcGround, LOW);
  
  // start the logging process
  Serial.begin(57600);
  statusUpdate("Initializing...");
  statusUpdate();
  
  // pause to allow for power supply
  // to stabilize completely and
  // for RTC to spin up
  delay(1500);
  
  statusUpdate("Device has been powered on or restarted...");  
  statusUpdate();
  
  // initialize the Wire.h protocol (analog pins 4/5)
  statusUpdate("Initializing the Wire.h protocol...");
  Wire.begin();
  
  // start the RTClib.h protocol
  statusUpdate("Initializing the RTClib.h protocal...");
  _rtc.begin();
  
  // initialize the clock
  statusUpdate("Initializing the Real Time Clock...");
  if (! _rtc.isrunning())
  {
    statusUpdate("The Real Time Clock was stopped, setting time...");
    setTimeFromPC();
  }
  else
  {
    if (_blResetClock)
    {
      statusUpdate("Real Time Clock required an update...");
      setTimeFromPC();
    }
  }
    
  
  statusUpdate("Initializing the SD Card...");
  pinMode(10, OUTPUT); // required for use of SD.h
  
  if (!SD.begin(4))
  {
    statusUpdate("  card failed or not present.");
    statusUpdate();
    return;
  }
  
  statusUpdate();
  statusUpdate("The Device is Ready!");
  statusUpdate();

  _blClockSet = true;
  _blLogToSD_Ready = true;
}

void loop() 
{
  statusUpdate((String)(millis() / 1000));
  delay(1000);
}

It seems as soon as my sketch gets above ~21,000 bytes, the SD write function starts failing.

No, it’s when you run out of SRAM that is happens.

void statusUpdate(String Message)
{
  if (_blClockSet)
  {
    Message = getDateTimeStamp(_rtc.now()) + "     " + Message;
  }
  
  Serial.println(Message);
  
  if (_blLogToSD && _blLogToSD_Ready)
    writeToSd(Message);
}

Quit pissing away resources on the String class.

    Serial.println("  (save FAILED)");

Use the F() macro:
Serial.println(F(" (save FAILED)"));

Thanks! So, I take it passing strings around in order to do some status logging is using the SRAM, and the SRAM apparently doesn't flush itself after the status update is finished? Should I be using char[] instead of String? I implemented the F macro update you suggested, which did reduce the sketch size to below 21,000, but all SD writes are still failing.

Read up on SRAM, totally makes sense why I'm failing. Suggestions on methods of logging status updates without using strings??

Read this:

http://gammon.com.au/progmem

In particular the tip about "You can actually do that to any form of outputting ...".

With a bit of rejigging you could make your statusUpdate function take F() macros. All those "status updates" (which are very wordy) are contributing quite a lot to your problem.

That's awesome, thanks Nick!!

Just to show how you can get rid of String in your outputting routines. Change to:

void statusUpdate(const char * Message)
{
  if (_blClockSet)
    {
    Serial.print (getDateTimeStamp(_rtc.now()));
    Serial.print ("     ");
    }
  
  Serial.println(Message);
  
  if (_blLogToSD && _blLogToSD_Ready)
    writeToSd(Message);
}
void writeToSd(const char * Message)
{
  File output = SD.open("20141010.txt", FILE_WRITE);
  
  if (output)
  {  
    output.println(Message);
    output.close();
  }
  else
  {
    Serial.println("  (save FAILED)");
  }
}

Right, so that's String gone. Except for loop() which we change to:

void loop() 
{
  char buf [10];
  sprintf (buf, "%i", (millis() / 1000));
  statusUpdate(buf);
  delay(1000);
}

As for the F() macro. I really think you might just as easily cut down the wordy messages.

  statusUpdate("Device has been powered on or restarted...");  
  statusUpdate();
  
  // initialize the Wire.h protocol (analog pins 4/5)
  statusUpdate("Initializing the Wire.h protocol...");
  Wire.begin();
  
  // start the RTClib.h protocol
  statusUpdate("Initializing the RTClib.h protocal...");
  _rtc.begin();
  
  // initialize the clock
  statusUpdate("Initializing the Real Time Clock...");
  if (! _rtc.isrunning())
  {
    statusUpdate("The Real Time Clock was stopped, setting time...");
    setTimeFromPC();
  }
  else
  {
    if (_blResetClock)
    {
      statusUpdate("Real Time Clock required an update...");
      setTimeFromPC();
    }
  }

You are writing for a microprocessor, not Windows 8. Cut them down to "A", "B", etc. if you really must echo every last thing you are doing.