String variable sometimes empty.

I have a program that read temp and time and store that variables in a CSV file. Before saving it, I put all the variables together in a single variable. But sometimes something goes wrong and the variable remains empty, so in the file the line is empty too.
Here is the sketch:

#include <SPI.h>
#include "RF24.h"
#include "DHT.h"
#include <SD.h>
#include <Wire.h>  
//#include "SSD1306.h"
//#include "OLEDDisplayUi.h"
#include <TimeLib.h>
#include <DS1307RTC.h>

// DEBUG
//#define DEBUG // Enable debug
#include "DebugUtils.h"

// SD
// .csv: add comma (",") to separate different data (each one in a column), printLN to new row
const int chipSelect = 4;
String current_year = "";
String current_month = "";
String stringData = "";
String fileName = "";
String time = "";
int contNode[6] = {4,4,4,4,4,4}; // Write on SD every 60 minute (every 6 "cont")

float temp, hum;
// Data struct that contains Temp, Hum and the number of the node
struct data {
	float Temp;
	float Hum;
	int node = 4; // Not store in the addresses variable
} TandH;

unsigned long previousMillis = 0; // will store last time DHT was read
const long interval = 600000; // read after 10 minute
unsigned long currentMillis; // Store current intervall


void setup() {
	Serial.begin(9600);
	while (!Serial); // wait until Arduino Serial Monitor opens

	// Start SD library
	// see if the card is present and can be initialized:
	if (!SD.begin(chipSelect)) {
		SERIAL_PRINTLN("WARNING: card initialization failed, or not present");
	}
	SERIAL_PRINTLN("card initialized.");

	// Start TIME library
	setSyncProvider(RTC.get);   // the function to get the time from the RTC
	if(timeStatus()!= timeSet) 
		SERIAL_PRINTLN("Unable to sync with the RTC");
	else
		SERIAL_PRINTLN("RTC has set the system time");

	// Update current year and mounth variable
	// WARNING: the variables have String type, but the function return int variable
	current_year = String(year());
	//SERIAL_PRINTLN("The current year is: " + current_year);
	current_month = monthStr(month()); // WARNING: not sure if it is needed


	SERIAL_PRINTLN("Boot OK");
	SERIAL_PRINTLN("NODE: cameraPC");

}

void loop() {

	readDHT(0);
	delay(5000);
}

// Write the data on the SD card every hour
void write(float temp, float hum, int node) {
	contNode[node]++; // Increment the conunt
	// check if is passed an hour
	if (contNode[node] == 5) {
	contNode[node] = 0; // Reset the variable
	// Check if we need a new file o folder
	//SERIAL_PRINTLN("Checking if the current year is changed...");
	if (String(year()) != current_year) {
		//SERIAL_PRINTLN("Current year is changed, updating the variable...");
		current_year = String(year()); // Update the variable
		//SERIAL_PRINTLN("Variable update succesfully: " + current_year);
		String path = "/" + current_year; // WARNING: not sure what this line do...
		SD.mkdir(current_year); // Create a new folder with the name of the year
		SERIAL_PRINTLN("New folder created");
	}
	
	fileName = "/" + current_year + "/" + monthStr(month()) + ".csv";
	File dataFile = SD.open(fileName, FILE_WRITE); // Open the file
	// Check if the file opened properly
	if (dataFile) {
		//SERIAL_PRINT("Opening the file in path: ");
		//SERIAL_PRINTLN(fileName);
		SERIAL_PRINTLN("The file opened correctly!");
		time = String(hour()) + ":" + String(minute()) + ":" + String(second()); // Get the time
		stringData = String(day()) + "," + time + "," + String(temp) + "," + String(hum) + "," + String(node); // .csv formatting  + "," + String(error)
		dataFile.println(stringData); // Write on SD
		SERIAL_PRINT("Data wrote on SD: ");
		SERIAL_PRINTLN(stringData); // Write on serial monitor
		dataFile.close();
	}
	// if the file isn't open, pop up an error:
	else {
		SERIAL_PRINTLN("WARNING: error opening the file!");
	}
	}
	
}


