base64 encoding .txt file

Hello! I would apreciate if anyone will help. Problem is when i want to encode some .txt file wich consists of few lines of text, but when i add more than four lines of text, it don't want to encode all of them. i think that's because of limited ram. Second problem is that i do not know how to print char to multiple lines, not in one long line.

Here is the code:

#include <SD.h>
#include <Base64.h>
#include <Streaming.h>
#include <SPI.h>

char o;
String b = ""; 
byte atm = 0;
char tok[200];
char gatavais[200] = "";
File myFile;
File base;
int i = 0;

void setup() {
   Serial.begin(115200);
   Serial.print("Initializing SD card...");
   pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);   
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
}
void loop()
{
  char var = Serial.read();
switch (var) {
 case 'j':
 {
   myFile = SD.open("joks.txt", FILE_READ);
            base = SD.open("base.txt", FILE_WRITE);
                   Serial.println(" nolasam joks.txt:");
                   while(myFile.available())
                   {
                     char aChar = myFile.read();	 
                    b += aChar;
	    	       Serial.print(b);
}
             atm = b.length(); 
           b.toCharArray(tok,atm);  
           base64_encode(gatavais, tok, atm);
           Serial << "Encoded:  " << gatavais << endl;
           base.print(gatavais);
           b = 0;
           tok[200] = NULL;
           atm = 0; 
   myFile.close();
   base.close();
}
  break;   
}

Which SD library are you using? According to the SD.open() reference page for the standard SD library, you can only have one file open at a time.

i am using standard SD library, but it works, ir can open two files at one time, but problem isn't there, problem is that i can not encode all joks.txt file , it encodes only first 3 lines.

Ok. If you are happy with that.

I notice you are not checking that the files open successfully.

How long are the lines? I suspect it is processing up to the 512 character block size that SD works in.

lines are some 30 characters long

Try adding tests that the files open successfully.

ardonis:
...
i think that's because of limited ram.
...
Here is the code:

...

String b = "";
...
                  b += aChar;
...

Concatenating one character at a time like this, into a String object, is almost certainly going to use up all your RAM. Try another method.

i do not know any other methods, problem is, that to correctly encode base64 it needs all file or text encoded at one time or it will encode incorrectly. could you suggest some another way to do this?

Well, not really. Base64 encoding turns 3 bytes into 4. So you can do 3 bytes at a time.

So You suggest, that i need to read all file by spliting it into 3 byte array and send each 3 byte part to encode ?

ardonis:
i am using standard SD library, but it works, ir can open two files at one time, but problem isn't there,

Given that your solution doesn't work, I think you're too quick to dismiss that as a problem. The standard SD library only supports access to one file at a time. Of course, since you don't check for success, you will have no idea which file you actually have open. Since the symptoms of clashing file handles could leave you with buffers incompletely read or written, it could easily produce the type of confusing behaviour that you're describing.

So, i need to modifie code, that there are no posibility that two files are open at a time or just find a library that supports access to more than one file at a time.

Somehow i almost found solution to my second problem , but still it is not perfect, with some issues.
here is code:

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

float u = 0;
File base;
int i = 30.0;
int sk = 0;
String inData;

void setup() {
   Serial.begin(115200);
   Serial.print("Initializing SD card...");
   pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);   
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
}
void loop()
{
  char var = Serial.read();
switch (var) {

case 'u':
   {
     Serial.println("lasa");
             base = SD.open("base.txt", FILE_READ);
            while(base.available())    {
              char a = base.read();
           inData += a;
           sk++;
           u = (float)sk/i;
             Serial.println(sk);
             Serial.println(u);        
           if (u == 1 || u == 2 || u == 3 || u == 4 || u == 5 || u == 6 || u == 7 || u == 8 || u == 9){
             Serial.print(inData);
             Serial.println();
             inData = 0;
           } 
           }
           base.close();
   }
    break; 
}
}

Concatenating one character at a time like this, into a String object, is almost certainly going to use up all your RAM. Try another method.

