Time from DS3231 into String to save via SD-lib

Hello all,
I am trying to log analog readings with a timestamp from a DS3231 to an SD card. I know how to handle the DS3231 to display time on LCDs or Seria monitor.
I did not achieve my goal it with the SDfat-lib due to compiler errors, and I remember that it is the reason why I kept the IDE 0023 on my computer, because there it works (actually, I use the DS1307 there, but SDfat example code is apparently not working with IDEs > 1.0).
The standard SD lib example code (“Datalogger”) works fine. So does the RTC. I tried to build a string from the time readings like this:

get3231Date();
  String timestringOne = String((hours, DEC) + ":");
  String timestringTwo = String(timestringOne + (minutes, DEC) + ":");
  String timestringThree = String(timestringTwo + (seconds, DEC) + ",");

  String dataString = "";
  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ","; 
    }
  }
  String semifinal = String(timestringThree + dataString + ",h2o");
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(semifinal);
    dataFile.close();
    // print to the serial port too:
    Serial.println(semifinal);
  }

The timestamp I am getting reads: “10:10,analogvalue0…”. Why is that? Leaving out the “, DEC” does not help, even though it is ok if I print the values to an LCD. Declaring an intermediate variable like

int std = hours;

and then putting that one to the string doesn’t do the trick either. Also, separating the

+ ":");

two an own codeline is not helping, that seems to a valid concatenation.
I do not see why it doesn’t want to work. Any help is apprechiated. Thank you very much!


Some more details on the project, which is of a molecular biology nature:
MEGA 2560, IDE 1.5.2
MCP23008 to control valves via I2C
20x4 Display
DS3231 RTC via I2C
MMA7361 3-axis g-sensor
two servos for pumping and actuation
DS18B20 temperature sensor
The MCU controls a device to isolate mRNA on magnetic beads in a tube. Sample solution containing mRNA and beads is pumped into the tube via servo 1 and a syringe. A magnet moved by the second servo captures the beads on the tube’s wall. Several cleansing steps are performed by pumping the syringe back and forth and switching LEE valves via MCP23008. The entire procedure runs during microgravity on a parabolic flight and starts after arming when the freefall pin goes HIGH (manual override switch is there as well). Even though the primary use of the MCU is controling the steps datalogging is desirable. It should give X, Y, Z acceleration and what procedure is taking place (check, hence the “h2o” in the code). But a timestamp would be nice as well, wouldn’t it?! After the last cleansing step the magnet swings away and the sample is flushed into a vial. µg should be over by now and a thorough cleansing starts as soon as the arming switch is turned of.

I tried to build a string from the time readings like this:

Why? Do you type whole sentences at once, or do you type individual letters? Write the values to the file ONE AT A TIME. It takes no longer and wastes far fewer resources.

String((hours, DEC)

Nonsense. Complete and utter nonsense. Just because one function (Serial.print()) knows what to do with , DEC does not mean that every function does. So, don't pretend that they all do.

  String timestringOne = String((hours, DEC) + ":");
  String timestringTwo = String(timestringOne + (minutes, DEC) + ":");
  String timestringThree = String(timestringTwo + (seconds, DEC) + ",");

Where are hours, minutes, and seconds declared?


Please note that in versions of the IDE up to and including 1.0.3, the String library has bugs as discussed here and here.

In particular, the dynamic memory allocation used by the String class may fail and cause random crashes.

I recommend reworking your code to manage without String. Use C-style strings instead (strcpy, strcat, strcmp, etc.), as described here for example.

Alternatively, install the fix described here: Fixing String Crashes

Preferably upgrade your IDE to version 1.0.4 or above at: http://arduino.cc/en/Main/Software


Maybe sprintf?


(minutes, DEC)

DEC is 10, and you are using (without realizing it) the "comma" operator.

For example:

int a = (4,5,6,7);

The variable "a" will be 7 in this example.

http://en.wikipedia.org/wiki/Comma_operator

Thank you very much, Nick. Sorry for keeping the posted code short, here is it all:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <Servo.h>
#include <SPI.h>
#include <SD.h>

const int chipSelect = 53;

byte seconds, minutes, hours, day, date, month, year;
char weekDay[4];

byte tMSB, tLSB;
float temp3231;

int led = 5;

//Uhr

#define DS3231_I2C_ADDRESS 104
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

void watchConsole()
{
  if (Serial.available()) {      // Look for char in serial queue and process if found
    if (Serial.read() == 84) {      //If command = "T" Set Date
      set3231Date();
      get3231Date();
      Serial.println(" ");
    }
  }
}

void set3231Date()
{
  //T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year)
  //T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99)
  //Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209

  seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result.  
  minutes = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  hours   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  day     = (byte) (Serial.read() - 48);
  date    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  month   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  year    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x00);
  Wire.write(decToBcd(seconds));
  Wire.write(decToBcd(minutes));
  Wire.write(decToBcd(hours));
  Wire.write(decToBcd(day));
  Wire.write(decToBcd(date));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}


