How to create a dynamic filename for an SD card on Arduino - c++

I'd like to log my data on my Arduino one file at a time. I'd like the filename to be a combination of the number of milliseconds that have passed + some ID. For example, GPS data would be millis()+"GPS".
I tried the following code, but it doesn't like the fact that I am using a String. I could use a char array, but the length would always be dynamic. Is there a way to do this with at string somehow?
static void writeToSD()
{
String logEntry = " GPS: ";
logEntry += GPSString;
String filename = String(millis());
filename += "GPS";
Serial.println(logEntry);
Serial.println(filename);
File dataFile = SD.open(filename, FILE_WRITE);
// If the file is available, write to it:
if (dataFile) {
dataFile.println(logEntry);
dataFile.close();
Serial.println("Closed");
}
// If the file isn't open, pop up an error:
else {
Serial.println("error opening file");
}
}

You could try the following
char fileNameCharArray[filename.length()];
filename.toCharArray(fileNameCharArray, filename.length())
File dataFile = SD.open(fileNameCharArray, FILE_WRITE);

sprintf (filename, "%ld-GPS", millis());
Note that the use of String on Arduino is discouraged because of the well documented memory leak/fragmentation problems.

Related

Arduino IDE ESP32 device using SPIFFS to save

I writing ESP32 program and using SPIFFS to save some data because I do not want to loose it after I power down the device.
I have 2 functions:
char* readFile(fs::FS &fs, const char *path)
{
Serial.printf("Reading file: %s\n", path);
File file = fs.open(path);
if (!file || file.isDirectory())
{
Serial.println("Failed to open file for reading");
return "FAIL";
}
Serial.print("Read from file: ");
while (file.available())
{
Serial.write(file.read());
delayMicroseconds(100);
//TODO
//append all characters and return it as a list of char arrays at the end of reading
}
file.close();
}
void writeFile(fs::FS &fs, const char *path, const char *message)
{
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if (!file)
{
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message))
{
Serial.println("File written");
}
else
{
Serial.println("Write failed");
}
file.close();
}
First of all, I run my program and call writeFile() and then followed by readFile():
int n = 0;
Wifi_setup();
Spiffs_setup();
n = scan_wifi_networks();
Serial.print("list_strings outside scan function = ");
for (int i = 0 ; i<=n ; i++){
Serial.println(found_networks[i]);
}
//compare list_strings with preset wifi networks. If match, everything is fine, if not, problem!
writeFile(SPIFFS, "/wifi.txt", "Telia-33F8A3-Greitas");
char* known_networks = readFile(SPIFFS, "/wifi.txt");
//send USD to the server, go back to sleep
//initialize_deep_sleep();
}
And that part works fine. From the serial monitor, I can see that it have sucesfully written my text to wifi.txt.
After I run write/read functions
Next, I comment out write function and only leave read function.I run the code again and it is not able to read back my text:
Only readFile
Can someone help me understand why is that happening? I thought that if I write to spiffs once, I will be able to access it afterwards but that is not the case. I have previously used EEPROM and that seemed to work. I can write to EEPROM address and simply access the same address later and the value will still be there after the power off. Any help is appreciated. Thanks in advance.
UPDATE1
I have managed to read back data from my SPIFFS after I wrote to it. I had missed one crucial step:
https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/
However, now, I am encountering another issue:
In my project folder, I have created a folder "data" which is supposed to be accessed by SPIFFS. In there, I create 2 files :
wifi.txt
update.bin
update.bin for now is not relevant. Lets talk about wifi.txt
After I have written to Spiffs, I now comment out the writeFile function and only leave out readFile. The result from my serial monitor:
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6388
entry 0x400806b4
Listing directory: /
FILE: /update.bin SIZE: 0
FILE: /wifi.txt SIZE: 20
Reading file: /wifi.txt
Read from file: Telia-33F8A3-Greitas
As you can see from the serial monitor above, my program recognises the text that I have written to my spifs /wifi.txt file. Howeverm when I go to my actual project directory and open data/wifi.txt it is emtpy:
How can this happen? My program recongises that theres data inside but it wont show up in the file.
UPDATE2
I have done some further testing of SPIFFS.
In my data folder, I have created another txt file test.txt. In there, I have manually put some text "this is text message".
In my program, I have called function:
char* returned_data = readFile(SPIFFS, "/test.txt");
And the serial monitor sucesfully printed the message that I have put. So that proves that the SPIFFS is able to read back from the files without any issues.
Then, I have modified my program:
writeFile(SPIFFS, "/test.txt", "hello123");
char* returned_data = readFile(SPIFFS, "/test.txt");
The code above should overwrite whatever I have written to my txt file with "hello123" and then my program should read the "hello123" back from Spiffs. The serial monitor response:
Listing directory: /
FILE: /update.bin SIZE: 0
FILE: /wifi.txt SIZE: 2
FILE: /test.txt SIZE: 8
Writing file: /test.txt
File written
Reading file: /wifi.txt
Read from file:
Reading file: /test.txt
Read from file: hello123
As you can see, it is able to sucesfully read back "hello123".
HOWEVER, when I go to my project folder, open the data/test.txt, I can still see my initial text not replaced with "hello123".
I do not understand how is this happening..

