Issues with Strings

Hi all,

I'm developing a program to monitor several digital sensor inputs and to datalog to an SD card when a trip is detected.

  • Monitor digital inputs & make a flag if any input is not equal to a pre-determined 'normal' state
  • Retrieve date/time info from DS1307 RTC
  • Monitor duration of 'trip' using millis()
  • Once sensor has returned to it's normal state, write the following information to the SD Card:
  • Date & Time of trip
  • Which sensor it was
  • Duration of trip

Obviously, the Date/Time has to be collected as soon as a trip is detected along with which sensor it was, however the duration cannot be calculated until the trip has finished.

I've been playing with this for a little over 2 weeks now with limited success, originally I had issues with writing to the SD card (which works fine using the 1,2,3 example sketch) and have now given up and started from scratch.

My approach now is to compile strings which can be sent to the Serial Monitor as and when they occur, but can also be concatened together for sending to the SD card when the cycle is complete.

Using the Serial Monitor, it is clear that the strings are not being constructed correctly and remain as initialised ("").

I've attached my code below, i'm aware that there are gaps in it in other areas however this is a bare minimum for me to debug... would appreciate any feedback regarding my string construction issue.

As a side note, i've noticed that this uses an awful lot of memory (only just fits on a UNO and causes the Serial Monitor to display gibberish).

{ I HAVE REMOVED ALL THE CODE HERE WHICH SETS UP PIN SELECTION, ETC TO FIT IN THE POST }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////
// *** PROGRAM VARIABLES *** //
///////////////////////////////

int sensor1_flag = 0;
int active_sensor = 0;
int timer_started = 0;
int write_enabled = 0;
unsigned long last_duration = 0;
unsigned long last_trip = 0;
String stringDateTime = "";
String stringSensor = "";
String stringDuration = "";
String dataString = "";

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
#include <SPI.h>
#include <SD.h>
File Datalog;

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
return ( (val/16*10) + (val%16) );
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Setup Code

void setup() {

pinMode(sensor1_pin, INPUT);
pinMode(chipSelect, OUTPUT);

Serial.begin(9600);
delay(500);
Serial.println("System Startup...");
Serial.print("Initialising RTC... ");
Wire.begin();
Serial.println("Done.");
Serial.print("Requesting current Date/Time from RTC... ");
updateTime();
Serial.println("Successful.");

second = 30;
minute = 21;
hour = 16;
dayOfWeek = 3;
dayOfMonth = 9;
month = 3;
year = 16;   // 2 digit year

// Sets the DS1307 time: second, minute, hour, dayofweek, dayofmonth, month, year...
// setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);

Serial.print("Initialising SD Card... ");
if (!SD.begin(chipSelect)) {
  Serial.println("Initialisation failed!");
  Serial.println("------------------------------------------------------------------------------------------");
  return;
}
Serial.println("Successful.");
Serial.println("------------------------------------------------------------------------------------------");

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Sets the Date & Time

void setDateDs1307(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.write(decToBcd(second)); // 0 to bit 7 starts the clock
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour)); // If you want 12 hour am/pm you need to set 
// bit 6 (also need to change readDateDs1307)
Wire.write(decToBcd(dayOfWeek));
Wire.write(decToBcd(dayOfMonth));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.endTransmission();
delay(250);
Serial.println("Real Time Clock updated successfully.");
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Requests the Date & Time

void getDateDs1307(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year)
{
// Reset the register pointer
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();

Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

// A few of these need masks because certain bits are control bits
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f); // Need to change this if 12 hour am/pm
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void updateTime()
{ 
  
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  // retrieve data from RTC
  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
  &year);
      
// DD/MM/YY
  String Day = String(dayOfMonth, DEC);
  String Month = String(month, DEC);
  String Year = String(year, DEC);

