Can Parola library display contiguous sentences/strings implemented with char[]?

I'm trying to display a quote (which can be very long/200+ chars) from a text file on an SD card onto an LED matrix. The quote is stored in a text file in pieces that are 100 characters long (I wrote a program that can modify the length, in characters, of each piece. Here is the format of that text file:

For a single quote:
Piece 1\r/n\0
Piece 2\r\n\0
...
Piece x\r\n\0(EOF)

x varies, depending on how long the quote is. However, each piece is of the same length. (Don't worry about remainders in cases where quotes don't break up evenly. Assume they are of the same length.)

In some cases 2 pieces may read like this:

"Strength of mind rests in sobriety for this keeps reason unclouded by passion. - Pythagoras"

And may be formatted in a text file like this:

"Strength of mind rests\r\n\0"
" in sobriety for this ke\r\n\0"
"eeps reason unclouded\r\n\0"
"by passion. - Pythagor\r\n\0"
"as_________________\r\n\0"

Each line is is stored in a character array and I would like to use Parola to display each string one immediately after the other. Is this possible?

Here is the code and text file, in case you want to work with it. Note that you need an SD card/Micro SD card, and the "U_QUOTES.txt". The code will generate a file called Q_Cached.txt":

/*
 * QuotesEditorV2 
 *   QuotesEditor.ino successfully retrieved a quote from a .txt file located on SD but storing the quote as
 *   a char string on the arduino's SRAM was impractical due to memory constraint. Teddy Roosevelt's quote
 *   (767 char) took up nearly 40% of RAM mem. 
 *
 *   QuotesEditorV2.ino solves the issue of SRAM memory constraint by leveraging the size of SD memory (~32GB).
 *   Instead of retrieving and storing to SRAM, we retrieve the quote in its entirety from a .txt file, 
 *   store it in an easily read format in another .txt file in SD memory, and read from that .txt file when 
 *   we want to display something. Effectively, we use SD memory to store what we would have stored in SRAM. 
 *
 *   Since memory is offchip, a shortcoming of this technique is that we increase memory access time. 
 *   Also, this technique requires the ability to open multiple files simultaneusly. If this isn't possible 
 *   (which might be the case since the Arduino does't have much RAM), we will require a small amount of SRAM
 *   to store data temporarily while we transfer a selected, reformatted quote. The size of SRAM needed for transfer
 *   should equal the size allocated to the data structure used with the Parola library functions to display
 *   a string. 

 * Notes:
 *   1. When the enter key is used in a text document, on a windows machine a \r (carriage return) 
 *      and \n (newline) pair is inserted. At the end of a file, an EOF character is inserted. 
 *          e.g.,  abcdefg\r\n 
 *   2. If the string is to be printed using print() or write(), it must be terminated using \0 character
 *      otherwise, the string to be printed will be printed with junk postfixed. 
 *          e.g.,  Serial.print("abcdefg\r\n");   gives the output "abcdefgXX" where XX is junk
 *          e.g.,  Serial.print("abcdefg\r\n\0"); gives the expected output
 */ 

#include <string.h>
#include <SPI.h>
#include <SD.h>

#define TRANSFER_SIZE 100   //How many characters per transfer 
#define QUOTE_NUM     3    //Which quote to select

int findQuoteStart(char src[], int targetQuote){
    File file; 
    char newChar; 
    int numEndlines = 0;
    int quote_startPosition = 0; 
    int quote_Index = (targetQuote*2)-2; //Number of endlines BEFORE the target quote begins

    file = SD.open(src, FILE_READ); 
    if(file){ 
      while((numEndlines <= quote_Index) && (numEndlines != quote_Index)){  
        newChar = file.read(); 
        if(newChar == '\n'){
          numEndlines++;
        }
        quote_startPosition++; //Update where we are in the document
      } 
    }else{Serial.println("ERROR: Failed to open file to find quote start.");}
    file.close();
    
    return quote_startPosition; 
}

int findQuoteSize(char src[], int startPos){
    int i = 0; 
    char newChar; 
    int numEndlines = 0; 
    int quoteSize = 0; //Includes \r,\n, 
    
    File file; 
    file = SD.open(src, FILE_READ); 
    file.seek(startPos); //Position cursor at start of quote  
    if(file){
      while((numEndlines <= 2) && (numEndlines != 2)){
         newChar = file.read(); 
         if(newChar == '\n'){
           numEndlines++;
         }
         quoteSize++; 
      } 
    }else{Serial.println("ERROR: Failed to open file to find quote size.");}
    file.close(); 
    
    return quoteSize; 
}

