Arduino reading SD file line by line C++ - c++

I am trying to read a text file "Print1.txt", line by line, from an SD card attached to my Arduino MEGA. So far I have the following code:
#include <SD.h>
#include <SPI.h>
int linenumber = 0;
const int buffer_size = 54;
int bufferposition;
File printFile;
char character;
char Buffer[buffer_size];
boolean SDfound;
void setup()
{
Serial.begin(9600);
bufferposition = 0;
}
void loop()
{
if (SDfound == 0)
{
if (!SD.begin(53))
{
Serial.print("The SD card cannot be found");
while(1);
}
}
SDfound = 1;
printFile = SD.open("Part1.txt");
if (!printFile)
{
Serial.print("The text file cannot be opened");
while(1);
}
while (printFile.available() > 0)
{
character = printFile.read();
if (bufferposition < buffer_size - 1)
{
Buffer[bufferposition++] = character;
if ((character == '\n'))
{
//new line function recognises a new line and moves on
Buffer[bufferposition] = 0;
//do some action here
bufferposition = 0;
}
}
}
Serial.println(Buffer);
delay(1000);
}
The function returns only the first line of the text file repeatedly.
My Question
How do I change the function to read a line of text, (with the hope to perform an action on such line, shown by "//do some action") and then move onto the next line in the subsequent loop, repeating this until the end of file has been reached?
Hopefully this makes sense.

Actually, your code returns only the last line of the text file because it is printing the buffer only after reading the whole data. The code is printing repeatedly because the file is being opened inside the loop function. Usually, reading a file should be done in the setup function that is executed only one time.
Instead of reading the data char by char into the buffer, you could read until find the delimiter and assign that to a String buffer. This approach keep your code simple. My suggestion to fix your code is right below:
#include <SD.h>
#include <SPI.h>
File printFile;
String buffer;
boolean SDfound;
void setup() {
Serial.begin(9600);
if (SDfound == 0) {
if (!SD.begin(53)) {
Serial.print("The SD card cannot be found");
while(1);
}
}
SDfound = 1;
printFile = SD.open("Part1.txt");
if (!printFile) {
Serial.print("The text file cannot be opened");
while(1);
}
while (printFile.available()) {
buffer = printFile.readStringUntil('\n');
Serial.println(buffer); //Printing for debugging purpose
//do some action here
}
printFile.close();
}
void loop() {
//empty
}

Related

Esp32 read lines from text file as strings get wrong worder

I have a text file test1.txt:
aaa|aaa|aaa
bbb|bbb|bbb
ccc|ccc|ccc
ddd|ddd|ddd
File has 4 rows as you can see. I use this code to read the content of the file and get each line as a separated string:
#include "SPIFFS.h"
String file_content = "";
char VALUE [1024] = {'\0'};
void setup() {
Serial.begin(9600);
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
File file = SPIFFS.open("/test1.txt");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
uint16_t i = 0;
while (file.available()) {
VALUE[i] = file.read();
Serial.println (VALUE[i]);
i++;
}
VALUE[i] ='\0';
Serial.println("Raw print");
Serial.println (VALUE); //use for debug
Serial.println("String print");
String myString = String(VALUE);
Serial.println (myString);
file.close();
int delimiter, delimiter_1, delimiter_2, delimiter_3, delimiter_4;
delimiter = myString.indexOf("\n");
delimiter_1 = myString.indexOf("\n", delimiter + 1);
delimiter_2 = myString.indexOf("\n", delimiter_1 +1);
delimiter_3 = myString.indexOf("\n", delimiter_2 +1);
delimiter_4 = myString.indexOf("\n", delimiter_3 +1);
// Define variables to be executed on the code later by collecting information from the readString as substrings.
String row1 = myString.substring(delimiter + 1, delimiter_1);
String row2 = myString.substring(delimiter_1 + 1, delimiter_2);
String row3 = myString.substring(delimiter_2 + 1, delimiter_3);
String row4 = myString.substring(delimiter_3 + 1, delimiter_4);
Serial.println("Rows print");
Serial.println(row1);
Serial.println(row2);
Serial.println(row3);
Serial.println(row4);
}
void loop() {}
Output of Rows print is:
Rows print
bbb|bbb|bbb
ccc|ccc|ccc
ddd|ddd|ddd
aaa|aaa|aaa
Why is the "aaa" row thelast one and not first one? Basically delimiter_1 is "bbb" row instead be "aaa" row, and delimiter_4 is "aaa" row instead "ddd" row.
Thanks.
Try this:
#include <SPIFFS.h>
using namespace std;
void setup() {
Serial.begin(115200);
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
File file = SPIFFS.open("/test1.txt");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
vector<String> v;
while (file.available()) {
v.push_back(file.readStringUntil('\n'));
}
file.close();
for (String s : v) {
Serial.println(s);
}
}
void loop() {}
Output:
aaa|aaa|aaa
bbb|bbb|bbb
ccc|ccc|ccc
ddd|ddd|ddd
EDIT:
If you want to manipulate individual element, you could use regular for-loop iteration. See example below:
for (int i = 0; i < v.size(); i++){
Serial.println(v[i]);
}