Infinite cycle due to QTextStream

So, I get infinite cycle while trying to read lines from file (line by line). I was trying to use do{}while(); cycle like that:
QTextStream stream(stdin);
QString line;
do {
line = stream.readLine();
} while (!line.isNull());
but I get empty string.
Sure, I checked file path (it is right). I was trying to use /Users/user/tts.txt path but without changes. I was trying to read other files (like m3u). And it's not working on macOS Catalina, Windows 10, Linux (Debian).
So, why did I get infinite cycle?
QStringList Manager::GetLinesFromFile(const QString &nameOfFile)
{
QStringList lines = {};
//path to file
const QString path = QCoreApplication::applicationDirPath() + "/bin/" + "tts.txt";
//"/Users/user/tts.txt"
QFile buffer;
buffer.QFile::setFileName(path);
#ifndef Q_DEBUG
qDebug() << path;
#endif
if(buffer.QFile::exists())
{
if(!buffer.QIODevice::open(QIODevice::ReadOnly))
{
#ifndef Q_DEBUG
qCritical() << "error: can't open file";
#endif
}
else
{
QTextStream stream(&buffer);
// both conditions
// (!stream.QTextStream::atEnd())
while(!buffer.QFileDevice::atEnd())
lines.QList::push_back(stream.QTextStream::readLine());
buffer.QFile::close();
}
}
else
{
#ifndef Q_DEBUG
qCritical() << "error: file not exists";
#endif
}
return lines;
}
Have a look at the QTextstream documentation https://doc.qt.io/qt-5/qtextstream.html. There is an example of reading line by line. Your while loop should read until the stream reaches the end of the buffer and many of the in built read functions will return false when his happens
So, I got it. I opened the file incorrectly.
I was using:
if(!file.QIODevice::open(QIODevice::ReadOnly))
but it should be like that:
if(!file.QFile::open(QFile::ReadOnly))

How to create a function: Get a single value from SD card file using the file name as function parameter