bool transferPiece(char src[], char newFile[], int startPos, int transferSize){
    File file;    
    char quotePiece[transferSize+3]; //Terminate each line in new text file with: \r\n
    int endlineReached=0; //If 1, write \0 to quotePieces[] that have yet been populated
    int eofReached=0;     //If 1, write \0 to quotePiecesp[ that have yet been populated
    int writeCounter=0;
    
    //Read from quotes text file 
    file = SD.open(src,FILE_READ);
    if(file){
       char newChar;    
       file.seek(startPos); //Go to where quote was left off
       quotePiece[transferSize] = '\r'; 
       quotePiece[transferSize+1] = '\n'; 
       quotePiece[transferSize+2] = '\0'; //Needed for print() functions
       
       while(writeCounter<transferSize){ 
         if((writeCounter<transferSize) && !eofReached && !endlineReached)
            newChar = file.read(); //Don't get new character if endline or eof reached
         switch(newChar){
             case 'EOF': //End of file reached
               eofReached=1; 
               break;
             case '\r': //Carriage return reached
               endlineReached=1;
             case '\n': //End of quote reached 
               endlineReached=1;        
             default: 
               if(!endlineReached && !eofReached){
                 quotePiece[writeCounter] = newChar; 
                 writeCounter++; 
               }else{
                 quotePiece[writeCounter] = '_';
                 writeCounter++; 
               }
               break; 
         }
         if((endlineReached || eofReached) && (writeCounter==transferSize))
           break; 
       }
       //Serial.write(quotePiece);        
       file.close(); 
    }else{Serial.println("Error: Could not transfer quote piece. Cache file doesn't exist");}
    
    //Write to the new file
       file = SD.open(newFile, FILE_WRITE); 
       if(file){
         file.seek(file.size()); //go to the end of the file
         file.write(quotePiece); 
         file.write('\r\n'); //???? quotePiece should have a newline
         file.close(); 
       }else{Serial.println("Error: Could not write substring of a quote to new text file");}  
    return 0; 
}

void transferQuote(char txtFile[], int quoteIndex, int strLength){
  
  if(quoteIndex>0){   
    int quote_StartPosition = findQuoteStart(txtFile, quoteIndex);
    int quote_Size = findQuoteSize(txtFile, quote_StartPosition); //In Bytes
    int numberPieces = (quote_Size/strLength)+1; //Number of lines in the new text file
    
    //Transfer Quotes{
    for(int i=0; i<numberPieces; i++){
       transferPiece("U_Quotes.txt","Q_Cached.txt", quote_StartPosition+i*strLength, strLength); 
    } 

  }else{Serial.println("Quote Transfer Failure: quoteIndex is less than or equal to 0!");}
}


void setup() {
  Serial.begin(9600); 
  
  //INITIATE SD
  //pinMode(10, OUTPUT); //SPI Requirement -- NOT NEEDED FOR SOME REASON?INCLUSION CAUSES CRASH
  if(SD.begin(4) == true){Serial.println("SD init success"); Serial.println();}
    else if(SD.begin(4) == false){Serial.println("SD init failure"); Serial.println();}
    
  //Delete old, create new text file
    File file; 
    if(SD.exists("Q_Cached.txt"))  //Delete old cached file if it exists
    {
       Serial.println("Q_Cache exists. Removing it now."); 
       SD.remove("Q_Cached.txt");
    }
    file = SD.open("Q_Cached.txt", FILE_WRITE); //Make new text file
    file.close(); 
    Serial.println("Done deleting old, creating new");
    
  //Transfer the quote of interest to a new file
    transferQuote("U_Quotes.txt", QUOTE_NUM, TRANSFER_SIZE);
}

void loop() {}

U_QUOTES.txt (5.3 KB)

Parola will display whatever you give it. If you want to display each piece continuously one after the other then you need to make sure that you create one long string from the text in the file (ie, read each line and remove the end of line terminations, concatenating the string. Pass this to the library and it will display the text string. You just need to make sure that the buffers are all sized large enough for the text you will display.

If you want to display the pieces separately, then you may need to make sure that the lines end at the end of words. In your example you have the "ke" in "keep" on one line and the rest on another.

One additional thought that gives you complete control on how the text is displayed is to use the MD_MAX72xx library directly. Provided you only want to display a scrolling message, there is an example on how to do this with the library. You can then read the file and load each character as you need it.

I decided that this was a good candidate as an example program for the MD_MAX72xx library so I have coded it up. The ino file is attached to this post and will be part of the next release. In the meantime you can download the example from the source code section of the library repository (link below).

MD_MAX72xx_Message_SD.ino (5.19 KB)