Read line by line in file and store into string, Flash data saving SPIFFS

I'm working on a project with Flash data saving. I'm using SPIFFS library for ESP32, I'm currently attempting to store the data from each line into a String. Since I have control of how many content can go into the file, it won't need more than 3 Strings to store the data. I could easily manage to store the first line content using readStringUntil. But I can't manage to get the content from 2 and 3 line.
For the first line I'm using this code:
//Pegar a primeira linha do arquivo, onde será armazenado o nome do WIFI (ssid)
void first_line (){
file = SPIFFS.open("/wifi.txt", "r");
while (file.available()) {
String first_line = file.readStringUntil('\n');
Serial.print(first_line);
break;
}
file.close();
}
I'm writing the code into the File with this function:
// Escrever mensagem dentro do arquivo
void write_file_info(String message) {
file = SPIFFS.open("/wifi.txt", FILE_WRITE);
if (!file){
Serial.println("Error opening file");
return;
}else{
Serial.println("Success opening file");
}
if (file.println(message)){
Serial.println("File was written");
}else{
Serial.println("File was not written");
}
file.close();
}
And I'm using Append to add the second and third line:
void append_file_info (String message){
file = SPIFFS.open("/wifi.txt", FILE_APPEND);
if (!file){
Serial.println("Erro ao realizar APPEND ao arquivo");
}
if (file.println(message)){
Serial.println("File was added");
}else{
Serial.println("File was not added");
}
file.close();
}
This is the current output, file size is just for manage and "content inside file" is just for reference:
File size: 37
Content inside file:
first line
second line
thrid line
This is how I'm reading the file:
void read_file_info() {
file = SPIFFS.open("/wifi.txt");
Serial.print("\nFile size: ");
Serial.println(file.size());
Serial.print("Content inside file: \n");
while (file.available()){
Serial.write(file.read());
}
Serial.println("\n");
file.close();
delay(3000);
}
I thought on trying to read the data after '\n', but couldn't find any documentation on reading after certain string.
I tried creating a buffer and splitting it later, the output from the buffer is correct but I can't split it into strings correctly:
void second_line (){
file = SPIFFS.open("/wifi.txt", "r");
char buffer[64];
while (file.available()) {
int l = file.readBytesUntil('\n', buffer, sizeof(buffer));
buffer[l] = 0;
Serial.println(buffer);
}
file.close();
}
It would be simpler using vector:
#include <SPIFFS.h>
using namespace std;
void setup() {
Serial.begin(115200);
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
File file = SPIFFS.open("/wifi.txt");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
vector<String> v;
while (file.available()) {
v.push_back(file.readStringUntil('\n'));
}
file.close();
for (String s : v) {
Serial.println(s);
}
}
void loop() {}
Use v[0] to get first line, v[1] for second line, v[2] for third line and so on.
I could manage to get it working like this:
void all_lines (){
file = SPIFFS.open("/wifi.txt", "r");
int i = 0;
char buffer[64];
String line_one, line_two, line_three;
while (file.available()) {
int l = file.readBytesUntil('\n', buffer, sizeof(buffer));
buffer[l] = 0;
if (i == 0) {
line_one = buffer;
}
if (i == 1) {
line_two = buffer;
}
if (i == 2) {
line_three = buffer;
}
i++;
if (i == 3){
break;
}
}
file.close();
}

