Display text by line from SDCARD

Hello again..

I want to print.lcd a 6 digit character text line from a sdcard. file name code.txt

abc123
abc456
abc789

and so on..

i want display on I2C LCD "abc123" on the first button press..
then "abc456" on 2nd press..
then "abc789" on thrid press and so on..

does anyone here know how to code this or where to start? ::slight_smile:

Where to start ?

Can you display text on the LCD ?
Do you know how to read an input pin ?
You need to detect when the button becomes pressed rather than when it is pressed
So, look at the StateChangeDetection example in the IDE
Can you read the complete data from the SD card and print it to the Serial monitor ?
Do you know how to detect the end of a line of data ?

I know and practice how to print on lcd. I also know to to set pin input output also button state.. i also know how to use serial monitor..

My problem is the code on how to read the content of a text file by line..

Line 1
Line 2
Line 3

Then display one line at a time on the lcd on a button press .

My problem is the code on how to read the content of a text file by line..

That is why I asked

Do you know how to detect the end of a line of data ?

HINT : what does each line of the file end with ?

The code below is a modification of the SD example Dumpfile and will get you started. It examines each character as it datafile.reads() it, and if it's a CR (\r) or LF (\n) prints that out. If it's a LF, which signifies the end of a line, it waits for a button press before reading the next character.

//this version displays one line at a time with their CRs and LFs
//      button from thePin to ground, with input pullup

/*
  SD card file dump

  This example shows how to read a file from the SD card using the
  SD library and send it over the serial port.

  The circuit:
   SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)

  created  22 December 2010
  by Limor Fried
  modified 9 Apr 2012
  by Tom Igoe

  This example code is in the public domain.

*/
#include <SPI.h>
#include <SD.h>

const int chipSelect = 4;
char theChar;
byte thePin = 2;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  pinMode(thePin, INPUT_PULLUP);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
  Serial.println("Press button to see each line");

  // 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("DATALOG1.TXT");

  // if the file is available, raed it:
  if (dataFile)
  {
    while (dataFile.available())
    {
      theChar = dataFile.read();

      if (theChar == '\n')
      {
        Serial.print("LF");
        while (digitalRead(thePin)) {} //wait until button pressed
        delay(500);                    //poor man's debounce
      }
      else if (theChar == '\r')
      {
        Serial.print("CR");
      }
      //else Serial.write(theChar); //ignores the cr and lf
      Serial.write(theChar);    //honours the cr and lf

    }
    Serial.println("End Of File");
    dataFile.close();
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog1.txt");
  }
}

void loop() {
}

If btw you open your file in Notepad++ you can see the line endings by going View > Show symbol and clicking Show end of line. Here's what my file looks like:

cr_lf.GIF

cr_lf.GIF

gantner_street:
The code below is a modification of the SD example Dumpfile and will get you started. It examines each character as it datafile.reads() it, and if it's a CR (\r) or LF (\n) prints that out. If it's a LF, which signifies the end of a line, it waits for a button press before reading the next character.

//this version displays one line at a time with their CRs and LFs

//      button from thePin to ground, with input pullup

/*
  SD card file dump

This example shows how to read a file from the SD card using the
  SD library and send it over the serial port.

The circuit:
  SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)

created  22 December 2010
  by Limor Fried
  modified 9 Apr 2012
  by Tom Igoe

This example code is in the public domain.

*/
#include <SPI.h>
#include <SD.h>

const int chipSelect = 4;
char theChar;
byte thePin = 2;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  pinMode(thePin, INPUT_PULLUP);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

Serial.print("Initializing SD card...");

// see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
  Serial.println("Press button to see each line");

// 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("DATALOG1.TXT");

// if the file is available, raed it:
  if (dataFile)
  {
    while (dataFile.available())
    {
      theChar = dataFile.read();

if (theChar == '\n')
      {
        Serial.print("LF");
        while (digitalRead(thePin)) {} //wait until button pressed
        delay(500);                    //poor man's debounce
      }
      else if (theChar == '\r')
      {
        Serial.print("CR");
      }
      //else Serial.write(theChar); //ignores the cr and lf
      Serial.write(theChar);    //honours the cr and lf

}
    Serial.println("End Of File");
    dataFile.close();
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog1.txt");
  }
}

void loop() {
}




If btw you open your file in Notepad++ you can see the line endings by going View > Show symbol and clicking Show end of line. Here's what my file looks like:

![cr_lf.GIF|221x105](upload://68YRIJdDT0KvkIhfk1Rj2UayuKb.gif)

Gantner this is very informative.. finally i can start something.. i will try this and let you know if this works..

By the way . How can i apply eeprom here so it will read the line where it stops incase of a power interruption?

projectdimpreza:
finally i can start something..

Finally? You could have taken the hint from earlier posts, that there must be a way to delimit a line, since all an SD card (or any file in fact) has is characters not lines. You are active in this thread here, where instead of asking the op there for their code in #18 you could have looked in a bit more detail. Reply #8 there already looks for the \n character:

ch = file.read();
...
 else if(ch == '\n')

projectdimpreza:
i will try this and let you know if this works..

It does work, at least for printing to the serial monitor. You will just need to add the lcd stuff.

projectdimpreza:
How can i apply eeprom here so it will read the line where it stops incase of a power interruption?

I'd be more inclined to engineer a fail-safe power supply like a battery backup. But now you know how to see lines, you could presumably increment a counter and store its value in the eeprom. Then on power up read it back and count ahead that number of \n's. But one thing at a time :wink:

gantner_street:
It does work, at least for printing to the serial monitor. You will just need to add the lcd stuff.

  if (dataFile)

{
    while (dataFile.available())
    {
      theChar = dataFile.read();

if (theChar == '\n')
      {
        Serial.print("LF");
        lcd.setCursor(0,3);
        lcd.print("LF");
        while (digitalRead(thePin)) {} //wait until button pressed
        delay(500);                    //poor man's debounce
      }
      else if (theChar == '\r')
      {
        Serial.print("CR");
        lcd.setCursor(0,3);
        lcd.print("CR");
      }

I know my code is wrong.. although its displaying the line everytime i press button only on serial monitor.. but on my LCD display it just display "LF" and not doing anything on a button press..

From a Private Message (rather keep it in the forum?):

projectdimpreza:
sorry i dont really understand what CR LF \n \r in the code or what do they do..

Think of a printer that prints characters, like an old school dot matrix. They receive characters one-by-one, so thet need to know when to a) go back to the left (that's the Carriage Return) and b) click the paper forward one line (that's the Line Feed). In the data those are represented by characters 0D in hex (decimal 13) and 0A (10) which are further represented in strings as '\r'and '\n'.

So on a printer, and by extension the serial monitor, it prints characters on one line until it gets those controls.

Since the file on the SD is nothing more or less than a stream of characters, you need to look for those explicitly so that you know where the breaks between the lines are. The serial monitor knows about them, and handles them as expected; the lcd doesn't- it doesn't mimic a printer.

So your lcd print needs to print the actual characters, one by one, stopping at the LF until you press the button at which point you would need to move the cursor back to the start point and then do the next line. The lcd doesn't need to know about CR's and LF's, only you do, to split the stream and control the cursor pos.

I suspect that you need a line like the one I commented out, the one that ignores the CRLF. If you change the comments from this:

//else Serial.write(theChar); //ignores the cr and lf
Serial.write(theChar);    //honours the cr and lf

... to this:

else Serial.write(theChar); //ignores the cr and lf
//Serial.write(theChar);    //honours the cr and lf

... you will see everything prints on one long line on your screen. That's more like how the lcd will understand it, except you need to do the splitting up and waiting for the button press.

So try something like this:

else lcd.print(theChar); //ignores the cr and lf
//Serial.write(theChar);    //honours the cr and lf

I don't have time to work on this; you need to figure this out further yourself.

gantner_street:

else lcd.print(theChar); //ignores the cr and lf

//Serial.write(theChar);    //honours the cr and lf

This work displaying the code by line.. but lcd.setCursor is acting weird.
Every time i set the cursor.. it only display the last letter or number of the code. and not the whole code.

without the lcd.setCursor = "abc123"
with lcd.setCursor = "3" only

if i dont use a set.Cursor.. the problem is it will continue to create a line of codes on the lcd every button press.. what i want to achieve is to display one code at a time whenever a button press and so on..

how to display (theChar) on my desired lcd display position..?

This complete sketch prints to the serial monitor showing "CR" and "LF", and also displays just the "human" characters on line 1 of the LCD:

//this version displays one line at a time with their CRs and LFs
//      button from thePin to ground, with input pullup
//      this one has I2C LCD

/*
  SD card file dump

  This example shows how to read a file from the SD card using the
  SD library and send it over the serial port.

  The circuit:
   SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)

  created  22 December 2010
  by Limor Fried
  modified 9 Apr 2012
  by Tom Igoe

  This example code is in the public domain.

*/
#include <SPI.h>
#include <SD.h>

const int chipSelect = 4;
char theChar;
byte thePin = 2;

//lcd stuff
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  pinMode(thePin, INPUT_PULLUP);
  while (!Serial)
  {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // initialize the LCD
  lcd.begin();

  // Turn on the blacklight and print a message.
  lcd.backlight();
  lcd.setCursor(0, 0); //move along and down
  lcd.print("      Hello");
  lcd.setCursor(0, 1); //move along and down
  lcd.print("      World");
  delay(1000);
  lcd.clear();

  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
  Serial.println("Press button to see each line");

  // 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("DATALOG1.TXT");

  // if the file is available, raed it:
  if (dataFile)
  {
    while (dataFile.available())
    {
      theChar = dataFile.read();

      if (theChar == '\n')
      {
        Serial.print("LF");
        while (digitalRead(thePin)) {} //wait until button pressed
        delay(500);                    //poor man's debounce
        lcd.setCursor(0, 0);
      }
      else if (theChar == '\r')
      {
        Serial.print("CR");
      }
      else lcd.print(theChar); //ignores the cr and lf
      Serial.write(theChar);    //honours the cr and lf

    }
    Serial.println("End Of File");
    lcd.print("  End Of File   ");
    dataFile.close();
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog1.txt");
  }
}

void loop() {
}

gantner_street:
This complete sketch prints to the serial monitor showing "CR" and "LF", and also displays just the "human" characters on line 1 of the LCD:

//this version displays one line at a time with their CRs and LFs

//      button from thePin to ground, with input pullup
//      this one has I2C LCD

/*
  SD card file dump

This example shows how to read a file from the SD card using the
  SD library and send it over the serial port.

The circuit:
  SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)

created  22 December 2010
  by Limor Fried
  modified 9 Apr 2012
  by Tom Igoe

This example code is in the public domain.

*/
#include <SPI.h>
#include <SD.h>

const int chipSelect = 4;
char theChar;
byte thePin = 2;

//lcd stuff
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  pinMode(thePin, INPUT_PULLUP);
  while (!Serial)
  {
    ; // wait for serial port to connect. Needed for native USB port only
  }

// initialize the LCD
  lcd.begin();

// Turn on the blacklight and print a message.
  lcd.backlight();
  lcd.setCursor(0, 0); //move along and down
  lcd.print("      Hello");
  lcd.setCursor(0, 1); //move along and down
  lcd.print("      World");
  delay(1000);
  lcd.clear();

Serial.print("Initializing SD card...");

// see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
  Serial.println("Press button to see each line");

// 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("DATALOG1.TXT");

// if the file is available, raed it:
  if (dataFile)
  {
    while (dataFile.available())
    {
      theChar = dataFile.read();

if (theChar == '\n')
      {
        Serial.print("LF");
        while (digitalRead(thePin)) {} //wait until button pressed
        delay(500);                    //poor man's debounce
        lcd.setCursor(0, 0);
      }
      else if (theChar == '\r')
      {
        Serial.print("CR");
      }
      else lcd.print(theChar); //ignores the cr and lf
      Serial.write(theChar);    //honours the cr and lf

}
    Serial.println("End Of File");
    lcd.print("  End Of File  ");
    dataFile.close();
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog1.txt");
  }
}

void loop() {
}

This WORKS! only problem now is the LCD backlight wont turn ON.. maybe a LCD I2C address issue?

I'm pretty sure if the address was wrong it wouldn't print anything. When you say it works, you do mean you see output on the lcd, just no backlight?

Run the hello world sketch or similar for whatever library you have.

gantner_street:
I'm pretty sure if the address was wrong it wouldn't print anything. When you say it works, you do mean you see output on the lcd, just no backlight?

Run the hello world sketch or similar for whatever library you have.

It does WORK now i just add the word "POSITIVE" at the end of lcd library code..

before its displaying the code without backlight.. but now backlight is on..

I can now move on and apply eeprom so it can remember the last code it display..

And also need to add 4 more .txt file to the sd card.. each one have codes in it.. my goal is to open and read the .txt file that correspond to the number of pulse it detect..

One.txt will open if 5pulse
Two.txt will open if 10pulse
Three.txt will open if 15pulse

projectdimpreza:
It does WORK now i just add the word "POSITIVE" at the end of lcd library code..

Great news.

projectdimpreza:
my goal is to open and read the .txt file that correspond to the number of pulse it detect..

One.txt will open if 5pulse
Two.txt will open if 10pulse
Three.txt will open if 15pulse

Pulses of what?

You will have to handle the fact that on the way to reading 10 pulses, you go past 5.... :wink:

By the way, do you understand how this line works:

while (digitalRead(thePin)) {} //wait until button pressed

gantner_street:
Great news.

Pulses of what?

You will have to handle the fact that on the way to reading 10 pulses, you go past 5.... :wink:

Pulse coming from a coin acceptor.. from now i think you will have an idea what i am building lol.

gantner_street:
By the way, do you understand how this line works:

while (digitalRead(thePin)) {} //wait until button pressed

From what i understand is that its waiting for the button to be press before it display the code.. am i right?

projectdimpreza:
From what i understand is that its waiting for the button to be press before it display the code.. am i right?

Yes, but how it works is this. You will notice that the pinMode for the button is input_pullup, which means that the pin is normally held high by that internal resistor. Because the switch goes from the pin to ground, when you press the button the pin goes low. Now, this:

while (digitalRead(thePin))

... is actually a shorthand "cheat" for:

while (digitalRead(thePin)==HIGH)

So it will sit there re-reading the pin as long as the pin is high, that is unpressed, and will carry out the contents of the {} which is nothing. So it sits there doing nothing until the test for digitalRead(thePin)==HIGH fails, that is the pin is taken low by the button press.

You may / will need to take this so-called "blocking" code into account if you expect the system to respond to other stimuli while it's sitting on its hands.