I'm making a data logging system, just for fun and learning the language. If I want to save a variable ".txt" file with an INT it works fine. Now I bought an RTC and I want the name to be as following: "Data_'DATE&TIME'.txt".
As you can see in the code below, I made a function newFileName what should be doing this, but my output also below is nothing like it. And on the SD card nothing even saves.
The things in comments are things I tried.
int CURRENT_FILE = 1;
String dataString = "";
String currentFileName = "";
String currentTimeStamp = "";
void setDatumTijd(){
t = rtc.getTime();
currentTimeStamp = rtc.getDateStr();
currentTimeStamp += "--";
currentTimeStamp += rtc.getTimeStr();
currentTimeStamp.replace(':', '.');
delay(50);
}
void makeNewFile(String currentTimeStamp){
char fileName[50];
char timeStamp[20];
// sprintf(timeStamp, currentTimeStamp.c_str());
sprintf(fileName, "Data_%d.txt", currentTimeStamp.c_str());
//currentFileName = fileName;
//currentFileName.toCharArray(fileName,50);
//currentTimeStamp += ".txt";
//currentTimeStamp.toCharArray(fileName, (currentTimeStamp.length()+1));
Serial.println(fileName);
currentFileName = fileName;
File dataFile = SD.open(fileName, FILE_WRITE);
saveHeader(currentFileName, currentTimeStamp);
dataFile.close();
}
enter image description here
The library that comes with Arduino doesn't support long filenames, it only supports "8 bytes for filename"."3 bytes for file type", and in your case, it is clearly exceeding that limit.
This all is due to the reason that most of the Arduino boards has lesser RAM.
But if you still want to go ahead, you can use the following library.
https://github.com/greiman/SdFat
This library supports Long File names and if I remember correctly, the Arduino Standard Library is also the wrapper of the library created by the same author.
https://forum.arduino.cc/index.php?topic=58549.msg421288#msg421288
Note: Long File Names consumes much RAM.
Related
Im try to add to my sketch a dynamic way to setup the ethernet info (mac, ip, gateway, subnet) from a configuration file (config.txt). So running a webserver and serving htm files from sd card, user can go to setting page, fill a form with these info and when posted , the webserver parse the http form and save (update) the config.txt file. After that system do a restart, in order to start with the new settings (by read the config.txt file)
I have create succesfully all the parts (sd, ethernet, webserver, webclient, create the config file from posted form data) except the get params by reading the config.txt file.
I can read line by line the config, I can split the line to param & value, and now I need to fill some byte variables with the readed data. I can (after a month of google searching) to read IPs (decimal values) to byte array. Im stack to read the MAC ADDRESS hex into byte array. The config file contains the:
mac=8f:2c:2b:19:e0:b7;
ip=192.168.1.200;
netmask=255.255.255.0;
gateway=192.168.1.254;
dns=8.8.8.8;
posturl=192.168.1.157;
postport=8080;
postscript=/itherm/update.php;
interval=60000;
and the code that I use to read is:
byte myMAC[6];
byte myIP[4];
File fset;
fset = SD.open("config.txt");
if (fset){
char ff[40];
while (fset.available()>1){
bool eol=false;
for (int i=0; !eol;i++){
ff[i]=fset.read();
if (ff[i]=='\n'){
eol=true;
}
}
String par="";
bool DONE=false;
for (int i=0; !DONE;i++){
par+=ff[i];
if (ff[i]== '='){DONE=true;}
}
String pval="";
DONE=false;
//------------------------
if (par=="ip=" ){
int x=0;
while(!DONE){
for(int i=3;i<=i+21;i++){
if(ff[i]=='.'){
myIP[x]=pval.toInt();
x++;
i++;
pval="";
}
else if(ff[i]==';' || i>20){
myIP[x]=pval.toInt();
DONE=true;
break;
}
pval+=ff[i];
}
}
}
} //while (fset.available()>1)
} //if (fset)
I will appreciate any help. Please no answers with simple use of Serial.print(). I have found hundreds of suggestions but none, that work properly to read all the parameters (dec, hex, strings). After a month of effort & searching, I wonder why something so necessary and useful does not exist as an example in the community, completely functional !!
Best regards
Okay so here is a complete set of routines to do what you want -I think you misunderstood the concept of char arrays vs a single char[0] The routines are documented and self explanatory. I recomend not to finish lines with ; but with '\n' which in your example is there anyway (also you can not see the new line terminator) To get the mac address I need three lines:
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
line one compares the first 4 characters and if it is 0 (meaning its a fit)
line two copies the chars from the fifth to the last char from the lineBuffer to the target array, which can actually be used as param for functions.
The file structure should be with no ; as you would have to parse ; and \n
mac=8f:2c:2b:19:e0:b7
ip=192.168.1.200
....
postport=8080
To convert a char array to eg int we use atoi(), to convert a single char[0] to a single number we use int singleDigit = char[0]-48;
const char configurationFilePath [] = "/someconfig.txt";
char cfgLine[128] = {'\0'}; // this is a global temp char array to hold the read lines (lenght= chars longest line +1)
char numBuffer[16] = {'\0'}; // this is a global temo char array to help to convert char to number
char macAddr [18] = {'\0'}; // this is a global char array to hold the mac address
char ipAddr [16] = {'\0'}; // this is a global char array to hold the IP address - max xxx.xxx.xxx.xxx
int postport=0;
// .... you can easyly implement for all other data you want to store/retrieve
// Counts the lines of a file
uint16_t countLines() {
uint16_t currentLineCount = 0;
File cfgFile = SD.open(configurationFilePath, "r");
if (!cfgFile) {
Serial.println(F("Config file open failed on read"));
} else {
while (cfgFile.available()) {
/** Lets read line by line from the file */
if (cfgFile.read() == '\n') currentLineCount ++; // Lines are delimited by '\n'
}
cfgFile.close();
}
return currentLineCount;
}
//Load the config file from SD/SPIFFS/LittleFS
bool loadConfigFile() {
uint16_t lineCounter = countLines();
if (lineCounter <= 0) {
Serial.print(F("No config data stored in file ")); Serial.println(configurationFilePath);
return false;
}
else {
File cfgFile = SD.open(configurationFilePath, "r");
while (cfgFile.available()) {
strcpy (cfgLine, (cfgFile.readStringUntil('\n').c_str())); // normaly you use new line, we copy one line at a time
// Serial.println(cfgLine); /** Printing for debuging purpose */
while (cfgLine[0] != '\0') { /* Block refilling of cfgLine till processed */
loadSingleCfgLine();
}
}
cfgFile.close();
Serial.println(F("[Success] Loaded config !"));
return true;
}
}
//Load the data of a single line into a char array
void loadSingleCfgLine() {
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
if (strncmp(cfgLine, "ip=", 3) == 0) {
strcpy (ipAddr, cfgLine + 3);
}
if (strncmp(cfgLine, "postport=", 9) == 0) {
strcpy (numBuffer, cfgLine + 9);
postport = atoi(numBuffer); // One extra step to convert to int
}
// ... easy to implement for all other data
}
I divided the routines into small independend functions, so its easy adaptable for different uses. I'm sorry for not digging into your code as it is hard to follow and unclear what you want todo.As an added bonus we do not use the String class. These Strings tend to fragment heap - causing resets/crashes while the global char arrays are compiled to flash and don't show this behavior.
I am having problems communicating FROM the arduino to my Qt application through QSerialPort. I have a listening signal that tells me when there is data ready to be read from the arduino. I expect a value for the number of steps that a stepper motor has undertaken before hitting a limit switch, so only a simple int such as "2005". When the data is available for reading, sometimes I get two separate reads with "200" and "5". Obviously this messes things up when I am parsing the data because it records it as two numbers, both much smaller than the intended number.
How can I fix this without me putting in a Sleep or QTimer to allow for a bit more time for the data to come in from the arduino? Note: my program is not multithreaded.
Example Qt code:
//Get the data from serial, and let MainWindow know it's ready to be collected.
QByteArray direct = arduino->readAll();
data = QString(direct);
emit dataReady();
return 0;
Arduino:
int count = 2005;
Serial.print(count);
You can add line break to synchronize.
Example Qt code:
//Get the data from serial, and let MainWindow know it's ready to be collected.
QByteArray direct = arduino->readLine();
data = QString(direct);
emit dataReady();
return 0;
Arduino:
int count = 2005;
Serial.print(count);
Serial.println();
If you are going to use QSerialPort::readyRead signal, you need to also use the QSerialPort::canReadLine function, see this.
Thank you for your help Arpegius. The println() function was definitely a good choice to use for the newline delimiter. And following that link, I was able to get a listening function that got everything the arduino sent as seperate strings. The extra if statements in the loop handle any cases where the incoming string does not contain the newline character (I am paranoid :D)
My code for anyone that has the same problem in the future.
int control::read()
{
QString characters;
//Get the data from serial, and let MainWindow know it's ready to be collected.
while(arduino->canReadLine())
{
//String for data to go.
bool parsedCorrectly = 0;
//characters = "";
//Loop until we find the newline delimiter.
do
{
//Get the line.
QByteArray direct = arduino->readLine();//Line();
//If we have found a new line character in any line, complete the parse.
if(QString(direct).contains('\n'))
{
if(QString(direct) != "\n")
{
characters += QString(direct);
characters.remove(QRegExp("[\\n\\t\\r]"));
parsedCorrectly = 1;
}
}
//If we don't find the newline straight away, add the string we got to the characters QString and keep going.
else
characters += QString(direct);
}while(!parsedCorrectly);
//Save characters to data and emit signal to collect it.
data = characters;
emit dataReady();
//Reset characters!
characters = "";
}
return 0;
}
I have the following code
global
File logFile
and inside loop()
DateTime now = rtc.now();
String stringFilename = "";
stringFilename = String(now.year())+"_"+String(now.month())+"_"+String(now.day());
stringFilename += ".csv";
Serial.println(stringFilename);
char filename[stringFilename.length() + 1];
stringFilename.toCharArray(filename, sizeof(filename));
String dataString = "";
dataString += String(now.year());
Serial.print("Filename: ");
Serial.println(filename);
if (!SD.exists(filename)){
Serial.println("File does not exist");
logFile = SD.open(filename, FILE_WRITE);
}
if (logFile){
logFile.println(dataString);
}else{
Serial.println("Could not open file");
}
Filename is something like 2014_5_26.csv. SD empty. The above code won't open file for writing. If I substitute filename with a literal like "datalog.txt" it works. What am I doing wrong?
EDIT: I got what my problem was. The name was to long. It can be only up to 8 characters. Can I ask something different? Can I call SD.begin(chipSelect) in loop to determine if SD is present or is this called always on setup?
You can use it in loop but the program speed will be reduce.
Don't forget to close your file (SD.close()) before to be sure that your data are correctly written to the SD card
8.3 filename rules may apply. Your Date derived ("2014_5_26") filename appears to have 9 characters. When you use "datalog", that has 8 characters, it works.
I have:
Arduino MEGA 2560;
Ethernet+SD shield http://www.amazon.com/dp/B0022TWQ22/?tag=stackoverfl08-20 ;
SD card 2GB FAT.
SD contains 400 files with names 00000000; 0000001; 0000002; ... 00000098; 0000099; 0000100; ... 00000398; 00000399.
I need to construct String var which will contain all the Filenames separated by ";" like this:
sdata = "0000001;0000002;0000003 ... 00000398;00000399;";
Code:
#include <SdFat.h>
#include <SPI.h>
const uint16_t chipSelect = SS;
char cnamefile[9];
String sdata="";
SdFat sd;
SdFile file;
void setup() {
Serial.begin(9600);
Serial.println("hi");
sdata="";
if (!sd.begin(chipSelect, SPI_HALF_SPEED)) sd.initErrorHalt();
Serial.println("List files");
while (file.openNext(sd.vwd(), O_READ)) {
file.getFilename(cnamefile);
file.close();
sdata = sdata + String(cnamefile) + ";";
}
Serial.print(sdata);
}
void loop() {
}
Listening to the COM port i see:
hi
List files
00000005;00000006;00000007;00000008;00000009;00000010;00000011;00000012;00000013;00000014;00000015;00000016;00000017;00000018;00000019;00000020;00000021;00000022;00000023;00000024;00000025;00000026;00000027;00000028;00000029;00000030;00000031;00000032;00000033;00000034;00000035;00000036;00000037;00000038;00000039;00000040;00000041;00000042;00000043;00000044;00000045;00000046;00000047;00000048;00000049;00000050;00000051;00000052;00000053;00000054;00000055;00000056;00000057;00000058;00000059;00000060;00000061;00000062;00000063;00000064;00000065;00000066;00000067;00000068;00000069;00000070;00000071;00000072;00000073;00000074;00000075;00000076;00000077;00000078;
How to fix this problem and put all filenames in one variable?
Information for: 400 names and 400 ";" its 3600 bytes. When i try to read any file and put all its contents (more than 3600 bytes) in "String sdata" it works normally. Problem only with listing.
Please help me in sorting out this issue.
This seems about the correct place that your program will fail. This innocent line is your problem:
sdata = sdata + String(cnamefile) + ";";
String concatentation like this will use 2X the memory of the sdata for a short moment. This is the how you should view the sequence of operations in that one line
// compiler has done this for you:
String temp1234 = sdata + String();
// note that at this moment, memory usage is now 2x sdata
String temp1235 = temp1234 + ";";
// now you can have 3x the memory used
// assignment to variable
sdata = temp1235;
// now can delete temporary variable
// compiler will do this
//temp1234.delete()
//temp1235.delete()
You are trying to create strings up to 3k bytes but have only 8k total RAM, so will not be able to do the above.
This demonstrates a couple of points about Strings. Your concatenation above on one line, is not necessarily better than this two line form:
sdata = sdata + String(cnamefile);
sdata = sdata + ";";
In this second form, you are ensured there will only be one temporary variable for the intermediate result.
This leads to the next hint. You should be thinking how am I going to escape the temporary variable. That is why we have += operator. Your best chance is to concatenate like this:
sdata += String(cnamefile);
sdata += ";";
If the += operator is available on the String class, the compiler will use this. That operator may be able to use a more memory efficient way of concatenation. For example, if the String was preallocated with some extra memory, then it could just place the new characters into the existing buffer.
In general this is a great learning method about strings in constrained memory spaces, because you must understand some compiler internals and operator details that are often ignored in large CPU environments.
Given the sizes you are suggesting, you will probably only be able to fit in RAM if you change to an approach of pre-constructing a String buffer and filling it with the file names. In other words: don't use String on a microcontroller.
There is no documentation in the help file as to the purpose of the Comma7IO class, just that it extends the CommaIO class.
What is the difference?
To support read and write of different
formats of external files, MorphX
features a range of different Io
classes; CommaIo for comma separated
files, Comma7Io for comma separated 7
bit files, BinaryIo for binary files
and AsciiIo for plain text files.
From this link: RE: [Axapta-Knowledge-Village] Somthing cool - IO
Run this job
static void TestComma7Io(Args _args)
{
str testString = 'ABCDEFG~ÀÁÂÃÄÅÆÇÈÉÊË~HIJKLMNOP';
str filename = #"C:\TMP\test1.txt";
str mode = 'W';
Io io;
container con;
FileIoPermission perm;
;
perm = new FileIoPermission(filename, mode);
if (!perm)
return;
perm.assert();
// BP deviation documented.
io = new Comma7Io(filename, mode);
if (io)
io.write(testString);
CodeAccessPermission::revertAssert();
}
and check the content of the file: "ABCDEFG~\300\301\302\303\304\305\306\307\310\311\312\313~HIJKLMNOP". As you see, 8-bit characters have been replaced with their octal codes.
If you replace io = new Comma7Io(filename, mode); with io = new CommaIo(filename, mode); the original string will be written to the file: "ABCDEFG~ÀÁÂÃÄÅÆÇÈÉÊË~HIJKLMNOP".