// Test to check for bugs reading from one file and
// writing to a second file.
// MJB 8/28/2024
#include <Arduino.h>
#include <SD.h>
#include <MTP_Teensy.h>
#include "TimeLib.h"
const char compileTime[] = " Compiled on " __DATE__ " " __TIME__;
void setup() {
elapsedMillis Leptontimer;
// Wait for Serial connection--but only for 1/2 second
Serial.begin(9600);
//wait 2 seconds for serial to start up
delay(2000);
Serial.printf("\n\nT4.1 File Read/Write Test %s\n", compileTime);
Serial.println("Type '?' to list commands.");
InitSD(); // Starts up SD Card and MTP
delay(1000);
}
void loop() {
char ch;
if(Serial.available()){
ch = Serial.read();
switch(ch){
case '?' :
Serial.println("1: write the original test file (TFile1.txt).\n");
Serial.println("2: Read TFile1, modify data and write to TFile2.\n");
Serial.println("3: Show the first 20 lines of TFile2.\n");
Serial.println("d: Show SD Card directory.\n");
Serial.println("m: Update MTP so you can see new files on your PC.\n");
Serial.println("r: Remove TFile1.txt and TFile2.txt if they exist.\n");
break;
case '1' : WriteFile1();
break;
case '2' : Read1Write2();
break;
case '3' : Show20();
break;
case 'd' : SDDir();
break;
case 'm' : MTPReset();
break;
case 'r' : RemoveFiles();
break;
default: Serial.println("Oops, Invalid command. Type '?' to see valid commands.\n");
}
}
MTP.loop();
}
// Write a million 32-character lines to file TFile1.txt
void WriteFile1(void){
FsFile tfile1; // Used FsFile so I can pre-allocate space
uint32_t stlen;
uint32_t i,ltime;
elapsedMillis ftimer;
// String is 32-characters. Terminating 0x00 isn't sent.
// String ends with line feed to display nicely.
const char *string1 = {"OLD: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"};
stlen = strlen(string1);
Serial.printf("Writing 1,000,000 copies of: string1 with length %lu bytes\n", stlen);
tfile1 = SD.sdfs.open("TFile1.txt", O_WRITE | O_CREAT);
if(tfile1.preAllocate(stlen * 1000000)){
Serial.println("Preallocation successful");
} else Serial.println("Preallocation failed!");
delay(1);
// This simple write routine is NOT optimized for speed
for(i=0; i< 1000000; i++){
tfile1.write(string1, stlen);
if((i%100000) == 0){ // show progress ever 100,000 lines to keep user happy
ltime = ftimer;
Serial.printf("Line %6lu at %6.3f\n", i,(float)ltime/1000.0);
}
}
tfile1.truncate(); //Truncate in case we don't write all we preallocated.
tfile1.close();
}
// Remove files TFile1.txt and TFile2.txt if they exist.
// Writing to an existing file is much slower, as pre-allocation fails.
void RemoveFiles(void){
if(SD.sdfs.exists("TFile1.txt")) SD.sdfs.remove("TFile1.txt");
if(SD.sdfs.exists("TFile2.txt")) SD.sdfs.remove("TFile2.txt");
Serial.println("TFile1.txt and TFile2.txt removed if they existed\n");
}
// Read a million 32-character lines from file TFile1.txt
// Modify the data and write to TFile2.txt
#define NUMLINES 1000 // Number of lines to read and write each time
char block1[32 * NUMLINES];
void Read1Write2(void){
FsFile tfile1, tfile2; // Used FsFile so I can pre-allocate space
uint32_t stlen, blklen;
uint32_t i,j,lnstart,ltime;
elapsedMillis ftimer;
stlen = 32;
blklen = stlen * NUMLINES;
Serial.println("reading 1,000,000 copies string1, modifying, and writing to TFile2.txt\n");
Serial.printf("Reading and writing in blocks of %lu lines.\n", NUMLINES);
tfile1 = SD.sdfs.open("TFile1.txt", O_READ);
tfile2 = SD.sdfs.open("TFile2.txt", O_WRITE | O_CREAT);
if(tfile2.preAllocate(stlen * 1000000)){
Serial.println("Preallocation of TFile2.txt successful");
} else Serial.println("Preallocation failed!");
delay(1);
for(i=0; i< 1000000/NUMLINES; i++){
tfile1.read(block1, blklen); // read the whole block
// change first three letters from 'OLD' to 'NEW' for each string in block
for(j= 0; j< NUMLINES; j++ ){
lnstart = j * stlen;
block1[lnstart] = 'N'; block1[lnstart+1] = 'E'; block1[lnstart+2] = 'W';
}
delayMicroseconds(2);
tfile2.write(block1, blklen);
if((i%100) == 0){ // show progress every 100000 lines to keep user happy
ltime = ftimer;
Serial.printf("Line %6lu at %6.3f\n", i*NUMLINES,(float)ltime/1000.0);
}
}
tfile1.close();
tfile2.truncate(); //Truncate in case we don't write all we preallocated.
tfile2.close();
}
// Show the first 20 lines of TFile2.txt
void Show20(void){
FsFile tfile2;
uint32_t stlen;
uint32_t i;
// String is 32-characters. Terminating 0x00 isn't sent.
// String ends with line feed to display nicely.
char string1[32];
stlen = 32;
Serial.println("Showing first 20 lines of TFile2.txt\n");
tfile2 = SD.sdfs.open("TFile2.txt", O_READ);
for(i= 0; i< 20; i++){
tfile2.read(string1, stlen);
Serial.println(string1);
}
Serial.println();
}
time_t get_TeensyTime(void) {
return Teensy3Clock.get();
}
/* ------------------------------------------------------------------------------
User-provided date time callback function.
See SdFile::dateTimeCallback() for usage.
------------------------------------------------------------------------------*/
void dateTime(uint16_t *date, uint16_t *time) {
// use the year(), month() day() etc. functions from timelib
// return date using FAT_DATE macro to format fields
*date = FAT_DATE(year(), month(), day());
// return time using FAT_TIME macro to format fields
*time = FAT_TIME(hour(), minute(), second());
}
void InitSD(void){
if (SD.begin(BUILTIN_SDCARD)) {
setSyncProvider(get_TeensyTime); // helps put time into file directory data
// set date time callback function
SdFile::dateTimeCallback(dateTime);
MTP.addFilesystem(SD, "SD Card");
Serial.println("Added SD card using built in SDIO");
} else {
Serial.println("No SD Card");
}
// mandatory to begin the MTP session.
MTP.begin();
}
void MTPReset(void){
MTP.send_DeviceResetEvent();
Serial.println("Requested PC to reset MTP Directory");
}
void SDDir(void){
Serial.println("SD Card Directory");
SD.sdfs.ls(LS_DATE | LS_SIZE);
Serial.println();
}