void readDHT(int address){
	// Read sensor value
	int error = 0;

	do {
	delay(2000);
	temp = random(0,100);
	hum = random(0,100);
	error++;
	} while (temp == 0 || hum == 0 && error <= 15);

	if(error >= 15){
		SERIAL_PRINTLN("Error while reading, NOT READ");
	}

	// Not send the value, this is the master
	// Writing it on SD, with the code of the reading module
	// cameraPC is not present in the address, the next number however is 4
	write(temp, hum, 4);
	SERIAL_PRINT("Data read = ");
	SERIAL_PRINT(temp);
	SERIAL_PRINT(" ");
	SERIAL_PRINTLN(hum);
}

And this is the serial output:

card initialized.
RTC has set the system time
Boot OK
NODE: cameraPC
The file opened correctly!
Data wrote on SD: 3,17:19:48,7.00,49.00,4
Data read = 7.00 49.00
Data read = 73.00 58.00
Data read = 30.00 72.00
Data read = 44.00 78.00
Data read = 23.00 9.00
The file opened correctly!
Data wrote on SD: 
Data read = 40.00 65.00
Data read = 92.00 42.00
Data read = 87.00 3.00
Data read = 27.00 29.00
Data read = 40.00 12.00
The file opened correctly!
Data wrote on SD: 3,17:20:58,3.00,69.00,4
Data read = 3.00 69.00
Data read = 9.00 57.00
Data read = 60.00 33.00
Data read = 99.00 78.00
Data read = 16.00 35.00
The file opened correctly!
Data wrote on SD: 
Data read = 97.00 26.00
Data read = 12.00 67.00
Data read = 10.00 33.00
Data read = 79.00 49.00
Data read = 79.00 21.00
The file opened correctly!
Data wrote on SD: 3,17:22:8,67.00,72.00,4
Data read = 67.00 72.00
Data read = 93.00 36.00
Data read = 85.00 45.00
Data read = 28.00 91.00
Data read = 94.00 57.00
The file opened correctly!
Data wrote on SD: 3,17:22:43,1.00,53.00,4
Data read = 1.00 53.00
Data read = 8.00 44.00
Data read = 68.00 90.00
Data read = 24.00 96.00
Data read = 30.00 3.00
The file opened correctly!
Data wrote on SD: 
Data read = 22.00 66.00
Data read = 49.00 24.00
Data read = 1.00 53.00
Data read = 77.00 8.00
Data read = 28.00 33.00
The file opened correctly!
Data wrote on SD: 3,17:23:53,98.00,81.00,4
Data read = 98.00 81.00
Data read = 35.00 13.00
Data read = 65.00 14.00
Data read = 63.00 36.00
Data read = 25.00 69.00
The file opened correctly!
Data wrote on SD: 3,17:24:28,15.00,94.00,4
Data read = 15.00 94.00
Data read = 29.00 1.00
Data read = 17.00 95.00
Data read = 5.00 4.00
Data read = 51.00 98.00
The file opened correctly!
Data wrote on SD: 3,17:25:4,88.00,23.00,4
Data read = 88.00 23.00
Data read = 5.00 82.00
Data read = 52.00 66.00
Data read = 16.00 37.00
Data read = 38.00 44.00
The file opened correctly!
Data wrote on SD: 3,17:25:39,1.00,97.00,4
Data read = 1.00 97.00
Data read = 71.00 28.00
Data read = 37.00 58.00
Data read = 77.00 97.00
Data read = 94.00 4.00
The file opened correctly!
Data wrote on SD: 3,17:26:14,9.00,31.00,4
Data read = 9.00 31.00
Data read = 45.00 75.00
Data read = 35.00 98.00
Data read = 42.00 99.00
Data read = 68.00 12.00
The file opened correctly!
Data wrote on SD: 3,17:26:49,60.00,57.00,4
Data read = 60.00 57.00
Data read = 94.00 8.00
Data read = 95.00 68.00
Data read = 13.00 30.00
Data read = 6.00 62.00
The file opened correctly!
Data wrote on SD: 3,17:27:24,42.00,65.00,4
Data read = 42.00 65.00
Data read = 82.00 52.00
Data read = 67.00 21.00
Data read = 95.00 12.00
Data read = 71.00 1.00
The file opened correctly!
Data wrote on SD: 3,17:27:59,90.00,31.00,4
Data read = 90.00 31.00
Data read = 38.00 57.00
Data read = 16.00 90.00
Data read = 40.00 79.00
Data read = 35.00 6.00
The file opened correctly!
Data wrote on SD: 3,17:28:34,72.00,98.00,4
Data read = 72.00 98.00
Data read = 95.00 19.00
Data read = 54.00 23.00
Data read = 89.00 60.00
Data read = 5.00 26.00
The file opened correctly!
Data wrote on SD: 3,17:29:9,23.00,6.00,4
Data read = 23.00 6.00
Data read = 13.00 70.00
Data read = 38.00 94.00
Data read = 20.00 44.00
Data read = 66.00 34.00
The file opened correctly!
Data wrote on SD: 3,17:29:44,26.00,94.00,4
Data read = 26.00 94.00
Data read = 63.00 38.00
Data read = 44.00 90.00
Data read = 50.00 59.00
Data read = 23.00 47.00
The file opened correctly!
Data wrote on SD: 3,17:30:19,85.00,17.00,4
Data read = 85.00 17.00
Data read = 72.00 39.00
Data read = 47.00 85.00
Data read = 96.00 85.00
Data read = 23.00 20.00
The file opened correctly!