// HH:MM:SS
  String Hour = String(hour, DEC);
  String Minute = String(minute, DEC);
  String Second = String(second, DEC);

  stringDateTime =  String(Day + "/" + Month + "/" + Year + " " + Hour + ":" + Minute + ":" + Second + " ");

}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop() {
  
                    // *** COMPARE INPUTS WITH THEIR NORMAL STATE & MAKE FLAG IF NOT *** //

sensor1_flag = digitalRead(sensor1_pin);

                    // *** SERIAL COMMS *** THIS SECTION CAN BE REMOVED ONCE PROVEN WORKING OK *** //

if (sensor1_flag == 1 && active_sensor == 0) {
  updateTime();
  last_trip = millis();
  timer_started = 1;
  active_sensor = 1;
}

if (active_sensor == 1 && sensor1_flag == 0 & active_sensor != 0) {
  last_duration = (millis() - last_trip);
  timer_started = 0;
  active_sensor = 0;
  write_enabled = 1;
}

  stringDuration =  String(last_duration + "ms");
  
                    // *** ASSEMBLE STRING FOR WRITING TO SD CARD *** //
                    
  switch (active_sensor) {
    case 1: stringSensor =  String(sensor1 + " ");
    break;
    case 2: stringSensor =  String(sensor2 + " ");
    break;
    case 3: stringSensor =  String(sensor3 + " ");
    break;
    case 4: stringSensor =  String(sensor4 + " ");
    break;
    case 5: stringSensor =  String(sensor5 + " ");
    break;
    case 6: stringSensor =  String(sensor6 + " ");
    break;
    case 7: stringSensor =  String(sensor7 + " ");
    break;
    case 8: stringSensor =  String(sensor8 + " ");
    break;
    case 9: stringSensor =  String(sensor9 + " ");
    break; 
  }
  
  dataString =  String(stringDateTime + stringSensor + stringDuration);
  
                    // *** SD CARD COMMS *** //

if (write_enabled == 1) {
Datalog = SD.open("Datalog.txt", FILE_WRITE);
if (Datalog) {
  Datalog.println(dataString);
  Datalog.close();
} else Serial.println("Error communicating with SD Card!");
write_enabled = 0;
}

// *** TEMPORARY DEBUGGING CODE *** //

Serial.println(stringDateTime);
Serial.println(stringSensor);
Serial.println(stringDuration);
Serial.println(dataString);

delay(5000);


}

Thanks in advance.

If you use the F macro for all your fixed character strings, you can save data memory (at the cost of code memory). Change e.g.

  Serial.print("Initialising SD Card... ");

to

  Serial.print(F("Initialising SD Card... "));

Do this for all your Serial print statements.

If your code does not fit, please attach it. It's a bit of a mission to figure out what needs to be what.

PS
If Serial.print prints garbage, it's usually due to memory problems. It can be anything but String (with capital S) might very well be the culprit. Use of character arrays might solve the issues.

You will surely be better off using sprintf, anything is better than String, but why just not send the data direct to SD?

Thanks, I've updated below as suggested. My concern is the correct construction of the strings themselves rather than the memory usage for the moment.

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////
// *** PROGRAM VARIABLES *** //
///////////////////////////////

int sensor1_flag = 0;
int active_sensor = 0;
int timer_started = 0;
int write_enabled = 0;
unsigned long last_duration = 0;
unsigned long last_trip = 0;
String stringDateTime = "";
String stringSensor = "";
String stringDuration = "";
String dataString = "";

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
#include <SPI.h>
#include <SD.h>
File Datalog;

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
return ( (val/16*10) + (val%16) );
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Setup Code