I appreciate any guidance or help I can get on this. I am writing a program with values for a PID stored on an SD card so I can change them on the touchscreen without the need to hook it up to my computer. I want a single function that I can call with parameters allowing me to increase or decrease the number and change the file name. The below function is what I have to change whether "pnum.txt" increases or decreases; however I cannot figure out how to make a File work as a parameter.
I have tried making " "pnum.txt" " (with quotes) as a char and as a String, and even though it prints out as it should, it doesn't work when inserted into the function. I have also tried passing the whole SD.open("pnum.txt", FILE_WRITE) and myFile = SD.open("pnum.txt", FILE_WRITE) as a File, but that does something odd - it will create the file when I open it, but it won't write to the file. I'm finding myself just trying the same things over and over, so I clearly have a lack of understanding that I'm not finding anywhere. Thank you again for any help on this!
float incDecValue(float value) {
//This is important, because the libraries are sharing pins
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
value;
SD.remove("pnum.txt");
myFile = SD.open("pnum.txt", FILE_WRITE);
myFile.print(value);
myFile.close();
counter = 0;
myFile = SD.open("pnum.txt");
if (myFile) {
while (myFile.available()) {
testChar[counter] = myFile.read();
counter++;
}
myFile.close();
}
float convertedValue = atof(testChar);
return convertedValue;
}
And I will call it like this.
pValue = incValue(pValue+=.5);
As not exactly knowing what you really want I did the following assumptions:
You want to save a single float value to a file called pnum.txt
You want to do something with that value
You want the processed value to write back to the file pnum.txt (overwriting the content)
Two different functions parametrized each with fileName as input and the value for write
So here a complete sequence (works as posted) you could easily implement into your code. No String class is used and its a one line file read/write only:
/* SD card pid read/write
The circuit:
SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 */
#include <SPI.h>
#include <SD.h>
const uint8_t chipSelect = 4;
char dataChar[16] = {'\0'};
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect.
}
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed or no card present");
return;
}
Serial.println("Card initialized");
}
void loop() {
float pValue = readFileValue ("pnum.txt") + 0.5;
writeFileValue ("pnum.txt", pValue);
}
float readFileValue (const char* fileName) {
/* Open the file. note that only one file can be open at a time,
so you have to close this one before opening another.*/
File dataFile = SD.open(fileName, FILE_READ);
// if the file is available, write to it:
if (dataFile) {
char c;
uint8_t i = 0;
while (dataFile.available()) {
c = dataFile.read(); // Read char by char
if (c != '\n') { // As long no line terminator
dataChar[i] = c;
i++;
}
else {
dataChar[i] = '\0'; // Terminate char array properly
dataFile.close();
break;
}
}
Serial.print("Success writing content: ");
Serial.println(dataChar);
}
else { // If the file isn't open, pop up an error:
Serial.print("Error opening requested file: ");
Serial.println(fileName);
}
float fileVal = atof(dataChar);;
return fileVal;
}
bool writeFileValue (const char* fileName, float fileVal) {
SD.remove(fileName); // Delete the existing file if existing
File dataFile = SD.open(fileName, FILE_WRITE);
// If the file opened okay, write to it
if (dataFile) {
// dtostrf(floatvar, StringLengthIncDecimalPoint, numVarsAfterDecimal, charbuf);
dtostrf(fileVal, 5, 2, dataChar); // Not really needed for this simple issue, but ..
Serial.print("Writing to file: ");
Serial.println(fileName);
dataFile.println(dataChar);
// Close the file
dataFile.close();
Serial.println("Success saving done");
return true;
} else {
// if the file didn't open, print an error:
Serial.println("Error opening file: ");
Serial.println(fileName);
return false;
}
}

fopen with const char * from QString not possible

To dynamically open a FILE I am passing a QString full path.
If passed as a variable, the code fails.
If entered directy (not via a variable) everything works just fine. What is going on here?
QString outputfile_qstring("C:/temp/out.mp3");
qDebug()<<"Original output file " << outputfile_qstring;
const char* outputfile = outputfile_qstring.toLatin1().constData();
qDebug()<<"Trying to open output file " << outputfile;
fout = fopen(outputfile, "wb+");
bool fileIsOpen = (fout != 0);
if ( !fileIsOpen ){
errStr_ = "Error opening the output file " + outputfile_qstring;
Q_ASSERT(false && "Could not open output file");
return false;
}
The QString to const char * conversion always fails.
Original output file "C:/temp/out.mp3"
Trying to open output file ????????????????????????aSC,_??r
The problem is here:
const char* outputfile = outputfile_qstring.toLatin1().constData();
The toLAtin1 function returns a QByteArray by value. And since you don't save that object, it will be destructed once the expression is finished, leaving you with outputfile being an invalid pointer to non-existing data.
The simple solution is to use the expression outputfile_qstring.toLatin1().constData() directly in the call to fopen. Or not use fopen and the C file functions at all and only use Qt files.

How to save poco uploaded file

I want to upload file using poco library.
Now I have the uploaded file in the istream variable but I don't know how I can save it to a file?
Here is my code where i can get the length of the file.
void handlePart(const MessageHeader &header, std::istream &stream) {
_type = header.get("Content-Type", "(unspecified)");
if (header.has("Content-Disposition")) {
std::string disp;
NameValueCollection params;
MessageHeader::splitParameters(header["Content-Disposition"], disp, params);
_name = params.get("name", "(unnamed)");
_fileName = params.get("filename", "(unnamed)");
}
CountingInputStream istr(stream);
NullOutputStream ostr;
StreamCopier::copyStream(istr, ostr);
_length = istr.chars();
}
Also now it's 1 file uploaded in the form if there be more than 1 file how it will be managed in istream?
Now there is 2 days I'm searching and testing different ways but I couldn't find any way, please help to resolve this problem.
Thank you in advanced.
Depend on #Abhinav question on this post:
We can write save code like this:
if (fileName.length() != 0){
std::ofstream fout( fileName, std::ios::binary);
fileList.push_back(fileName);
fout << stream.rdbuf() ;
fout.close();
}
But unfortunately it's working for one file if there is more than one this code can't catch correctly.