Arduino String not working inside FreeRTOS Task

I am trying to read Serial data on Arduino Mega 2560 using built in String object but its not printing the Serial data when used inside a FreeRTOS task like following code
void TaskSerialIn(void *pvParameters)
{
String json;
for(;;) {
while(Serial.available()) {
json = Serial.readStringUntil('\n');
Serial.println(json); // this code not printing anything
}
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
It doesn't work even if I declare the String as global variable.
Whereas the following code works when character array is used to store the bytes
char serialJson[65];
void TaskSerialIn(void *pvParameters)
{
char * jsonStr;
int i = 0;
for(;;) {
i = 0;
while(Serial.available()) {
char inChar = (char) Serial.read();
serialJson[i++] = inChar;
if(inChar == '\n') {
serialJson[i] = '\0';
Serial.print(serialJson);
}
}
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}
Any help would be appreciated on how could I use String to read the Serial data.

how can I display/save the value of what return an AT command

Good day all!
I am working on a project with Arduino UNO and a SIM908.
I am trying to understand the AT command.
When I enter
Serial.print("AT")
Serial.println("AT+CGPSSTATUS?");
Serial retuen a value and I would like to save that value into a buffer
char buffer[size]
I do not want to have other string than the return value of an AT command.
I also red that document
SIM908 AT Command Manual_V1.01
At the page 13, you can read (NB. < CR>< LF> : I added a space after the first <, other there are not display)
The "AT" or "at" prefix must be set at the beginning of each Command
line. To terminate a Command line enter < CR>. Commands are usually
followed by a response that includes. "< CR>< LF>< CR>< LF>"
Throughout this document, only the responses are presented, < CR>< LF>
are omitted intentionally
Then, I am asking how I can "extract" the response between < CR>< LF> and < CR>< LF>
Looking at this exemple (let me know if I am wrong), how can I detect the < CR>< LF>
void setup()
{
char buffer[200];
Serial.println("AT+CGPSSTATUS?");
}
void loop()
{
if (Serial.available())
{
// HERE I SHOULD CHECK IF CR ANF LF
Serial.write(Serial.read());
// AND SAVE IT IN buffer. IS'T NOT?
}
}
}
Do you see what I means?
How could you help me to store in a buffer, only the return value of an AT command?
Many thank for your help
It's very interresting what you show me. Here is how I adapt my code
I adapted my code and by the way I created a file for testing Serail while we send a AT command.
The concern function are loop() and read_AT_string(). (I renamed the read_String to read_AT_string().
Here my code and I explain, after the issue, regardin your proposal
#include <SoftwareSerial.h>
int baud_rate = 9600;
int pin_gsm = 3;
int pin_gps = 4;
int pin_power = 5;
//int pin_dtr = 6;
boolean debug = true;
boolean raedy_to_go = false;
// Reading String
#define BUFFERSIZE 200
char buffer[BUFFERSIZE];
char inChar;
int index;
void setup()
{
Serial.begin(baud_rate);
delay(5000); // Wait for 5sec after begin
if(debug)
{
Serial.println(F("\n****************************"));
Serial.println(F("STARTING SYSTEM Read AT stream"));
Serial.println(F("******************************"));
}
pinMode(pin_gsm,OUTPUT); // Set the pins
pinMode(pin_gps,OUTPUT);
pinMode(pin_power,OUTPUT);
powerUpSim908:
if(powerUpSim908())
{
delay(1000);
if(gps_power()){
gsm_enable();
raedy_to_go = true;
if(debug)
{
Serial.println(F("\n****************************"));
Serial.println(F("READY TO GO\n"));
Serial.println(F("****************************\n"));
}
}
else
{
raedy_to_go = false;
if(debug)
{
Serial.println(F("\nNOT READY TO GO.\nGPS could not be power\nRestart the module\nor/and check the battery level.\n"));
}
goto powerUpSim908;
}
}
else
{
raedy_to_go = false;
if(debug)
{
Serial.println(F("\nNOT READY TO GO.\nCheck the battery level.\n"));
}
};
}
void loop()
{
/*
if (Serial.available())
{
Serial.print("Character received: ");
Serial.write(Serial.read());
Serial.println("");
}
*/
if(raedy_to_go)
{
read_AT_string("AT",5000);
delay(10000);
}
}
char read_AT_string(char* command, int timeout)
{
unsigned long previous;
previous = millis();
Serial.println(F("\nDISPLAY BUFFER:"));
index=0;
Serial.println(command);
do
{
if(Serial.available() > 0) // Don't read unless
// there you know there is data
{
Serial.println("1");
if (Serial.peek() == 13) // check if CR (without reading)
{
Serial.println("13");
if(Serial.available() > 0)
{
Serial.read(); // read and ignore
if (Serial.peek()==10) // then check if LF (without reading)
{
Serial.println("10");
if(index < Serial.readBytesUntil(13, buffer, BUFFERSIZE-1)) // One less than the size of the buffer array
{
Serial.println("b");
inChar = Serial.read(); // Read a character
buffer[index] = inChar; // Store it
index++; // Increment where to write next
buffer[index] = '\0'; // Null terminate the string
}
}
}
}
}
}while(((millis() - previous) < timeout));
Serial.println(buffer);
buffer[0]='\0';
Serial.println(F("END DISPLAY BUFFER"));
}
/* FUNCTION */
boolean powerUpSim908(void)
{
if(debug)
{
Serial.println(F("Powering up SIM908"));
}
boolean turnedON = false;
//uint8_t answer=0;
int cont;
for (cont=0; cont<3; cont++)
{
digitalWrite(pin_power,HIGH);
delay(1500);
digitalWrite(pin_power,LOW);
Serial.println(F("Checking if the module is up"));
if(sendATcommand("AT", "OK", 5000))
{
cont = 4; // Leave the loop
turnedON = true;
}
else
{
turnedON = false;
if(debug)
{
Serial.println(F("\nTrying agin to turn on SIM908"));
}
};
}
if(turnedON)
{
if(debug)
{
Serial.println(F("Module is tunrned up\n"));
}
}
else
{
if(debug)
{
Serial.println(F("Module is NOT tunrned ON\n"));
}
}
return turnedON;
}
boolean sendATcommand(char* ATcommand, char* expected_answer, unsigned int timeout)
{
uint8_t x=0;
bool answer=false;
//åchar response[100];
//buffer[0]='\0';
unsigned long previous;
//memset(response, '\0', 100); // Initialice the string
//Serial.println(response);
delay(100);
while( Serial.available() > 0) Serial.read(); // Clean the input buffer
if (ATcommand[0] != '\0')
{
Serial.println(ATcommand); // Send the AT command
}
x = 0;
previous = millis();
index=0;
do
{
if(Serial.available() > 0)
// there you know there is data
{
if(index < BUFFERSIZE-1) // One less than the size of the array // Same as buffer size
{
inChar = Serial.read(); // Read a character
buffer[index] = inChar; // Store it
index++; // Increment where to write next
//Serial.println(index);
buffer[index] = '\0'; // Null terminate the string
}
}
}while(((millis() - previous) < timeout));
if(strstr(buffer,"NORMAL POWER DOWN") != NULL)
{
answer = false;
}
else if (strstr(buffer, expected_answer) != NULL) // check if the desired answer (OK) is in the response of the module
{
/*
Serial.println(F("### BUFFER"));
Serial.println(buffer);
Serial.println(F("### END BUFFER"));
*/
answer = true;
}
else
{
answer = false;
}
if(debug)
{
if(answer)
{
//Serial.println(F("Expected answer : OK!\n"));
}
else
{
//Serial.println(F("Expected answer : KO!\n"));
};
}
return answer;
}
void gps_enable(void)
{
if(debug)
{
Serial.println(F("\nEnabling GPS ..."));
}
digitalWrite(pin_gps,LOW); //Enable GPS mode
digitalWrite(pin_gsm,HIGH); //Disable GSM mode
delay(2000);
}
void gsm_enable(void)
{
if(debug)
{
Serial.println(F("\nEnabling GSM ..."));
}
digitalWrite(pin_gsm,LOW); //Enable GSM mode
digitalWrite(pin_gps,HIGH); //Disable GPS mode
delay(2000);
}
/* UTILISTIES */
/* GPS */
boolean gps_power(void) //turn on GPS power supply
{
/*
Serial.println("AT");
delay(2000);
*/
boolean gpspwr = false;
boolean gpsrst = false;
if(sendATcommand("AT+CGPSPWR=1","OK",2000))
{
gpspwr = true;
if(debug)
{
Serial.println("turn on GPS power supply => OK");
}
}
else
{
if(debug)
{
Serial.println("turn on GPS power supply => KO");
}
};
//delay(1000);
if(sendATcommand("AT+CGPSRST=1","OK",2000))
{
gpsrst = true;
if(debug)
{
Serial.println("reset GPS in autonomy mode => OK");
}
}
else
{
if(debug)
{
Serial.println("reset GPS in autonomy mode => KO");
}
}; //reset GPS in autonomy mode
delay(1000);
if(gpspwr && gpsrst)
{
return true;
}else
{
return false;
}
}
At the read_AT_string, the first if(Serial.peek()==13) always return false.
1 is printed, but '13' is not, then I supposed
if(Serial.peek()==13)
return false
Here is what is printed within 5 sec
AT DISPLAY BUFFER:
1
1
1
1
1
1
1
1
1
[...] // It prints 1 until now
1
END DISPLAY BUFFER
Here a piece of code to detect and remove CR+LF (Attention: if A CR is read but it's not followed by a LF, it is removed as well):
if (Serial.peek()==13) { // check if CR (without reading)
Serial.read(); // read and ignore
if (Serial.peek()==10) // then check if LF (without reading)
Serial.read();
}
To read the rest of the response from Serial you could use:
buffer[Serial.readBytesUntil(13, buffer, 199)]=0; // readbytes returns the number of bytes read
You then have to discard the ending CRLF (same as above).
Edit
There are several issues with the code that you've posted in a separate answer.
When you powerUpSim908() you have to be aware that the gsm module may send unrequested data (see documentation, chapter 1.4):
Note: A HEX string such as "00 49 49 49 49 FF FF FF FF" will be sent
out through serial port at the baud rate of 115200 immediately after
SIM908 is powered on. The string shall be ignored since it is used for
synchronization with PC tool. Only enter AT Command through serial
port after SIM908 is powered on and Unsolicited Result Code "RDY" is
received from serial port.
This means that before you send anything, you have to discard this data by reading it. I guess this is why you don't get CRLF when reading the response: you first get the HEX string or "RDY".
Then readBytesUntil() reads as many bytes as available (maxi 199 in my example above), stores them in the buffer. It stops reading when it encounters the byte 13 (i.e.CR). There is no need to loop on an index. The function returns the number of chars that could be read, and it doesn't put an ending 0 at the end of the buffer (i.e. no valid C string). If you want to use the function in another way than what I proposed, you must store the length returned, because you have no other way to find it out later on.

Reading from Arduino Serial seems to be very unstable

I have the following simple code to read commands sent from the computer to an Arduino Mega board:
void get_command()
{
char received;
int index = 0;
while (Serial.available() > 0) {
if (index < BUFFER_SIZE -1) {
received = Serial.read();
if (received == '\n') {
buffer[index] = '\0';
parse_command(buffer);
} else {
buffer[index++] = received;
}
} else {
Serial.println('666'); // buffer overflow
}
}
}
The commands are like A123 B123 C123 D123\n
Basically a sequence of space separated instructions, were each instruction starts by a letter and follows by a number. A command ends with a "\n".
Reading this seems to be very unstable. Sometimes I get the command perfectly, sometimes I miss the first letter, sometimes a pair of them, sometimes it starts by the number,...
I am sending the commands through the serial monitor, set to newline.
Before having this code, I'd simply check for the size using Serial.available, then Id wait a second to fill the buffer, and then I'd copy the buffer to a char*. This worked flawlessly. I am doing now this loop waiting for a \n, which seems more elegant, but it is being very unstable.
Code works by simply adding a delay(100) after the first Serial.available()
This is the code:
void get_command()
{
char received;
if (Serial.available() > 0) {
delay(100); // let the buffer fill up
int index = 0;
while (Serial.available() > 0) {
if (index < BUFFER_SIZE -1) {
received = Serial.read();
if (received != '\n') {
buffer[index] = received;
index++;
} else {
buffer[index] = '\0';
parse_command(buffer);
}
} else {
Serial.println('666'); // buffer overflow
}
}
}
}