void setup() {

pinMode(sensor1_pin, INPUT);
pinMode(chipSelect, OUTPUT);

Serial.begin(9600);
delay(500);
Serial.println(F("System Startup..."));
Serial.print(F("Initialising RTC... "));
Wire.begin();
Serial.println(F("Done."));
Serial.print(F("Requesting current Date/Time from RTC... "));
updateTime();
Serial.println(F("Successful."));

second = 30;
minute = 21;
hour = 16;
dayOfWeek = 3;
dayOfMonth = 9;
month = 3;
year = 16;   // 2 digit year

// Sets the DS1307 time: second, minute, hour, dayofweek, dayofmonth, month, year...
// setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);

Serial.print(F("Initialising SD Card... "));
if (!SD.begin(chipSelect)) {
  Serial.println(F("Initialisation failed!"));
  Serial.println(F("------------------------------------------------------------------------------------------"));
  return;
}
Serial.println(F("Successful."));
Serial.println(F("------------------------------------------------------------------------------------------"));

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Sets the Date & Time

void setDateDs1307(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.write(decToBcd(second)); // 0 to bit 7 starts the clock
Wire.write(decToBcd(minute));
Wire.write(decToBcd(hour)); // If you want 12 hour am/pm you need to set 
// bit 6 (also need to change readDateDs1307)
Wire.write(decToBcd(dayOfWeek));
Wire.write(decToBcd(dayOfMonth));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.endTransmission();
delay(250);
Serial.println("Real Time Clock updated successfully.");
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Requests the Date & Time

void getDateDs1307(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year)
{
// Reset the register pointer
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0);
Wire.endTransmission();

Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

// A few of these need masks because certain bits are control bits
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f); // Need to change this if 12 hour am/pm
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void updateTime()
{ 
  
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  // retrieve data from RTC
  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
  &year);
      
// DD/MM/YY
  String Day = String(dayOfMonth, DEC);
  String Month = String(month, DEC);
  String Year = String(year, DEC);

// HH:MM:SS
  String Hour = String(hour, DEC);
  String Minute = String(minute, DEC);
  String Second = String(second, DEC);

  stringDateTime =  String(Day + "/" + Month + "/" + Year + " " + Hour + ":" + Minute + ":" + Second + " ");

}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop() {
  
                    // *** COMPARE INPUTS WITH THEIR NORMAL STATE & MAKE FLAG IF NOT *** //

sensor1_flag = digitalRead(sensor1_pin);

                    // *** SERIAL COMMS *** THIS SECTION CAN BE REMOVED ONCE PROVEN WORKING OK *** //

if (sensor1_flag == 1 && active_sensor == 0) {
  updateTime();
  last_trip = millis();
  timer_started = 1;
  active_sensor = 1;
}

if (active_sensor == 1 && sensor1_flag == 0 & active_sensor != 0) {
  last_duration = (millis() - last_trip);
  timer_started = 0;
  active_sensor = 0;
  write_enabled = 1;
}

  stringDuration =  String(last_duration + "ms");
  
                    // *** ASSEMBLE STRING FOR WRITING TO SD CARD *** //
                    
  switch (active_sensor) {
    case 1: stringSensor =  String(sensor1 + " ");
    break;
    case 2: stringSensor =  String(sensor2 + " ");
    break;
    case 3: stringSensor =  String(sensor3 + " ");
    break;
    case 4: stringSensor =  String(sensor4 + " ");
    break;
    case 5: stringSensor =  String(sensor5 + " ");
    break;
    case 6: stringSensor =  String(sensor6 + " ");
    break;
    case 7: stringSensor =  String(sensor7 + " ");
    break;
    case 8: stringSensor =  String(sensor8 + " ");
    break;
    case 9: stringSensor =  String(sensor9 + " ");
    break; 
  }
  
  dataString =  String(stringDateTime + stringSensor + stringDuration);
  
                    // *** SD CARD COMMS *** //

if (write_enabled == 1) {
Datalog = SD.open("Datalog.txt", FILE_WRITE);
if (Datalog) {
  Datalog.println(dataString);
  Datalog.close();
} else Serial.println(F("Error communicating with SD Card!"));
write_enabled = 0;
}

// *** TEMPORARY DEBUGGING CODE *** //

Serial.println(stringDateTime);
Serial.println(stringSensor);
Serial.println(stringDuration);
Serial.println(dataString);

delay(5000);


}

Can you please attach (as attachment or over two or three posts) a complete copy of the code as it currently is? We still don't have any idea what type of variable sensor1, sensor2 etc is.

And memory issues are a concern. If memory is nearly full and you try to allocate a String (e.g. Day, Month etc in your updateTime), it might well not be created correctly if you are out of memory.

This would be a modified version of updateTime(). It does not append a space at the end as that can be added when it's printed / written.

// below variable replaces stringDateTime in the original code
// assumes a 2-digit year
char strDateTime[18];

void updateTime()
{

  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  // retrieve data from RTC
  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month,
                &year);

  // place formatted date/time in strDateTime; assumes that year is 2-digit max
  sprintf(strDateTime, "%02d/%02d/%02d %02d:%02d:%02d", dayOfMonth, month, year, hour, minute, second);

You can use snprintf instead of sprintf to harden the code against buffer overflows if any of the variables would be more than two digits.