LittleFS media data Save or Restore from SD card
Using one of these two commented xferSD() calls:
Code:
#define destLFS 1
#define destSD 2
// xferSD( destSD ); // do MediaTransfer LFS TO SD
// xferSD( destLFS ); // do MediaTransfer SD TO LFS
NOTE: In Any LittleFS/examples/Integrity place it in setup() before this line to prevent errors reported in filecount:
Code:
filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk
And this piece of INO code { has to be INO to share the global "myfs" - unless it is made Media specific and extern in user code if not visibly global}.
Above placed in any of the LittleFS/examples/Integrity using this sketch code will 'recursively' copy as indicated between LFS and SD Media - all files and directories. Tested on PSRAM.ino and PROG.ino.
Removed most debug spew, only two cases of error displayed.
Example will use SD directory: "\LFS_CPY". SD Media of GB bigger than any LFS Media's MB's, so makes no sense to store/copy all of SD root. This SD directory name can be tested and changed programmatically {with change in xferSD()} to allow saving multiple runs of data if desired.
NOTE: This code has a DEBUG copy of makeRootDirsTest() that adds extra files and dirs to prove function when sending LFS data to SD. It is not needed in use.
For a quick test in Integrity examples put in setup - Fill the LFS Media using '3' to make some test files {perhaps "S" or "B"}, then restart to push to SD with destSD.
Then do a 'w' to Wipe all LFS Media files, and rebuild with destLFS and the target SD card that should hold the files. It will then restore to any LFS media - though it does not check for SIZE issues if the contents won't restore to Media in use.
Code:
#include <SD.h>
void xferSD( int copyType ) { // do MediaTransfer with SD
#define destLFS 1
#define destSD 2
static bool initSD = true;
Serial.print("Initializing SD card...");
if ( initSD && !SD.begin(BUILTIN_SDCARD)) { // see if the card is present and can be initialized:
Serial.println("\n\n SD Card failed, or not present - Cannot do Xfer\n");
}
else {
initSD = false;
Serial.println("card initialized.\n\n");
if ( copyType == destSD ) {
// char szSDdir[] = "/"; // COPY to SD ROOT
char szSDdir[] = "LFS_CPY/"; // COPY to SD subdirectory
if ( '/' != szSDdir[0] )
SD.mkdir( szSDdir );
makeRootDirsTest(); // BUGBUG DEBUG make extra subdirs and files to show function
Serial.println("\n STARTING :: LittleFS copy to SD card XFER ...\n\n");
mediaTransfer( myfs.open("/"), szSDdir, destSD ); // TOO SD
Serial.println("\n LittleFS copy to SD card XFER COMPLETE.\n\n");
}
else {
char szLFSdir[] = "/";
char szSDdir[] = "LFS_CPY";
Serial.println("\n STARTING :: SD card copy to LittleFS XFER ...\n\n");
mediaTransfer( SD.open(szSDdir), szLFSdir, destLFS ); // FROM SD
Serial.println("\n SD card copy to LittleFS XFER COMPLETE.\n\n");
}
}
}
void mediaTransfer(File dir, char* szDir, int destMedia) {
char szNewDir[36];
while (true) {
File entry = dir.openNextFile();
if (! entry) {
break;
}
if (entry.isDirectory()) {
strcpy( szNewDir, szDir);
if ( destMedia == destLFS )
myfs.mkdir( szNewDir );
else
SD.mkdir( szNewDir );
strcat( szNewDir, entry.name());
if ( destMedia == destLFS ) myfs.mkdir( szNewDir );
else SD.mkdir( szNewDir );
strcat( szNewDir, "/");
mediaTransfer(entry, szNewDir, destMedia);
} else {
uint64_t fileSize, sizeCnt = 0, xfSize = 1;
char mm[512];
strcpy( szNewDir, szDir);
strcat( szNewDir, entry.name() );
File dataFile;
if ( destMedia == destLFS ) {
dataFile = myfs.open(szNewDir, FILE_WRITE_BEGIN);
}
else {
dataFile = SD.open(szNewDir, FILE_WRITE_BEGIN);
}
if ( !dataFile )
Serial.print("\td_FILE: NOT open\n");
fileSize = entry.size();
while ( entry.available() ) {
if ( fileSize < sizeCnt ) break;
if ( fileSize - sizeCnt >= sizeof(mm) ) xfSize = sizeof(mm);
else xfSize = fileSize - sizeCnt;
entry.read( &mm , xfSize );
dataFile.write( &mm , xfSize );
sizeCnt += xfSize;
}
if (fileSize != sizeCnt ) {
Serial.print("\n File Size Error:: ");
Serial.println( entry.name() );
}
dataFile.close();
}
entry.close();
}
}
void makeRootDirsTest() { // TEST DEBUG CODE
char szDir[36];
for ( uint32_t ii = 1; ii <= NUMDIRS; ii++ ) {
sprintf( szDir, "/%lu_dir", ii );
myfs.mkdir( szDir );
sprintf( szDir, "/%lu_dir/aFile.txt", ii ); // BUGBUG DEBUG
file3 = myfs.open(szDir, FILE_WRITE);
file3.write( szDir , 12 );
file3.close();
sprintf( szDir, "/%lu_dir/TEST", ii ); // BUGBUG DEBUG
myfs.mkdir( szDir ); // BUGBUG DEBUG
sprintf( szDir, "/%lu_dir/TEST/bFile.txt", ii ); // BUGBUG DEBUG
file3 = myfs.open(szDir, FILE_WRITE);
file3.write( szDir , 12 );
file3.close();
}
filecount = printDirectoryFilecount( myfs.open("/") ); // Set base value of filecount for disk
}
This function does all the work when properly called as desired: void mediaTransfer(File dir, char* szDir, int destMedia)
A practical example of LitteFS on Flash chip, with performance comparision.
Quote:
Originally Posted by
defragster
From quoted linked post:
Hi all,
please let me know if I'd better start a new post instead of writing here...
I went on with my tests, and wish to share the (even partial) result:
- copying .raw audio files from SD to Flash chip: works fine
- creating an example for playing a .raw audio file from LittleFS: ok works fine
- comparing the read-time reading audio files, using traditional SerialFlash solution and LittleFS file system.
This is the sketch using LittleFS, main code:
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>
#include "play_serialflash_raw_LittleFS.h"
AudioPlaySerialflashRaw_LittleFS playFlash;
AudioOutputI2S audio_out;
AudioConnection patchCord1(playFlash, 0, audio_out, 0);
AudioConnection patchCord2(playFlash, 0, audio_out, 1);
AudioControlSGTL5000 board;
void setup()
{
board.enable();
board.volume(0.2);
AudioMemory(5);
}
void loop()
{
playFlash.play("41.raw");
delay(10000);
}
AudioPlaySerialflashRaw_LittleFS.h
Code:
#ifndef play_serialflash_raw_LittleFS_h_
#define play_serialflash_raw_LittleFS_h_
#include "Arduino.h"
#include <AudioStream.h>
#include "LittleFS.h"
class AudioPlaySerialflashRaw_LittleFS : public AudioStream
{
public:
AudioPlaySerialflashRaw_LittleFS(void) : AudioStream(0, NULL)
{
begin();
}
void begin(void);
bool play(const char *filename);
void stop(void);
bool isPlaying(void) { return playing; }
uint32_t positionMillis(void);
uint32_t lengthMillis(void);
virtual void update(void);
private:
LittleFS_SPIFlash myfs; // ha i metodi di LittleFS che ha i metodi di FS
File rawfile; // SerialFlashFile rawfile;
uint32_t file_size;
volatile uint32_t file_offset;
volatile bool playing;
};
#endif
AudioPlaySerialflashRaw_LittleFS.cpp
Code:
#include <Arduino.h>
#include "play_serialflash_raw_LittleFS.h"
#include "spi_interrupt.h"
void AudioPlaySerialflashRaw_LittleFS::begin(void)
{
playing = false;
file_offset = 0;
file_size = 0;
}
bool AudioPlaySerialflashRaw_LittleFS::play(const char *filename)
{
stop();
AudioStartUsingSPI();
// rawfile = SerialFlash.open(filename);
// if (!rawfile)
if(!myfs.begin(6))
{
Serial.println("unable to access Flash");
AudioStopUsingSPI();
return false;
}
rawfile = myfs.open(filename);
if(!rawfile)
{
Serial.println("unable to access file");
AudioStopUsingSPI();
return false;
}
file_size = rawfile.size();
file_offset = 0;
//Serial.println("able to open file");
playing = true;
return true;
}
void AudioPlaySerialflashRaw_LittleFS::stop(void)
{
__disable_irq();
if (playing)
{
playing = false;
__enable_irq();
rawfile.close();
AudioStopUsingSPI();
}
else
{
__enable_irq();
}
}
void AudioPlaySerialflashRaw_LittleFS::update(void)
{
unsigned int i, n;
audio_block_t *block;
unsigned long T0;
int time_lap;
// only update if we're playing
if (!playing)
return;
// allocate the audio blocks to transmit
block = allocate();
if (block == NULL) return;
if (rawfile.available())
{
T0 = micros();
n = rawfile.read(block->data, AUDIO_BLOCK_SAMPLES*2);
time_lap=micros()-T0;
file_offset += n;
for (i=n/2; i < AUDIO_BLOCK_SAMPLES; i++)
{
block->data[i] = 0;
}
transmit(block);
Serial.println(time_lap);
}
else
{
rawfile.close();
AudioStopUsingSPI();
playing = false;
}
release(block);
}
#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592
uint32_t AudioPlaySerialflashRaw_LittleFS::positionMillis(void)
{
return ((uint64_t)file_offset * B2M) >> 32;
}
uint32_t AudioPlaySerialflashRaw_LittleFS::lengthMillis(void)
{
return ((uint64_t)file_size * B2M) >> 32;
}
where this part of code allows to measure the reading performance:
Code:
time_lap=micros()-T0;
file_offset += n;
for (i=n/2; i < AUDIO_BLOCK_SAMPLES; i++)
{
block->data[i] = 0;
}
transmit(block);
Serial.println(time_lap);
Now, using a T4.1@600MHz, and AUDIO_BLOCK_SAMPLES = 128, if I compare the reading performance of the previous code, with a similar example which uses AudioPlaySerialflashRaw:
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>
AudioPlaySerialflashRaw playFlash;
AudioOutputI2S audio_out;
AudioConnection patchCord1(playFlash, 0, audio_out, 0);
AudioConnection patchCord2(playFlash, 0, audio_out, 1);
AudioControlSGTL5000 board;
void setup()
{
board.enable();
board.volume(0.2);
AudioMemory(5);
SerialFlash.begin(6);
}
void loop()
{
playFlash.play("41.raw");
delay(10000);
}
the update() time in microseconds is:
AudioPlaySerialflashRaw example:
Code:
50
50
50
50
50
50
50
50
50
50
50
...
... and so on
AudioPlaySerialflashRaw_LittleFS example:
Code:
77
78
696
78
77
78
77
78
77
78
77
78
77
78
77
78
77
78
773
78
77
78
77
78
77
78
77
78
78
78
78
78
78
78
696
78
78
78
This report shows that with LittleFS the reading time is 55% higher for 15 cycles, and after that (should be after 4KB of data) the next cycle is 10 times longer; for my application, where audio polyphony is up to 16, this slow reading time (maybe due to some "jumps" among sectors??) is incompatible