void get3231Date()
{
  // send request to receive data starting at register 0
  Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
  Wire.write(0x00); // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes

  if(Wire.available()) {
    seconds = Wire.read(); // get seconds
    minutes = Wire.read(); // get minutes
    hours   = Wire.read();   // get hours
    day     = Wire.read();
    date    = Wire.read();
    month   = Wire.read(); //temp month
    year    = Wire.read();

    seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111)); // convert BCD to decimal
    minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111)); // convert BCD to decimal
    hours   = (((hours & B00110000)>>4)*10 + (hours & B00001111)); // convert BCD to decimal (assume 24 hour mode)
    day     = (day & B00000111); // 1-7
    date    = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31
    month   = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow
    year    = (((year & B11110000)>>4)*10 + (year & B00001111));
  }
  else {
    //oh noes, no data!
  }

  switch (day) {
  case 1:
    strcpy(weekDay, "Sun");
    break;
  case 2:
    strcpy(weekDay, "Mon");
    break;
  case 3:
    strcpy(weekDay, "Tue");
    break;
  case 4:
    strcpy(weekDay, "Wed");
    break;
  case 5:
    strcpy(weekDay, "Thu");
    break;
  case 6:
    strcpy(weekDay, "Fri");
    break;
  case 7:
    strcpy(weekDay, "Sat");
    break;
  }

}

float get3231Temp()
{
  //temp registers (11h-12h) get updated automatically every 64s
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x11);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 2);

  if(Wire.available()) {
    tMSB = Wire.read(); //2's complement int portion
    tLSB = Wire.read(); //fraction portion

    temp3231 = (tMSB & B01111111); //do 2's math on Tmsb
    temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8
  }
  else {
    //oh noes, no data!
  }

  return temp3231;
}




void setup() {
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(5, OUTPUT);
  pinMode(53, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
}
  
}

void loop() {
  get3231Date();
  String timestringOne = String((hours, DEC) + ":");
  String timestringTwo = String(timestringOne + (minutes, DEC) + ":");
  String timestringThree = String(timestringTwo + (seconds, DEC) + ",");

  String dataString = "";
  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ","; 
    }
  }
  String semifinal = String(timestringThree + dataString + ",h2o");
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(semifinal);
    dataFile.close();
    // print to the serial port too:
    Serial.println(semifinal);
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  } 
  digitalWrite(led, HIGH);
  delay(250);
  digitalWrite(led, LOW);
  delay(1750);
}

I copied the code parts together and checked that everything is there - it is. But I probably will get into the sprintf anyway.
Paul, sorry for going on “pretending” stuff like using an LCD and a servo. I hope it will not spoil your mood further.

    if (Serial.read() == 84) {      //If command = "T" Set Date

Keep it simple:

    if (Serial.read() == 'T') {      //If command = "T" Set Date

  Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes

  if(Wire.available()) {

Should be:

  if (Wire.requestFrom(DS3231_I2C_ADDRESS, 7) == 7) {

Similarly in get3231Temp.

http://www.gammon.com.au/i2c


You still have to fix this:

  String timestringOne = String((hours, DEC) + ":");
  String timestringTwo = String(timestringOne + (minutes, DEC) + ":");
  String timestringThree = String(timestringTwo + (seconds, DEC) + ",");

The solution to this problem is embarrassingly simple... Just convert the date to integer format, individually dealing with each field. Then convert to String. You can then as the strings to make any desired output and it works in the SD library function

Then convert to String.

Completely unnecessary!