i do not know any other methods

Here's one:

/*----------------------------------------------------------------------------*/
/* addchr(buf,c) - add character to end of NUL-terminated string              */
/* Note: char array at buf must be large enough to add contain the added char */
/* Written by Morris Dovey (mrdovey@iedu.com)                                 */
/*----------------------------------------------------------------------------*/
char *addchar(char *buf,char c)
{  char *p = buf;                      /* Working output pointer              */
   while (*p) ++p;                     /* Find the terminating NUL character  */
   *p++ = c;                           /* Replace NUL with caller's char      */
   *p = '\0';                          /* Re-terminate the string             */
   return buf;                         /* Return pointer to start of string   */
}                                      /*  end: addchar()                     */
/*----------------------------------------------------------------------------*/

Reply to Morris Dovey.

thanks for code, but i am not quite sure if i understand how i need to use this code.

Instead of

...
String b = ""; 
...
                  b += aChar;
...

You can

...
char b[100] = "";
...
   addchar(b,aChar);
...

So far i understand, but as i am beginner, i do not know how to measure char length, with string it is very easy (String.length():wink:

Then I suggest you find a book or web site with instructional content and study to remedy your present lack of knowledge. The C++ String methods may be suitable for use on systems with sufficient memory to use wastefully, but aren't generally suitable for tiny systems like the Arduino.

"Easy" has very little value when it produces non-functional results.

Thanks for answers and advices. I think i will need some extra time to finish this program.

i managed to resolve my second problem, because it was easier than first one, now i am trying to solve first problem.

this code read a long line by reading each symbol and storing it at "inData" and at the same time int "sk" counts wich symbol from begging it is, to see if it has reached 60th symbol or 120th symbol or 180th symbol .... it simply divide "sk" with 60 and if obtained result is natural number , then we know that "inData" has stored 60 symbols and we cah print them out and clear "inData" and store to it next 60 symbols. to deal with last line if it is smaller than 60 symbols, it reads how large is file and subtract 60 from it and the compare obtained result with "sk", if sk is greater than obtained result then it print out last read symbols.

here is code that reads long line who is in text file and split it to multiple lines:

// adding libraries
#include <SD.h>
#include <Base64.h>
#include <Streaming.h>
#include <SPI.h>

float u = 0;    // define a float number, who will help to identifie if string "inData" contain 60 symbols
File myFile;
File base;	
float i = 60.0;  // define how long  lines will be. how much symbols will be on each line
int sk = 0;	// define int who will show how much symbols are read
String inData;	// define string where read symbols from base.txt will be kept


void setup() {
   Serial.begin(115200);
   Serial.print("Initializing SD card...");
   pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);   
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
}
void loop()
{
  char var = Serial.read();
switch (var) {

case 'u':
   {
     Serial.println("lasa");
             base = SD.open("base.txt", FILE_READ);
            while(base.available())    {  // reading file from start to end only once
              char a = base.read();   // store read value to "a"
           inData += a;  // storing read values to "inData"
           sk++; 		//for each read symbol from base.txt add +1 value to 
           u = (float)sk/i;		// here we calculate "u" ,who later will allow us to know when 60 characters or symbols are read from base.txt  
           if (u == floor(u)){   // if u is natural number, without any decimal places, do ...
             Serial.print(inData);  // print stored 60 characters and add new line symbole
             Serial.println(); // and add new line symbole
             inData = 0;
           } 
           else if ((base.size()-i) < sk) {  // if we read last line who are less than  60 symbols, print it too
                                             // it has been done by reading file size, 
				             //  then subtracting from it 60 symbols and
				             // obtained result comparing with "sk" 
				             // (int wich shows us how much symbols are read)
					     // if sk is greater than obtained result , 
					     // then we know that there are left less than 60 symbols
             Serial.print(inData);
             inData = 0;
           } 
           }
        
           base.close();
   }
    break; 
}
}

What is that mess with sk, u, i, and floor(u) supposed to be doing?

int i = 60.0;

Does this look reasonable to you?