Any suggestion?
Many thanks!

Well the real good suggestion is don't do that and don't use the String class... just use char buffers and write data to your file one variable at a time, you don't need your intermediary fully concatenated String...

just use char buffers and write data to your file one variable at a time

+1

To build the filename:

    char fileName[16];
    filename[0] = '/'; // start with a slash, before the filename
    strcpy( &filename[1], monthStr(month()) ); // copy the month string after the slash
    strcat( &filename[1], ".csv" );            // append the extension

To write the pieces:

      dataFile.print( day() )
      dataFile.print( ',' );
      dataFile.print( hour() );
      dataFile.print( ':' );
      dataFile.print( minute() );
      dataFile.print( ':' );
      dataFile.print( second() );
      dataFile.print( ',' );
      dataFile.print( temp );
      dataFile.print( ',' );
      dataFile.print( hum );
      dataFile.print( ',' );
      dataFile.print( node );
      // dataFile.print( ',' );
      // dataFile.print( error );
      dataFile.println();

Official docs here, lots of C string tutorials available elsewhere, or just ask. :)

Usually that means that the String class failed to allocate a sufficiently sized block of memory (either due to being out of RAM, or more frequently, due to memory fragmentation resulting in there being no contiguous blocks of free heap memory long enough for the string.

Doing things like

String foo = stringA + ":" + stringB + " " + stringC;

is particularly bad, because it ends up allocating and deallocating a whole bunch of intermediate strings there, resulting in fragmentation of the heap.

This is why we tell people not to use String.

Well the real good suggestion is don’t do that and don’t use the String class… just use char buffers and write data to your file one variable at a time, you don’t need your intermediary fully concatenated String…

I tried to use String class just because I’m a bit more confident with it. Thanks for the advice, I’ll change to char.

To build the filename:

Code: [Select]
char fileName[16];
filename[0] = ‘/’; // start with a slash, before the filename
strcpy( &filename[1], monthStr(month()) ); // copy the month string after the slash
strcat( &filename[1], “.csv” ); // append the extension

As I said before, I’m not good with the char class. I know that probably with a copy&paste all works fine, but I want to understand what this line of code actually do. If you can explain it, in particulary the strcat and strcpy.

Usually that means that the String class failed to allocate a sufficiently sized block of memory (either due to being out of RAM, or more frequently, due to memory fragmentation resulting in there being no contiguous blocks of free heap memory long enough for the string.

Many many thanks! Now I understand what is the problem, also because before uploading the Arduino IDE says that there is a low memory and then stability error could occur. I simply don’t understand why with a slightly modified version (keeping the same amount of memory occupated) all worked well… Anyway, I will try with char.

Just another question, this is half the final code, and my UNO is already out of memory. Can someone advise my an improved board with more memory? The only request is that is must be cheap (10€ about), so no Arduino 101 or Arduino Zero.

Many thanks to all!

Hello

good attitude not to be willing to blindly copy & paste! +1 karma for this :)

have a look at the c-string (working on char arrays terminated by a null character) functions

--> stdlib.h --> string.h

[url=http://www.cplusplus.com/reference/cstring/strcpy/]strcpy()[/url] will copy one null terminated c-string into another one

[url=http://www.cplusplus.com/reference/cstring/strcat/]strcat()[/url] adds at the end of a null terminated c-string another one and properly terminates the whole thing with a null character

In both cases make sure you know there is enough space in the target buffer including for the non visible end of string null character mark before calling that function or you will overflow and go write to the next memory bytes and possibly trash other variables (some other functions to know about: strlen() gives you the size of a cstring and sizeof()will give you the number of bytes in an array for example)

|500x225

Many thanks!
So, a char class is an array of characters. When I declare it I must say the type and the name like always, but I have to specify the max number of characters that it can contain (ES: char pathLocation[80]).
strcpy(a,b) simply copy the “b” array of character in the “a” array, starting from the 0 position if it does not specify in this way: a[23]. Also, after having copied the whole array the function add a “null character” at the and.
strcat(a,b) copy the “b” array in the “a” one, but this time it will start from the “null character” (removing it before starting to copy).
Is it all right? And there is something else to know about the char class?

I also figured out what is the line of code that made the String empty and I want to understand why this line influence the String. Don’t worry, I’ll not come back to String, I’m just a bit curious.
The code that don’t work well:

#include <SPI.h>
#include "RF24.h"
#include "DHT.h"
#include <SD.h>
#include <Wire.h>  
//#include "SSD1306.h"
//#include "OLEDDisplayUi.h"
#include <TimeLib.h>
#include <DS1307RTC.h>

// DEBUG
#define DEBUG // Enable debug
#include "DebugUtils.h"

// SD
// .csv: add comma (",") to separate different data (each one in a column), printLN to new row
const int chipSelect = 4;
String current_year = "";
String current_month = "";
String stringData = "";
String fileName = "";
String time = "";
int contNode[6] = {4,4,4,4,4,4}; // Write on SD every 60 minute (every 6 "cont")

float temp, hum;
// Data struct that contains Temp, Hum and the number of the node
struct data {
	float Temp;
	float Hum;
	int node = 4; // Not store in the addresses variable
} TandH;

unsigned long previousMillis = 0; // will store last time DHT was read
const long interval = 600000; // read after 10 minute
unsigned long currentMillis; // Store current intervall


void setup() {
	Serial.begin(9600);
	while (!Serial); // wait until Arduino Serial Monitor opens

	// Start SD library
	// see if the card is present and can be initialized:
	if (!SD.begin(chipSelect)) {
		SERIAL_PRINTLN("WARNING: card initialization failed, or not present");
	}
	SERIAL_PRINTLN("card initialized.");

	// Start TIME library
	setSyncProvider(RTC.get);   // the function to get the time from the RTC
	if(timeStatus()!= timeSet) 
		SERIAL_PRINTLN("Unable to sync with the RTC");
	else
		SERIAL_PRINTLN("RTC has set the system time");

	// Update current year and mounth variable
	// WARNING: the variables have String type, but the function return int variable
	current_year = String(year());
	//SERIAL_PRINTLN("The current year is: " + current_year);
	current_month = monthStr(month()); // WARNING: not sure if it is needed


	SERIAL_PRINTLN("Boot OK");
	SERIAL_PRINTLN("NODE: cameraPC");

}

void loop() {

	readDHT(0);
	delay(1000);
}

// Write the data on the SD card every hour
void write(float temp, float hum, int node) {
	contNode[node]++; // Increment the conunt
	// check if is passed an hour
	if (contNode[node] == 5) {
	contNode[node] = 0; // Reset the variable
	// Check if we need a new file o folder
	//SERIAL_PRINTLN("Checking if the current year is changed...");
	if (String(year()) != current_year) {
		//SERIAL_PRINTLN("Current year is changed, updating the variable...");
		current_year = String(year()); // Update the variable
		//SERIAL_PRINTLN("Variable update succesfully: " + current_year);
		String path = "/" + current_year; // WARNING: not sure what this line do...
		SD.mkdir(current_year); // Create a new folder with the name of the year
		SERIAL_PRINTLN("New folder created");
	}
	
	fileName = "/" + current_year + "/" + monthStr(month()) + ".csv";
	File dataFile = SD.open(fileName, FILE_WRITE); // Open the file
	// Check if the file opened properly
	if (dataFile) {
		SERIAL_PRINT("Opening the file in path: ");
		SERIAL_PRINTLN(fileName);
		SERIAL_PRINTLN("The file opened correctly!");
		time = String(hour()) + ":" + String(minute()) + ":" + String(second()); // Get the time
		stringData = String(day()) + "," + time + "," + String(temp) + "," + String(hum) + "," + String(node); // .csv formatting  + "," + String(error)
		dataFile.println(stringData); // Write on SD
		SERIAL_PRINT("Data wrote on SD: ");
		SERIAL_PRINTLN(stringData); // Write on serial monitor
		dataFile.close();
	}
	// if the file isn't open, pop up an error:
	else {
		SERIAL_PRINTLN("WARNING: error opening the file!");
	}
	}
	
}

The code working well:

#include <SPI.h>
#include "RF24.h"
#include "DHT.h"
#include <SD.h>
#include <Wire.h>  
//#include "SSD1306.h"
//#include "OLEDDisplayUi.h"
#include <TimeLib.h>
#include <DS1307RTC.h>

// DEBUG
#define DEBUG // Enable debug
#include "DebugUtils.h"

// SD
// .csv: add comma (",") to separate different data (each one in a column), printLN to new row
const int chipSelect = 4;
String current_year = "";
String current_month = "";
int contNode[6] = {4,4,4,4,4,4}; // Write on SD every 60 minute (every 6 "cont")

float temp, hum;
// Data struct that contains Temp, Hum and the number of the node
struct data {
	float Temp;
	float Hum;
	int node = 4; // Not store in the addresses variable
} TandH;

unsigned long previousMillis = 0; // will store last time DHT was read
const long interval = 600000; // read after 10 minute
unsigned long currentMillis; // Store current intervall


void setup() {
	Serial.begin(9600);
	while (!Serial); // wait until Arduino Serial Monitor opens

	// Start SD library
	// see if the card is present and can be initialized:
	if (!SD.begin(chipSelect)) {
		SERIAL_PRINTLN("WARNING: card initialization failed, or not present");
	}
	SERIAL_PRINTLN("card initialized.");

	// Start TIME library
	setSyncProvider(RTC.get);   // the function to get the time from the RTC
	if(timeStatus()!= timeSet) 
		SERIAL_PRINTLN("Unable to sync with the RTC");
	else
		SERIAL_PRINTLN("RTC has set the system time");

	// Update current year and mounth variable
	// WARNING: the variables have String type, but the function return int variable
	current_year = String(year());
	//SERIAL_PRINTLN("The current year is: " + current_year);
	current_month = monthStr(month()); // WARNING: not sure if it is needed


	SERIAL_PRINTLN("Boot OK");
	SERIAL_PRINTLN("NODE: cameraPC");

}

void loop() {

	readDHT(0);
	delay(1000);
}

// Write the data on the SD card every hour
void write(float temp, float hum, int node) {
	contNode[node]++; // Increment the conunt
	// check if is passed an hour
	if (contNode[node] == 5) {
	contNode[node] = 0; // Reset the variable
	// Check if we need a new file o folder
	//SERIAL_PRINTLN("Checking if the current year is changed...");
	if (String(year()) != current_year) {
		//SERIAL_PRINTLN("Current year is changed, updating the variable...");
		current_year = String(year()); // Update the variable
		//SERIAL_PRINTLN("Variable update succesfully: " + current_year);
		String path = "/" + current_year; // WARNING: not sure what this line do...
		SD.mkdir(current_year); // Create a new folder with the name of the year
		SERIAL_PRINTLN("New folder created");
	}
	
	String fileName = "/" + current_year + "/" + monthStr(month()) + ".csv";
	File dataFile = SD.open(fileName, FILE_WRITE); // Open the file
	// Check if the file opened properly
	if (dataFile) {
		SERIAL_PRINT("Opening the file in path: ");
		SERIAL_PRINTLN(fileName);
		SERIAL_PRINTLN("The file opened correctly!");
		String time = String(hour()) + ":" + String(minute()) + ":" + String(second()); // Get the time
		String stringData = String(day()) + "," + time + "," + String(temp) + "," + String(hum) + "," + String(node); // .csv formatting  + "," + String(error)
		dataFile.println(stringData); // Write on SD
		SERIAL_PRINT("Data wrote on SD: ");
		SERIAL_PRINTLN(stringData); // Write on serial monitor
		dataFile.close();
	}
	// if the file isn't open, pop up an error:
	else {
		SERIAL_PRINTLN("WARNING: error opening the file!");
	}
	}
	
}

The thing that change is the time stringdata filename String are declared in the function in a sketch and globally in the other.

Eternyt: Many thanks! So, a char class is an array of characters.

char isn't a class. It is a regular old built in data type. You are just using arrays of them to hold your string of characters.

So, a char class is an array of characters

no a c-string is an array of characters - it's plain old C, so no class in there :)

Sorry for the mistake, but the rest is right?