Teensyduino File System Integration, including MTP and MSC

@defragster

The integrity sketch is posted anywhere but probably can push it up to MTP_Teensy for now. Really only meant as a development sketch. I did put in a couple of fixes for that sd card issue also added in flash6 but interesting thing is I can write a large file to flash5 but not flash6.

EDIT: Just added that updated sketch to MTP_Teensy.
 
@defragster

The integrity sketch is posted anywhere but probably can push it up to MTP_Teensy for now. Really only meant as a development sketch. I did put in a couple of fixes for that sd card issue also added in flash6 but interesting thing is I can write a large file to flash5 but not flash6.

EDIT: Just added that updated sketch to MTP_Teensy.

Thx Mike! I see that propagated to Kurt's MTP and will get to it.

Now running TD 1.56 b3: Given the CORES update what is the set of WIP from github that is needed to get all the WIP code to work?
 
Mike:
> in that sketch flash5 defined and not used
> flash6 used but not defined
--> seems okay changing to :: LittleFS_SPIFlash flash6;

Looks like it just needs MTP and USBHost_t36
Code:
teensy_size: Memory Usage on Teensy 4.0:
teensy_size:   FLASH: code:146492, data:21896, headers:8760   free for files:1854468
teensy_size:    RAM1: variables:52128, code:140072, padding:23768   free for local variables:308320
teensy_size:    RAM2: variables:5216  free for malloc/new:519072
Multiple libraries were found for "USBHost_t36.h"
 Used: C:\T_Drive\tCode\libraries\USBHost_t36
 Not used: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\USBHost_t36
Multiple libraries were found for "SD.h"
 Used: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\SD
 Not used: C:\T_Drive\Arduino_1.8.16_156\libraries\SD
Using library SD at version 2.0.0 in folder: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\SD 
Using library SdFat at version 2.1.0 in folder: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\SdFat 
Using library SPI at version 1.0 in folder: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\SPI 
[B]Using library MTP_Teensy at version 1.0.0 in folder: C:\T_Drive\tCode\libraries\MTP_Teensy 
Using library USBHost_t36 at version 0.1 in folder: C:\T_Drive\tCode\libraries\USBHost_t36[/B] 
Using library LittleFS at version 1.0.0 in folder: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\LittleFS 
      upload@10883550-Teensy  Uploading to board '10883550-Teensy' (Teensy 4.0)

Running:
Code:
Dump Storage list(4)
store:0 storage:10001 name:SDIO fs:2000bc70
store:1 storage:20001 name:Prog fs:2000bb98
store:2 storage:30001 name:MSC0-SSD_GPT120 fs:20009a70
store:3 storage:40001 name:MSC1-120GB_Ext fs:20009f40
 
I am just in the process of updating to the beta3...

Actually updated the Teensyduino, but waiting for run of current sketch to complete low level formatting of the Flash chips.

@defragster: as I tried to mention a day or two ago:
Other than MTP_Teensy and USBHost_t36 stuff, everything in this beta build should be up to date.

Warning: I have updated some the MTP_Teensy stuff with more debug output for SendObject.

Trying to figure out why we often will drop the MTP connections when we try to write to the SPI Flash board to Flash_5 and especially Flash_6...

So trying to output additional information like here is the last sendObject that failed on Flash_5...
Code:
MTPD::SendObject: len:22015988

MTPD::SendObject *** USB Read Timeout ***
 # USB Packets: 2556 total: 4 avg ms: 0 max: 1
 # Write: 640 total:10333 avg ms: 16 max: 218(1)
  >> >=100ms: 1 50:0 40:129 30:45 20:0 10:0
  >> Last write times
  1 40 6 43 6 43 6 43 6 43 6 39 7 39 6 40 6 40 6 40 6 39 6 40 6 43 6 42 6 47 6 39
>>>Total Time: 11405548
*MTPD::send_Event(4001)
So this run shows we did 640 writes before it failed:
The Maximum time it took for one write was 218ms and it was on the first write.
The last 32 writes took 1 40 6 43... ms.
So every other write took >40ms... Which implies that in this case we are writing 2K writes and erase blocks are 4K in size, so makes sense every other...
And we see that only that first write took > 100ms (tenth of second), and 129 of the write took > 40...

The last failure on Flash 6 had some similar information:
Code:
MTPD::SendObject: len:22015988

MTPD::SendObject *** USB Read Timeout ***
 # USB Packets: 484 total: 1 avg ms: 0 max: 1
 # Write: 122 total:3038 avg ms: 24 max: 48(120)
  >> >=100ms: 0 50:0 40:61 30:0 20:0 10:0
  >> Last write times
  1 7 48 7 42 7 43 6 44 7 43 6 45 6 44 7 44 7 43 6 43 6 43 7 44 6 43 7 43 6 44 7
>>>Total Time: 4120795
*MTPD::send_Event(4001)

Now to try building with the new release.

EDIT: After Low level format of 5(512JVEM)
Code:
19875 RESP:2001(RSP:OK)l: 24 T:11 : 60001 ffffffff 14
LP:19875 CMD: 100d(SEND_OBJECT)l: 12 T:12
MTPD::SendObject: len:22015988
 # USB Packets: 42999 total: 84 avg ms: 0 max: 1
 # Write: 10750 total:69454 avg ms: 6 max: 166(8173)
  >> >=100ms: 1 50:1 40:0 30:0 20:0 10:0
  >> Last write times
  6 7 6 7 6 7 5 8 5 7 6 7 7 6 8 6 7 5 8 6 7 6 7 5 7 6 7 6 7 6 7 6
>>>Total Time: 69562785

Which succeed... only took about 69.6 seconds to copy...
 
Last edited:
Follow on: Still need to figure out why SD at times still fails on large files...

Code:
LP:111748 CMD: 100d(SEND_OBJECT)l: 12 T:f
MTPD::SendObject: len:22015988

MTPD::SendObject *** USB Read Timeout ***
len 1012
 # USB Packets: 42997 total: 349 avg ms: 0 max: 1
 # Write: 10750 total:1312 avg ms: 0 max: 85(1825)
  >> >=100ms: 0 50:2 40:1 30:2 20:0 10:5
  >> Last write times
  0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1
>>>Total Time: 1728746
*MTPD::send_Event(4001)

This was to an SDIO Sandisk 32gb disk... Don't remember if this was the one that acted slow at times in previous stuff...

But again it more or less wrote the whole file in 1.73 seconds... So lots faster, but for some reason it said it was still waiting for 1012 bytes of data ...
Will reboot it and then download it and compare to see if missing stuff is at end or were other packets dropped.

EDIT: They compare the same?
 
...

@defragster: as I tried to mention a day or two ago:
Other than MTP_Teensy and USBHost_t36 stuff, everything in this beta build should be up to date.

Warning: I have updated some the MTP_Teensy stuff with more debug output for SendObject.

...

Indeed - p#528 noted I got a working build with just those two. Failure before beta 3 was trying to assemble the various parts.

Got the MTP update - will move over and build.


Working in USB_MTP_IntegrityCheck.ino
> Seeing some oddity on restarting - no drives in Explorer 1 or 2 times - noteusing the T_4.0 on FRDM4236 board w/USBHost
-> realized can leave the HUB switch off and see data because the powered USBHost Hub backfeeds with no host switch
-> PCB VIN<>VUSB not cut ( as it should be but then no way to use without powered hub as there is a diode on the pins for switch )
> one 'B'ig file showed SLOW dots - but gave a normal looking time to GPT SSD - compared to first "B" - but maybe I wasn't watching ...
 
Note: it is not uncommon if your sketch adds several storage objects that the initial directory may be empty. But refresh (F5) or clicking on it in the folder view should then show up the items...

This happens as I try to make sure that we answer MTP on the host quick enough before it decides to bail on us.
 
Note: it is not uncommon if your sketch adds several storage objects that the initial directory may be empty. But refresh (F5) or clicking on it in the folder view should then show up the items...

This happens as I try to make sure that we answer MTP on the host quick enough before it decides to bail on us.

Will keep that in mind - seems I tried something that and still empty.

Will be rebuilding with new debug version soon. Changing links over for sublime to clean new IDE/TD folder slowed me down. And have to make a home visit now ...
 
Follow on: Still need to figure out why SD at times still fails on large files...

Code:
LP:111748 CMD: 100d(SEND_OBJECT)l: 12 T:f
MTPD::SendObject: len:22015988

MTPD::SendObject *** USB Read Timeout ***
len 1012
 # USB Packets: 42997 total: 349 avg ms: 0 max: 1
 # Write: 10750 total:1312 avg ms: 0 max: 85(1825)
  >> >=100ms: 0 50:2 40:1 30:2 20:0 10:5
  >> Last write times
  0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1
>>>Total Time: 1728746
*MTPD::send_Event(4001)

This was to an SDIO Sandisk 32gb disk... Don't remember if this was the one that acted slow at times in previous stuff...

But again it more or less wrote the whole file in 1.73 seconds... So lots faster, but for some reason it said it was still waiting for 1012 bytes of data ...
Will reboot it and then download it and compare to see if missing stuff is at end or were other packets dropped.

EDIT: They compare the same?

Going to start with this one. Assuming since its a 32GB SD card its formatted as FAT32. Under that assumption transferred several large files (copy multiple large files at the same time are hit and miss on whether they work), so these were transferred indiviually:
Code:
MTPD::SendObject: len:23672380
 # USB Packets: 46235 total: 915 avg ms: 0 max: 1
 # Write: 11559 total:1362 avg ms: 0 max: 152(6097)
  >> >=100ms: 1 50:5 40:2 30:2 20:0 10:4
  >> Last write times
  0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
>>>Total Time: 2403160
SendObject() returned true
Code:
MTPD::SendObject: len:23672380
 # USB Packets: 46235 total: 332 avg ms: 0 max: 1
 # Write: 11559 total:2530 avg ms: 0 max: 298(10913)
  >> >=100ms: 2 50:10 40:0 30:0 20:0 10:7
  >> Last write times
  0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
>>>Total Time: 3306216
SendObject() returned true
yes this is dup of the previous one with different file name.
Code:
MTPD::SendObject: len:22015988
 # USB Packets: 42999 total: 216 avg ms: 0 max: 1
 # Write: 10750 total:2222 avg ms: 0 max: 168(1393)
  >> >=100ms: 3 50:0 40:1 30:11 20:2 10:4
  >> Last write times
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
>>>Total Time: 2480481
SendObject() returned true
and
Code:
MTPD::SendObject: len:1439758
 # USB Packets: 2812 total: 21 avg ms: 0 max: 1
 # Write: 704 total:110 avg ms: 0 max: 35(417)
  >> >=100ms: 0 50:0 40:0 30:1 20:0 10:0
  >> Last write times
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0
>>>Total Time: 160474
SendObject() returned true

Did a format of the SD Card in builtin and no issues and it transferred the file again.
 
@KurtE
without doing a low level format I copied over several files:
Code:
MTPD::SendObject: len:1037303
 # USB Packets: 2026 total: 5 avg ms: 0 max: 1
 # Write: 507 total:5389 avg ms: 10 max: 51(456)
  >> >=100ms: 0 50:1 40:57 30:0 20:0 10:0
  >> Last write times
  3 42 6 41 7 42 6 42 6 41 6 41 6 44 6 43 6 42 6 40 6 43 6 42 6 44 6 43 6 43 6 44
>>>Total Time: 5405578
SendObject() returned true


MTPD::SendObject: len:307693
 # USB Packets: 600 total: 0 avg ms: 0 max: 0
 # Write: 151 total:994 avg ms: 6 max: 8(1)
  >> >=100ms: 0 50:0 40:0 30:0 20:0 10:0
  >> Last write times
  2 7 6 7 5 7 6 7 6 7 6 8 6 7 6 7 6 7 6 8 5 8 6 8 6 7 6 7 6 8 6 7
>>>Total Time: 1068560
SendObject() returned true



MTPD::SendObject: len:22015988
[COLOR="#FF0000"]MTPD::SendObject *** USB Read Timeout ***
[/COLOR] # USB Packets: 500 total: 3 avg ms: 0 max: 1
 # Write: 126 total:3060 avg ms: 24 max: 47(14)
  >> >=100ms: 0 50:0 40:62 30:1 20:0 10:0
  >> Last write times
  1 6 46 6 41 7 41 6 39 6 44 6 42 6 41 6 45 6 44 6 41 7 41 6 47 6 42 6 41 7 44 7
>>>Total Time: 4079212
*MTPD::send_Event(4001)
notice getting those long alternating times on a 1mb or a 22mb except the 22mb time out.

Not sure why. But before this update was getting better performance on flash5
 
My first sketch was the Kurt2Files old variant - and it failed - that goes to SD IIRC ... just back from visit in time for lunch ... will try that again.

Argggh - ... something else ...
 
My first sketch was the Kurt2Files old variant - and it failed - that goes to SD IIRC ... just back from visit in time for lunch ... will try that again.

Argggh - ... something else ...

Ok not sure what sketch that you are working with but I made sure I didn't install USBHost_t36 when I installed and the downloaded @KurtE's versions for MTP and USBHost:
Code:
https://github.com/KurtE/USBHost_t36/tree/FS_Integration_MSC
and
https://github.com/KurtE/MTP_Teensy

I kind of modified the MTP_TEST sketch to include the integrity stuff from the USB_MTP_CheckIntegrity sketch (still testing to make sure it works right):
Code:
#include <SD.h>
#include <MTP_Teensy.h>

//---------------------------------------------------
// Select drives you want to create
//---------------------------------------------------
#define USE_SD  1         // SDFAT based SDIO and SPI
#ifdef ARDUINO_TEENSY41
#define USE_LFS_RAM 0     // T4.1 PSRAM (or RAM)
#else
#define USE_LFS_RAM 0     // T4.1 PSRAM (or RAM)
#endif
#ifdef ARDUINO_TEENSY_MICROMOD
#define USE_LFS_QSPI 0    // T4.1 QSPI
#define USE_LFS_PROGM 1   // T4.4 Progam Flash
#define USE_LFS_SPI 0     // SPI Flash
#define USE_LFS_NAND 0
#define USE_LFS_QSPI_NAND 0
#define USE_LFS_FRAM 0
#else
#define USE_LFS_QSPI 1    // T4.1 QSPI
#define USE_LFS_PROGM 1   // T4.4 Progam Flash
#define USE_LFS_SPI 1     // SPI Flash
#define USE_LFS_NAND 1
#define USE_LFS_QSPI_NAND 0
#define USE_LFS_FRAM 0
#endif
#define USE_MSC 0    // set to > 0 experiment with MTP (USBHost.t36 + mscFS)
#define USE_SW_PU  1 //set to 1 if SPI devices does not have PUs,
                     // https://www.pjrc.com/better-spi-bus-design-in-3-steps/


#define DBGSerial Serial
FS *myfs;
File dataFile, myFile;  // Specifes that dataFile is of File type
int record_count = 0;
bool write_data = false;
uint8_t current_store = 0;

#define BUFFER_SIZE_INDEX 128
uint8_t write_buffer[BUFFER_SIZE_INDEX];
#define buffer_mult 4
uint8_t buffer_temp[buffer_mult*BUFFER_SIZE_INDEX];

int bytesRead;
uint32_t drive_index = 0;

// These can likely be left unchanged
#define MYBLKSIZE 2048 // 2048
#define SLACK_SPACE  40960 // allow for overhead slack space :: WORKS on FLASH {some need more with small alloc units}
#define size_bigfile 1024*1024*24  //24 mb file

// Various Globals
const uint32_t lowOffset = 'a' - 'A';
const uint32_t lowShift = 13;
uint32_t errsLFS = 0, warnLFS = 0; // Track warnings or Errors
uint32_t lCnt = 0, LoopCnt = 0; // loop counters
uint64_t rdCnt = 0, wrCnt = 0; // Track Bytes Read and Written
int filecount = 0;
int loopLimit = 0; // -1 continuous, otherwise # to count down to 0
bool bWriteVerify = true;  // Verify on Write Toggle
File file3; // Single FILE used for all functions


//=============================================================================
// Global defines
//=============================================================================

MTPStorage storage;
MTPD    mtpd(&storage);

//=============================================================================
// MSC & SD classes
//=============================================================================
#if USE_SD==1
#define USE_BUILTIN_SDCARD
#if defined(USE_BUILTIN_SDCARD) && defined(BUILTIN_SDCARD)
#define CS_SD  BUILTIN_SDCARD
#else
#define CS_SD 10
#endif
#endif


// SDClasses 
#if USE_SD==1
  // edit SPI to reflect your configuration (following is for T4.1)
  #define SD_MOSI 11
  #define SD_MISO 12
  #define SD_SCK  13

  #define SPI_SPEED SD_SCK_MHZ(16)  // adjust to sd card 

  #if defined (BUILTIN_SDCARD)
    const char *sd_str[]={"sdio","sd1"}; // edit to reflect your configuration
    const int cs[] = {BUILTIN_SDCARD,10}; // edit to reflect your configuration
  #else
    const char *sd_str[]={"sd1"}; // edit to reflect your configuration
    const int cs[] = {10}; // edit to reflect your configuration
  #endif
  const int nsd = sizeof(sd_str)/sizeof(const char *);
  
SDClass sdx[nsd];
#endif

// =======================================================================
// Set up MSC Drive file systems on different storage media
// =======================================================================
#if USE_MSC == 1
#include <USBHost_t36.h>
#include <USBHost_ms.h>

// Add USBHost objectsUsbFs
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub(myusb);

// MSC objects.
msController drive1(myusb);
msController drive2(myusb);
msController drive3(myusb);
msController drive4(myusb);

msFilesystem msFS1(myusb);
msFilesystem msFS2(myusb);
msFilesystem msFS3(myusb);
msFilesystem msFS4(myusb);
msFilesystem msFS5(myusb);

// Quick and dirty
msFilesystem *pmsFS[] = {&msFS1, &msFS2, &msFS3, &msFS4, &msFS5};
#define CNT_MSC  (sizeof(pmsFS)/sizeof(pmsFS[0]))
uint32_t pmsfs_store_ids[CNT_MSC] = {0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL};
char  pmsFS_display_name[CNT_MSC][20];

msController *pdrives[] {&drive1, &drive2, &drive3, &drive4};
#define CNT_DRIVES  (sizeof(pdrives)/sizeof(pdrives[0]))
bool drive_previous_connected[CNT_DRIVES] = {false, false, false, false};
#endif


// =======================================================================
// Set up LittleFS file systems on different storage media
// =======================================================================

#if USE_LFS_FRAM == 1 || USE_LFS_NAND == 1 || USE_LFS_PROGM == 1 || USE_LFS_QSPI == 1 || USE_LFS_QSPI_NAND == 1 || \
  USE_LFS_RAM == 1 || USE_LFS_SPI == 1
#include <LittleFS.h>
#endif

#if USE_LFS_RAM==1
const char *lfs_ram_str[] = {"RAM1", "RAM2"};  // edit to reflect your configuration
const int lfs_ram_size[] = {200'000,4'000'000}; // edit to reflect your configuration
const int nfs_ram = sizeof(lfs_ram_str)/sizeof(const char *);
LittleFS_RAM ramfs[nfs_ram];
#endif

#if USE_LFS_QSPI==1
const char *lfs_qspi_str[]={"QSPI"};     // edit to reflect your configuration
const int nfs_qspi = sizeof(lfs_qspi_str)/sizeof(const char *);
LittleFS_QSPIFlash qspifs[nfs_qspi];
#endif

#if USE_LFS_PROGM==1
const char *lfs_progm_str[]={"PROGM"};     // edit to reflect your configuration
const int lfs_progm_size[] = {1'000'000}; // edit to reflect your configuration
const int nfs_progm = sizeof(lfs_progm_str)/sizeof(const char *);
LittleFS_Program progmfs[nfs_progm];
#endif

#if USE_LFS_SPI==1
const char *lfs_spi_str[]={"sflash5", "sflash6"}; // edit to reflect your configuration
const int lfs_cs[] = {5, 6}; // edit to reflect your configuration
const int nfs_spi = sizeof(lfs_spi_str)/sizeof(const char *);
LittleFS_SPIFlash spifs[nfs_spi];
#endif

#if USE_LFS_NAND == 1
const char *nspi_str[]={"WINBOND1G", "WINBOND2G"};     // edit to reflect your configuration
const int nspi_cs[] = {3, 4}; // edit to reflect your configuration
const int nspi_nsd = sizeof(nspi_cs)/sizeof(int);
LittleFS_SPINAND nspifs[nspi_nsd]; // needs to be declared if LittleFS is used in storage.h
#endif

#if USE_LFS_QSPI_NAND == 1
const char *qnspi_str[]={"WIN-M02"};     // edit to reflect your configuration
const int qnspi_nsd = sizeof(qnspi_str)/sizeof(const char *);
LittleFS_QPINAND qnspifs[qnspi_nsd]; // needs to be declared if LittleFS is used in storage.h
#endif

void storage_configure()
{
  DateTimeFields date;
  breakTime(Teensy3Clock.get(), date);
  const char *monthname[12]={
    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
  DBGSerial.printf("Date: %u %s %u %u:%u:%u\n",
    date.mday, monthname[date.mon], date.year+1900, date.hour, date.min, date.sec);

    
  #if USE_SD==1
    #if defined SD_SCK
      SPI.setMOSI(SD_MOSI);
      SPI.setMISO(SD_MISO);
      SPI.setSCK(SD_SCK);
    #endif

    for(int ii=0; ii<nsd; ii++)
    { 
      #if defined(BUILTIN_SDCARD)
        if(cs[ii] == BUILTIN_SDCARD)
        {
          DBGSerial.printf("!! Try installing BUILTIN SD Card");
          if(!sdx[ii].begin(BUILTIN_SDCARD))
          { 
            Serial.printf("SDIO Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
          }
          else
          {
            storage.addFilesystem(sdx[ii], sd_str[ii]);
            uint64_t totalSize = sdx[ii].totalSize();
            uint64_t usedSize  = sdx[ii].usedSize();
            Serial.printf("SDIO Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
            Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
          }
        }
        else if(cs[ii]<BUILTIN_SDCARD)
      #endif
      {
        pinMode(cs[ii],OUTPUT); digitalWriteFast(cs[ii],HIGH);
        if(!sdx[ii].begin(cs[ii])) 
        { Serial.printf("SD Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
        }
        else
        {
          storage.addFilesystem(sdx[ii], sd_str[ii]);
          uint64_t totalSize = sdx[ii].totalSize();
          uint64_t usedSize  = sdx[ii].usedSize();
          Serial.printf("SD Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
          Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
        }
      }
    
    }
    #endif

#if USE_LFS_RAM==1
  for (int ii=0; ii<nfs_ram;ii++) {
    if (!ramfs[ii].begin(lfs_ram_size[ii])) {
      DBGSerial.printf("Ram Storage %d %s failed or missing",ii,lfs_ram_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(ramfs[ii], lfs_ram_str[ii]);
      uint64_t totalSize = ramfs[ii].totalSize();
      uint64_t usedSize  = ramfs[ii].usedSize();
      DBGSerial.printf("RAM Storage %d %s %llu %llu\n", ii, lfs_ram_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_PROGM==1
  for (int ii=0; ii<nfs_progm;ii++) {
    if (!progmfs[ii].begin(lfs_progm_size[ii])) {
      DBGSerial.printf("Program Storage %d %s failed or missing",ii,lfs_progm_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(progmfs[ii], lfs_progm_str[ii]);
      uint64_t totalSize = progmfs[ii].totalSize();
      uint64_t usedSize  = progmfs[ii].usedSize();
      DBGSerial.printf("Program Storage %d %s %llu %llu\n", ii, lfs_progm_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_QSPI==1
  for(int ii=0; ii<nfs_qspi;ii++) {
    if(!qspifs[ii].begin()) {
      DBGSerial.printf("QSPI Storage %d %s failed or missing",ii,lfs_qspi_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(qspifs[ii], lfs_qspi_str[ii]);
      uint64_t totalSize = qspifs[ii].totalSize();
      uint64_t usedSize  = qspifs[ii].usedSize();
      DBGSerial.printf("QSPI Storage %d %s %llu %llu\n", ii, lfs_qspi_str[ii], totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_SPI==1
  for (int ii=0; ii<nfs_spi;ii++) {
    if (USE_SW_PU == 1) {
      pinMode(lfs_cs[ii],OUTPUT);
      digitalWriteFast(lfs_cs[ii],HIGH);
    }
    if (!spifs[ii].begin(lfs_cs[ii], SPI)) {
      DBGSerial.printf("SPIFlash Storage %d %d %s failed or missing",ii,lfs_cs[ii],lfs_spi_str[ii]);      DBGSerial.println();
    } else {
      storage.addFilesystem(spifs[ii], lfs_spi_str[ii]);
      uint64_t totalSize = spifs[ii].totalSize();
      uint64_t usedSize  = spifs[ii].usedSize();
      DBGSerial.printf("SPIFlash Storage %d %d %s %llu %llu\n", ii, lfs_cs[ii], lfs_spi_str[ii],
        totalSize, usedSize);
    }
  }
#endif
#if USE_LFS_NAND == 1
  for(int ii=0; ii<nspi_nsd;ii++) {
    if (USE_SW_PU == 1) {
      pinMode(nspi_cs[ii],OUTPUT);
      digitalWriteFast(nspi_cs[ii],HIGH);
    }
    if(!nspifs[ii].begin(nspi_cs[ii], SPI)) {
      DBGSerial.printf("SPIFlash NAND Storage %d %d %s failed or missing",ii,nspi_cs[ii],nspi_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(nspifs[ii], nspi_str[ii]);
      uint64_t totalSize = nspifs[ii].totalSize();
      uint64_t usedSize  = nspifs[ii].usedSize();
      DBGSerial.printf("Storage %d %d %s %llu %llu\n", ii, nspi_cs[ii], nspi_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_QSPI_NAND == 1
  for(int ii=0; ii<qnspi_nsd;ii++) {
    if(!qnspifs[ii].begin()) {
       DBGSerial.printf("QSPI NAND Storage %d %s failed or missing",ii,qnspi_str[ii]); DBGSerial.println();
    } else {
      storage.addFilesystem(qnspifs[ii], qnspi_str[ii]);
      uint64_t totalSize = qnspifs[ii].totalSize();
      uint64_t usedSize  = qnspifs[ii].usedSize();
      DBGSerial.printf("Storage %d %s %llu %llu\n", ii, qnspi_str[ii], totalSize, usedSize);
    }
  }
#endif

}

void setup()
{

  // Open serial communications and wait for port to open:
#if defined(USB_MTPDISK_SERIAL)
  while (!Serial && millis() < 5000) {
    // wait for serial port to connect.
  }
#else
  //while(!DBGSerial.available()); // comment if you do not want to wait for
                                   // terminal (otherwise press any key to continue)
  while (!Serial && !DBGSerial.available() && millis() < 5000) 
  //myusb.Task(); // or third option to wait up to 5 seconds and then continue
#endif

  DBGSerial.print(CrashReport);
  DBGSerial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  delay(3000);
  

  //This is mandatory to begin the mtpd session.
  mtpd.begin();
  
  storage_configure();

  #if USE_MSC == 1
  myusb.begin();
  DBGSerial.print("Initializing MSC Drives ...");
  DBGSerial.println("\nInitializing USB MSC drives...");
  DBGSerial.println("MSC and MTP initialized.");
  checkMSCChanges();
  #endif
  DBGSerial.println("\nSetup done");
  menu();
}

int ReadAndEchoSerialChar() {
  int ch = DBGSerial.read();
  if (ch >= ' ') DBGSerial.write(ch);
  return ch;
}

void loop()
{

  if ( DBGSerial.available() ) {
    uint8_t command = DBGSerial.read();
    int ch = DBGSerial.read();
    uint8_t storage_index = CommandLineReadNextNumber(ch, 0);
    while (ch == ' ') ch = DBGSerial.read();

    uint32_t fsCount;
    switch (command) {
    case '1':
      // first dump list of storages:
      fsCount = storage.getFSCount();
      DBGSerial.printf("\nDump Storage list(%u)\n", fsCount);
      for (uint32_t ii = 0; ii < fsCount; ii++) {
        DBGSerial.printf("store:%u storage:%x name:%s fs:%x\n", ii, mtpd.Store2Storage(ii),
          storage.getStoreName(ii), (uint32_t)storage.getStoreFS(ii));
      }
      //DBGSerial.println("\nDump Index List");
      //storage.dumpIndexList();
      break;
    case '2':
      if (storage_index < storage.getFSCount()) {
        DBGSerial.printf("Storage Index %u Name: %s Selected\n", storage_index,
          storage.getStoreName(storage_index));
        myfs = storage.getStoreFS(storage_index);
        current_store = storage_index;
      } else {
        DBGSerial.printf("Storage Index %u out of range\n", storage_index);
      }
      break;

    case 'l': listFiles(); break;
    case 'e': eraseFiles(); break;
    case 's':
      DBGSerial.println("\nLogging Data!!!");
      write_data = true;   // sets flag to continue to write data until new command is received
      // opens a file or creates a file if not present,  FILE_WRITE will append data to
      // to the file created.
      dataFile = myfs->open("datalog.txt", FILE_WRITE);
      logData();
      break;
    case 'x': stopLogging(); break;
    case'r':
      DBGSerial.println("Reset");
      mtpd.send_DeviceResetEvent();
      break;
    case 'd': dumpLog(); break;
    case 'b':
      bigFile( 0 ); // delete
      command = 0;
      break;
    case 'B':
      bigFile( 1 ); // CREATE
      command = 0;
      break;
    case 't':
      bigFile2MB( 0 ); // CREATE
      command = 0;
      break;
    case 'S':
      bigFile2MB( 1 ); // CREATE
      command = 0;
     break;
    case 'n': // No Verify on write
      bWriteVerify = !bWriteVerify;
      bWriteVerify ? DBGSerial.print(" Write Verify on: ") : DBGSerial.print(" Write Verify off: ");
     command = 0;
      break;
    case 'i':
      writeIndexFile();
      break;
    case 'R':
      DBGSerial.print(" RESTART Teensy ...");
      delay(100);
      SCB_AIRCR = 0x05FA0004;
      break;
    
    case '\r':
    case '\n':
    case 'h': menu(); break;
    }
    while (DBGSerial.read() != -1) ; // remove rest of characters.
  } else {
    #if USE_MSC == 1
    checkMSCChanges();
    #endif
    mtpd.loop();
  }

  if (write_data) logData();
}

#if USE_MSC == 1
void checkMSCChanges() {
  myusb.Task();

  USBMSCDevice mscDrive;
  PFsLib pfsLIB;
  for (uint8_t i=0; i < CNT_DRIVES; i++) {
    if (*pdrives[i]) {
      if (!drive_previous_connected[i]) {
        if (mscDrive.begin(pdrives[i])) {
          Serial.printf("\nUSB Drive: %u connected\n", i);
          pfsLIB.mbrDmp(&mscDrive, (uint32_t)-1, Serial);
          Serial.printf("\nTry Partition list");
          pfsLIB.listPartitions(&mscDrive, Serial);
          drive_previous_connected[i] = true;
        }
      }
    } else {
      drive_previous_connected[i] = false;
    }
  }
  bool send_device_reset = false;
  for (uint8_t i = 0; i < CNT_MSC; i++) {
    if (*pmsFS[i] && (pmsfs_store_ids[i] == 0xFFFFFFFFUL)) {
      Serial.printf("Found new Volume:%u\n", i); Serial.flush();
      // Lets see if we can get the volume label:
      char volName[20];
      if (pmsFS[i]->mscfs.getVolumeLabel(volName, sizeof(volName)))
        snprintf(pmsFS_display_name[i], sizeof(pmsFS_display_name[i]), "MSC%d-%s", i, volName);
      else
        snprintf(pmsFS_display_name[i], sizeof(pmsFS_display_name[i]), "MSC%d", i);
      pmsfs_store_ids[i] = storage.addFilesystem(*pmsFS[i], pmsFS_display_name[i]);

      // Try to send store added. if > 0 it went through = 0 stores have not been enumerated
      if (mtpd.send_StoreAddedEvent(pmsfs_store_ids[i]) < 0) send_device_reset = true;
    }
    // Or did volume go away?
    else if ((pmsfs_store_ids[i] != 0xFFFFFFFFUL) && !*pmsFS[i] ) {
      if (mtpd.send_StoreRemovedEvent(pmsfs_store_ids[i]) < 0) send_device_reset = true;
      storage.removeFilesystem(pmsfs_store_ids[i]);
      // Try to send store added. if > 0 it went through = 0 stores have not been enumerated
      pmsfs_store_ids[i] = 0xFFFFFFFFUL;
    }
  }
  if (send_device_reset) mtpd.send_DeviceResetEvent();
}
#endif

void logData()
{
  // make a string for assembling the data to log:
  String dataString = "";

  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ",";
    }
  }

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    // print to the serial port too:
    DBGSerial.println(dataString);
    record_count += 1;
  } else {
    // if the file isn't open, pop up an error:
    DBGSerial.println("error opening datalog.txt");
  }
  delay(100); // run at a reasonable not-too-fast speed for testing
}

void stopLogging()
{
  DBGSerial.println("\nStopped Logging Data!!!");
  write_data = false;
  // Closes the data file.
  dataFile.close();
  DBGSerial.printf("Records written = %d\n", record_count);
  mtpd.send_DeviceResetEvent();
}


void dumpLog()
{
  DBGSerial.println("\nDumping Log!!!");
  // open the file.
  dataFile = myfs->open("datalog.txt");

  // if the file is available, write to it:
  if (dataFile) {
    while (dataFile.available()) {
      DBGSerial.write(dataFile.read());
    }
    dataFile.close();
  }
  // if the file isn't open, pop up an error:
  else {
    DBGSerial.println("error opening datalog.txt");
  }
}

void menu()
{
  DBGSerial.println();
  DBGSerial.println("Menu Options:");
  DBGSerial.println("\t1 - List Drives (Step 1)");
  DBGSerial.println("\t2 - Select Drive for Logging (Step 2)");
  DBGSerial.println("\tl - List files on disk");
  DBGSerial.println("\te - Erase files on disk");
  DBGSerial.println("\ts - Start Logging data (Restarting logger will append records to existing log)");
  DBGSerial.println("\tx - Stop Logging data");
  DBGSerial.println("\td - Dump Log");
  DBGSerial.println("\tr - Reset MTP");
  DBGSerial.printf("\n\t%s","R - Restart Teensy");
  DBGSerial.printf("\n\t%s","i - Write Index File to disk");
  DBGSerial.printf("\n\t%s","'B, or b': Make Big file half of free space, or remove all Big files");
  DBGSerial.printf("\n\t%s","'S, or t': Make 2MB file , or remove all 2MB files");
  DBGSerial.printf("\n\t%s","'n' No verify on Write- TOGGLE");

  DBGSerial.println("\th - Menu");
  DBGSerial.println();
}

void listFiles()
{
  DBGSerial.print("\n Space Used = ");
  DBGSerial.println(myfs->usedSize());
  DBGSerial.print("Filesystem Size = ");
  DBGSerial.println(myfs->totalSize());

  printDirectory(myfs);
}

void eraseFiles()
{
  //DBGSerial.println("Formating not supported at this time");
  DBGSerial.println("\n*** Erase/Format started ***");
  myfs->format(1, '.', DBGSerial);
  Serial.println("Completed, sending device reset event");
  mtpd.send_DeviceResetEvent();
}

void printDirectory(FS *pfs) {
  DBGSerial.println("Directory\n---------");
  printDirectory(pfs->open("/"), 0);
  DBGSerial.println();
}

void printDirectory(File dir, int numSpaces) {
  while (true) {
    File entry = dir.openNextFile();
    if (! entry) {
      //DBGSerial.println("** no more files **");
      break;
    }
    printSpaces(numSpaces);
    DBGSerial.print(entry.name());
    if (entry.isDirectory()) {
      DBGSerial.println("/");
      printDirectory(entry, numSpaces + 2);
    } else {
      // files have sizes, directories do not
      printSpaces(36 - numSpaces - strlen(entry.name()));
      DBGSerial.print("  ");
      DBGSerial.println(entry.size(), DEC);
    }
    entry.close();
  }
}

void printSpaces(int num) {
  for (int i = 0; i < num; i++) {
    DBGSerial.print(" ");
  }
}


uint32_t CommandLineReadNextNumber(int &ch, uint32_t default_num) {
  while (ch == ' ') ch = DBGSerial.read();
  if ((ch < '0') || (ch > '9')) return default_num;

  uint32_t return_value = 0;
  while ((ch >= '0') && (ch <= '9')) {
    return_value = return_value * 10 + ch - '0';
    ch = DBGSerial.read();
  }
  return return_value;
}



void readVerify( char szPath[], char chNow ) {
  uint32_t timeMe = micros();
  file3 = myfs->open(szPath);
  if ( 0 == file3 ) {
    Serial.printf( "\tV\t Fail File open %s\n", szPath );
    errsLFS++;
  }
  char mm;
  char chNow2 = chNow + lowOffset;
  uint32_t ii = 0;
  while ( file3.available() ) {
    file3.read( &mm , 1 );
    rdCnt++;
    //Serial.print( mm ); // show chars as read
    ii++;
    if ( 0 == (ii / lowShift) % 2 ) {
      if ( chNow2 != mm ) {
        Serial.printf( "<Bad Byte!  %c! = %c [0x%X] @%u\n", chNow2, mm, mm, ii );
        errsLFS++;
        break;
      }
    }
    else {
      if ( chNow != mm ) {
        Serial.printf( "<Bad Byte!  %c! = %c [0x%X] @%u\n", chNow, mm, mm, ii );
        errsLFS++;
        break;
      }
    }
  }
  Serial.printf( "  Verify %u Bytes ", ii );
  if (ii != file3.size()) {
    Serial.printf( "\n\tRead Count fail! :: read %u != f.size %llu", ii, file3.size() );
    errsLFS++;
  }
  file3.close();
  timeMe = micros() - timeMe;
  Serial.printf( " @KB/sec %5.2f", ii / (timeMe / 1000.0) );
}

bool bigVerify( char szPath[], char chNow ) {
  uint32_t timeMe = micros();
  file3 = myfs->open(szPath);
  uint64_t fSize;
  if ( 0 == file3 ) {
    return false;
  }
  char mm;
  uint32_t ii = 0;
  uint32_t kk = file3.size() / 50;
  fSize = file3.size();
  Serial.printf( "\tVerify %s bytes %lu : ", szPath, fSize );
  while ( file3.available() ) {
    file3.read( &mm , 1 );
    rdCnt++;
    ii++;
    if ( !(ii % kk) ) Serial.print('.');
    if ( chNow != mm ) {
      Serial.printf( "<Bad Byte!  %c! = %c [0x%X] @%u\n", chNow, mm, mm, ii );
      errsLFS++;
      break;
    }
    if ( ii > fSize ) { // catch over length return makes bad loop !!!
      Serial.printf( "\n\tFile LEN Corrupt!  FS returning over %u bytes\n", fSize );
      errsLFS++;
      break;
    }
  }
  if (ii != file3.size()) {
    Serial.printf( "\n\tRead Count fail! :: read %u != f.size %llu\n", ii, file3.size() );
    errsLFS++;
  }
  else
    Serial.printf( "\tGOOD! >>  bytes %lu", ii );
  file3.close();
  timeMe = micros() - timeMe;
  Serial.printf( "\n\tBig read&compare KBytes per second %5.2f \n", ii / (timeMe / 1000.0) );
  if ( 0 == ii ) return false;
  return true;
}



void bigFile( int doThis ) {
  char myFile[] = "/0_bigfile.txt";
  char fileID = '0' - 1;
  DateTimeFields dtf = {0, 10, 7, 0, 22, 7, 121};

  if ( 0 == doThis ) {  // delete File
    Serial.printf( "\nDelete with read verify all #bigfile's\n");
    do {
      fileID++;
      myFile[1] = fileID;
      if ( myfs->exists(myFile) && bigVerify( myFile, fileID) ) {
        filecount--;
        myfs->remove(myFile);
      }
      else break; // no more of these
    } while ( 1 );
  }
  else {  // FILL DISK
    uint32_t resW = 1;
    
    char someData[MYBLKSIZE];
    uint32_t xx, toWrite;
    toWrite = (myfs->totalSize()) - myfs->usedSize();
    if ( toWrite < 65535 ) {
      Serial.print( "Disk too full! DO :: reformat");
      return;
    }
    toWrite = size_bigfile;
    toWrite -= SLACK_SPACE;
    xx = toWrite;
    Serial.printf( "\nStart Big write of %u Bytes", xx);
    uint32_t timeMe = micros();
    file3 = nullptr;
    do {
      if ( file3 ) file3.close();
      fileID++;
      myFile[1] = fileID;
      file3 = myfs->open(myFile, FILE_WRITE);
    } while ( fileID < '9' && file3.size() > 0);
    if ( fileID == '9' ) {
      Serial.print( "Disk has 9 halves 0-8! DO :: b or q or F");
      return;
    }
    memset( someData, fileID, 2048 );
    int hh = 0;
    while ( toWrite > 2048 && resW > 0 ) {
      resW = file3.write( someData , 2048 );
      hh++;
      if ( !(hh % 40) ) Serial.print('.');
      toWrite -= 2048;
    }
    xx = toWrite;
    file3.setCreateTime(dtf);
    file3.setModifyTime(dtf);
    file3.close();
    timeMe = micros() - timeMe;
    file3 = myfs->open(myFile, FILE_WRITE);
    if ( file3.size() > 0 ) {
      filecount++;
      Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() );
    }
    if ( file3 != 0 ) file3.close();
    Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) );
    Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs->usedSize(), myfs->totalSize());
    if ( myfs->usedSize() == myfs->totalSize() ) {
      Serial.printf("\n\n\tWARNING: DISK FULL >>>>>  Bytes Used: %llu, Bytes Total:%llu\n\n", myfs->usedSize(), myfs->totalSize());
      warnLFS++;
    }
    if ( resW < 0 ) {
      Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW );
      errsLFS++;
      myfs->remove(myFile);
    }
  }
}

void bigFile2MB( int doThis ) {
  char myFile[] = "/0_2MBfile.txt";
  char fileID = '0' - 1;
  DateTimeFields dtf = {0, 10, 7, 0, 22, 7, 121};

  if ( 0 == doThis ) {  // delete File
    Serial.printf( "\nDelete with read verify all #bigfile's\n");
    do {
      fileID++;
      myFile[1] = fileID;
      if ( myfs->exists(myFile) && bigVerify( myFile, fileID) ) {
        filecount--;
        myfs->remove(myFile);
      }
      else break; // no more of these
    } while ( 1 );
  }
  else {  // FILL DISK
    uint32_t resW = 1;
    
    char someData[2048];
    uint32_t xx, toWrite;
    toWrite = 2048 * 1000;
    if ( toWrite > (65535 + (myfs->totalSize() - myfs->usedSize()) ) ) {
      Serial.print( "Disk too full! DO :: q or F");
      return;
    }
    xx = toWrite;
    Serial.printf( "\nStart Big write of %u Bytes", xx);
    uint32_t timeMe = micros();
    file3 = nullptr;
    do {
      if ( file3 ) file3.close();
      fileID++;
      myFile[1] = fileID;
      file3 = myfs->open(myFile, FILE_WRITE);
    } while ( fileID < '9' && file3.size() > 0);
    if ( fileID == '9' ) {
      Serial.print( "Disk has 9 files 0-8! DO :: b or q or F");
      return;
    }
    memset( someData, fileID, 2048 );
    int hh = 0;
    while ( toWrite >= 2048 && resW > 0 ) {
      resW = file3.write( someData , 2048 );
      hh++;
      if ( !(hh % 40) ) Serial.print('.');
      toWrite -= 2048;
    }
    xx -= toWrite;
    file3.setCreateTime(dtf);
    file3.setModifyTime(dtf);
    file3.close();
    timeMe = micros() - timeMe;
    file3 = myfs->open(myFile, FILE_WRITE);
    if ( file3.size() > 0 ) {
      filecount++;
      Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() );
    }
    if ( file3 != 0 ) file3.close();
    Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) );
    Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs->usedSize(), myfs->totalSize());
    if ( myfs->usedSize() == myfs->totalSize() ) {
      Serial.printf("\n\n\tWARNING: DISK FULL >>>>>  Bytes Used: %llu, Bytes Total:%llu\n\n", myfs->usedSize(), myfs->totalSize());
      warnLFS++;
    }
    if ( resW < 0 ) {
      Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW );
      errsLFS++;
      myfs->remove(myFile);
    }
  }
}

void writeIndexFile() 
{
  DateTimeFields dtf = {0, 10, 7, 0, 22, 7, 121};
  // open the file.
  Serial.println("Write Large Index File");
  uint32_t timeMe = micros();
  file3 = myfs->open("LargeIndexedTestfile.txt", FILE_WRITE_BEGIN);
  if (file3) {
    file3.truncate(); // Make sure we wipe out whatever was written earlier
    for (uint32_t i = 0; i < 43000*4; i++) {
      memset(write_buffer, 'A'+ (i & 0xf), sizeof(write_buffer));
      file3.printf("%06u ", i >> 2);  // 4 per physical buffer
      file3.write(write_buffer, i? 120 : 120-12); // first buffer has other data...
      file3.printf("\n");
    }
    file3.setCreateTime(dtf);
    file3.setModifyTime(dtf);
    file3.close();
    
    timeMe = micros() - timeMe;
    file3 = myfs->open("LargeIndexedTestfile.txt", FILE_WRITE);
    if ( file3.size() > 0 ) {
       Serial.printf( " Total time to write %d byte: %5.2f seconds\n", file3.size(), (timeMe / 1000.0));
       Serial.printf( "\n\tBig write KBytes per second %5.2f \n", file3.size() / (timeMe / 1000.0) );
    }
    if ( file3 != 0 ) file3.close();
    Serial.println("\ndone.");
    
  }
}
 
@KurtE - @Paul - @defragster
Just on the off chance that the issue may be with going to version 2.4.1 of LittleFS I reverted back to our previous version. Seems the new version is a bit faster - only thing I did was a quick format of the flash (512 non-dtr version):
Code:
old version of littleFS - 2.0.2+
=================================================================================

Start Big write of 2048000 Bytes.........................
Big write /0_2MBfile.txt took  9.05 Sec for 2048000 Bytes : file3.size()=2048000
	Big write KBytes per second 226.22 

Bytes Used: 2060288, Bytes Total:67108864

==================================================================================

Start Big write of 25124864 Bytes.........
Big write /0_bigfile.txt took 304.39 Sec for 2048 Bytes : file3.size()=25122816
	Big write KBytes per second  0.01 

Bytes Used: 27234304, Bytes Total:67108864

============================= copy 22 mb file ===========================================
LP:479091 CMD: 100d(SEND_OBJECT)l: 12 T:25
MTPD::SendObject: len:22015988

MTPD::SendObject *** USB Read Timeout ***
 # USB Packets: 430 total: 2 avg ms: 0 max: 1
 # Write: 216 total:3022 avg ms: 13 max: 51(40)
  >> >=100ms: 0 50:11 40:43 30:0 20:0 10:0
  >> Last write times
  1 3 2 3 48 3 2 3 50 3 2 3 48 3 2 3 48 2 3 3 46 3 3 2 49 2 3 3 47 3 3 2
>>>Total Time: 4034318
*MTPD::send_Event(4001)

and with version 2.4.1:
Code:
NEW Version of littleFS (2.4.1)
===================================================================================


Start Big write of 2048000 Bytes.........................
Big write /0_2MBfile.txt took  6.03 Sec for 2048000 Bytes : file3.size()=2048000
	Big write KBytes per second 339.52 

Bytes Used: 2060288, Bytes Total:67108864

==================================================================================
Start Big write of 25124864 Bytes........
Big write /0_bigfile.txt took 74.54 Sec for 2048 Bytes : file3.size()=25122816
	Big write KBytes per second  0.03 

Bytes Used: 27234304, Bytes Total:67108864

==========================copy 22 mb file===================================

MTPD::SendObject *** USB Read Timeout ***
 # USB Packets: 3160 total: 7 avg ms: 0 max: 1
 # Write: 791 total:8198 avg ms: 10 max: 1900(785)
  >> >=100ms: 7 50:0 40:0 30:0 20:0 10:0
  >> Last write times
  258 259 259 258 259 259 1900 5 7 5 7 5 7 6 7 5 7 6 6 6 7 6 7 5 8 5 7 6 7 5 7 6
>>>Total Time: 9216677
*MTPD::send_Event(4001)
======================== after quickformat =======================================

MTPD::SendObject: len:22015988

MTPD::SendObject *** USB Read Timeout ***
 # USB Packets: 468 total: 2 avg ms: 0 max: 1
 # Write: 118 total:3026 avg ms: 25 max: 49(28)
  >> >=100ms: 0 50:0 40:59 30:0 20:0 10:0
  >> Last write times
  1 6 47 6 46 5 47 5 44 5 46 6 44 6 45 6 45 5 45 6 46 5 46 6 47 6 45 5 48 5 44 5
>>>Total Time: 4038404
*MTPD::send_Event(4001)

EDIT:
Quick summary issues not caused by switching version of LFS and seems new version may be a hair faster
 
Ok not sure what sketch that you are working with but I made sure I didn't install USBHost_t36 when I installed and the downloaded @KurtE's versions for MTP and USBHost:
Code:
https://github.com/KurtE/USBHost_t36/tree/FS_Integration_MSC
and
https://github.com/KurtE/MTP_Teensy

I kind of modified the MTP_TEST sketch to include the integrity stuff from the USB_MTP_CheckIntegrity sketch (still testing to make sure it works right):
Code:
...

I got those two and it seemed well for MTP - from post #528 above:
Code:
Using library MTP_Teensy at version 1.0.0 in folder: C:\T_Drive\tCode\libraries\MTP_Teensy 
Using library USBHost_t36 at version 0.1 in folder: C:\T_Drive\tCode\libraries\USBHost_t36
I didn't exclude installed USBHost - but it seems to have ignored it.

Problem was the SD based Kurt2Files example from pages back ...
> looking to retry that and I got burned ... Just sent email about the 'arrrgh' :(

CODE for "MTP_TEST_MS" builds fine and seems ready to go - forgot to plug in USB
Code:
LP:10568 CMD: 1004(GET_STORAGE_IDS)l: 12 T:3
10568 RESP:2001(RSP:OK)l: 12 T:3
LP:10569 CMD: 1005(GET_STORAGE_INFO)l: 16 T:4 : 10001
65537 0 name:sdio
65537 0 name:sdio
10569 RESP:2001(RSP:OK)l: 16 T:4 : 10001
LP:10569 CMD: 1005(GET_STORAGE_INFO)l: 16 T:5 : 20001
131073 1 name:PROGM
131073 1 name:PROGM
10569 RESP:2001(RSP:OK)l: 16 T:5 : 20001
LP:10569 CMD: 1005(GET_STORAGE_INFO)l: 16 T:6 : 30001
196609 2 name:sflash5
196609 2 name:sflash5
10985 RESP:2001(RSP:OK)l: 16 T:6 : 30001
LP:10986 CMD: 1005(GET_STORAGE_INFO)l: 16 T:7 : 40001
262145 3 name:sflash6
262145 3 name:sflash6
11006 RESP:2001(RSP:OK)l: 16 T:7 : 40001
LP:11006 CMD: 1005(GET_STORAGE_INFO)l: 16 T:8 : 50001
327681 4 name:WINBOND1G
327681 4 name:WINBOND1G
12799 RESP:2001(RSP:OK)l: 16 T:8 : 50001
LP:12799 CMD: 1005(GET_STORAGE_INFO)l: 16 T:9 : 60001
393217 5 name:WINBOND2G
393217 5 name:WINBOND2G

Plugged in HUB w/HDD and USB SSD - and they are not found?

<EDIT>: Just noticed that sketch isn't asking for USB_HOST?
Code:
Multiple libraries were found for "SD.h"
 Used: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\SD
 Not used: C:\T_Drive\Arduino_1.8.16_156\libraries\SD
Using library SD at version 2.0.0 in folder: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\SD 
Using library SdFat at version 2.1.0 in folder: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\SdFat 
Using library SPI at version 1.0 in folder: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\SPI 
Using library MTP_Teensy at version 1.0.0 in folder: C:\T_Drive\tCode\libraries\MTP_Teensy 
Using library LittleFS at version 1.0.0 in folder: C:\T_Drive\Arduino_1.8.16_156\hardware\teensy\avr\libraries\LittleFS
 
Last edited:
@defragster
Did you remember to enable MSC?

Code:
#define USE_MSC 0    // set to > 0 experiment with MTP (USBHost.t36 + mscFS)

Have to set this to 1 to use MSC devices.
 
@defragster
Did you remember to enable MSC?

Code:
#define USE_MSC 0    // set to > 0 experiment with MTP (USBHost.t36 + mscFS)

Have to set this to 1 to use MSC devices.

You mean that line #28 ... no I did not do that before :) --- didn't even remember to 'Save' the <Edit> above before I ran to do errands ...
Code:
Dump Storage list(8)
store:0 storage:10001 name:sdio fs:2000ca14
store:1 storage:20001 name:PROGM fs:2000c814
store:2 storage:30001 name:sflash5 fs:2000d3b4
store:3 storage:40001 name:sflash6 fs:2000d490
store:4 storage:50001 name:WINBOND1G fs:2000c3c8
store:5 storage:60001 name:WINBOND2G fs:2000c4bc
store:6 storage:70001 name:MSC0-SSD_GPT120 fs:2000a370
store:7 storage:80001 name:MSC1-120GB_Ext fs:2000a840

That seems to do the trick ...

Added another USB Flash and that UPDATED and came online!
 
Not sure what this is trying to tell me?: !! Try installing BUILTIN SD CardaddFilesystem: 0 sdio 2000ca14
Code:
        if(cs[ii] == BUILTIN_SDCARD)
        {
          DBGSerial.printf("!! Try installing BUILTIN SD Card");

with media:
Code:
Dump Storage list(9)
store:0 storage:10001 name:sdio fs:2000ca14
store:1 storage:20001 name:PROGM fs:2000c814
store:2 storage:30001 name:sflash5 fs:2000d3b4
store:3 storage:40001 name:sflash6 fs:2000d490
[B]store:4 storage:50001 name:WINBOND1G fs:2000c3c8
store:5 storage:60001 name:WINBOND2G fs:2000c4bc[/B]
store:6 storage:70001 name:MSC0-SSD_GPT120 fs:2000a370
store:7 storage:80001 name:MSC1-120GB_Ext fs:2000a840
store:8 storage:90001 name:MSC2 fs:2000ad10

Where #4 and #5 have Integrity test folders and files. Clicking in Win Explorer causes SerMon parsing - but Explorer gets lost waiting?
>'24' (select 1gb)
>'l' shows all the files are there

The first time it restarted before I could capture the output when I clicked the 1Gb ... no display then 2Gb drive

Here is the T_sermon output file: View attachment MTPTEST_00.txt
 
In above p#537 bigFile() as written tried 25MB on LittleFS PROG and fails
> "B" results in a ZERO size file
> file cannot be deleted with 'b'
> 'l' still shows 0_bigfile.txt sz=0
--> Not sure of the LFS solution, but normal program might do this.

Making local edit to write HALF available space or 25MB if less than half the disk.

Also editing '1' media list to show "STORE:" on the active media:
> yes it shows when changed - but it can get scrolled way off - or not be on the expected disk ... like PROG above
--> THE selected DISK auto resets after a "l" !!!!! ... that needs fixed - it sets just like '2' does.

Can print way to many '.' on bigfile with fixed "hh % 40" - will edit that ...
 
In above p#537 bigFile() as written tried 25MB on LittleFS PROG and fails
> "B" results in a ZERO size file
> file cannot be deleted with 'b'
> 'l' still shows 0_bigfile.txt sz=0
--> Not sure of the LFS solution, but normal program might do this.

Making local edit to write HALF available space or 25MB if less than half the disk.

Also editing '1' media list to show "STORE:" on the active media:
> yes it shows when changed - but it can get scrolled way off - or not be on the expected disk ... like PROG above
--> THE selected DISK auto resets after a "l" !!!!! ... that needs fixed - it sets just like '2' does.

Can print way to many '.' on bigfile with fixed "hh % 40" - will edit that ...

Thanks Tim for testing - have to run and see whats going on - Win1g and Win2g didn't seem to have any issues. Have some appointments this morning so will test in the afternoon.
 
Early morning all... (At least here):

SendObject failing on large files...

Right now at least to me, there are logically two parts from this (maybe more... Somtimes I can not count):

a) Can we detect it and do something such that when it happens it does not stop MTP from working, requiring Teensy to be reset...

b) Figure out why and hopefully fix that... Or at least minimize it.

Right now I think I have the beginnings of a) I have not checked it in as I changed the code more than I had to and still experiementing.
In my file this start at about line 2474:
Code:
#else
bool MTPD::SendObject() {
  pull_packet(rx_data_buffer);
  read(0, 0);
  //      printContainer();
  uint32_t len = ReadMTPHeader();
  printf("MTPD::SendObject: len:%u\n", len);
  disk_pos = 0;
  elapsedMicros em_total = 0;

  uint32_t sum_read_em = 0;
  uint32_t c_read_em = 0;
  uint32_t read_em_max = 0;

  uint32_t sum_write_em = 0;
  uint32_t c_write_em = 0;
  uint32_t write_em_max = 0;
  uint32_t write_em_max_index = 0;
  uint32_t c_write_em_GE_10 = 0;
  uint32_t c_write_em_GE_20 = 0;
  uint32_t c_write_em_GE_30 = 0;
  uint32_t c_write_em_GE_40 = 0;
  uint32_t c_write_em_GE_50 = 0;
  uint32_t c_write_em_GE_100 = 0;
    

  uint32_t last_n_write_times[32];
  uint8_t count_zero_packets = 0;


  // first copy in the rest of the first packet into the diskbuffer.
  int cb_recv = MTP_RX_SIZE - sizeof(MTPHeader);
  check_memcpy(disk_buffer_, rx_data_buffer + sizeof(MTPHeader), cb_recv, disk_buffer_, DISK_BUFFER_SIZE);
  c_read_em = 1;
  len -= cb_recv;
  disk_pos = cb_recv;




  while ((int)len > 0) {
    // read in next sector
    elapsedMillis emRead = 0;
    cb_recv = usb_mtp_recv(rx_data_buffer, SENDOBJECT_READ_TIMEOUT_MS);
    delayMicroseconds(50);
    if (cb_recv > 0) { // read a packet of data
      uint32_t em = emRead;
      sum_read_em += em;
      c_read_em++;
      if (em > read_em_max)
        read_em_max = em;
        if (count_zero_packets) {
          count_zero_packets = 0;
          printf("\nMTPD::SendObject *** Received packet after 0 packet ***\n");
        }
    } else if (cb_recv == 0) {
      printf("\nMTPD::SendObject *** USB Read 0 bytes ***\n");
      count_zero_packets++;
      // don't sit here endless in 0 length packets. 
      if (count_zero_packets > 1) break;

    } else {
      printf("\nMTPD::SendObject *** USB Read Timeout ***\n");
      break; //
    }

    // Now see how much of this that will fit into output buffer
    uint32_t bytes = cb_recv; // how many data in usb-packet
    bytes = min(bytes, len);              // loimit at end
    uint32_t to_copy = min(bytes, DISK_BUFFER_SIZE - disk_pos); // how many data to copy to disk buffer
    //printf("    %u %u %u\n", len, bytes, to_copy);
    check_memcpy(disk_buffer_ + disk_pos, rx_data_buffer, to_copy, disk_buffer_, DISK_BUFFER_SIZE);
    disk_pos += to_copy;
    bytes -= to_copy;
    len -= to_copy;
    // printf("a %d %d %d %d %d\n", len,disk_pos,bytes,index,to_copy);
    //
    if (disk_pos == DISK_BUFFER_SIZE) {
      elapsedMillis emWrite = 0;
      if (storage_->write((const char *)disk_buffer_, DISK_BUFFER_SIZE) <
          DISK_BUFFER_SIZE)
        return false;
      uint32_t em = emWrite;
      last_n_write_times[c_write_em & 0x1f] = em;
      sum_write_em += em;
      c_write_em++;
      if (em > write_em_max) {
        write_em_max = em;
        write_em_max_index = c_write_em;
      }
      if (em >= 100) c_write_em_GE_100++;
      else if (em >= 50) c_write_em_GE_50++;
      else if (em >= 40) c_write_em_GE_40++;
      else if (em >= 30) c_write_em_GE_30++;
      else if (em >= 20) c_write_em_GE_20++;
      else if (em >= 10) c_write_em_GE_10++;

      disk_pos = 0;

      if (bytes) // we have still data in transfer buffer, copy to initial disk_buffer_
      {
        check_memcpy(disk_buffer_, rx_data_buffer + to_copy, bytes, disk_buffer_, DISK_BUFFER_SIZE);
        disk_pos += bytes;
        len -= bytes;
      }
      // printf("b %d %d %d %d %d\n", len,disk_pos,bytes,index,to_copy);
    }
  }

  printf("len %d\n",disk_pos);
  if (disk_pos) {
    elapsedMillis emWrite = 0;
    if (storage_->write((const char *)disk_buffer_, disk_pos) < disk_pos)
      return false;
    uint32_t em = emWrite;
    last_n_write_times[c_write_em & 0x1f] = em;
    sum_write_em += em;
    c_write_em++;
    if (em > write_em_max) {
      write_em_max = em;
        write_em_max_index = c_write_em;
      }
      if (em >= 100) c_write_em_GE_100++;
      else if (em >= 50) c_write_em_GE_50++;
      else if (em >= 40) c_write_em_GE_40++;
      else if (em >= 30) c_write_em_GE_30++;
      else if (em >= 20) c_write_em_GE_20++;
      else if (em >= 10) c_write_em_GE_10++;
    disk_pos = 0;
  }

  // lets see if we should update the date and time stamps.
  storage_->updateDateTimeStamps(object_id_, dtCreated_, dtModified_);

  storage_->close();

  if (c_read_em)
    printf(" # USB Packets: %u total: %u avg ms: %u max: %u\n", c_read_em,
           sum_read_em, sum_read_em / c_read_em, read_em_max);
  if (c_write_em)
    printf(" # Write: %u total:%u avg ms: %u max: %u(%u)\n", c_write_em,
           sum_write_em, sum_write_em / c_write_em, write_em_max, write_em_max_index);
    printf("  >> >=100ms: %u 50:%u 40:%u 30:%u 20:%u 10:%u\n", c_write_em_GE_100,
        c_write_em_GE_50, c_write_em_GE_40, c_write_em_GE_30, c_write_em_GE_20, c_write_em_GE_10);
    printf("  >> Last write times\n ");
    uint8_t dump_count = min(32u, c_write_em);
    uint8_t index = c_write_em;
    while (dump_count--){
      index = (index - 1) & 0x1f;
      printf(" %u",last_n_write_times[index] );
    }
  printf("\n");
  printf(">>>Total Time: %u\n", (uint32_t)em_total);
  Serial.flush();

  return (len == 0);
}
#endif

And I changed the calling cod ab about 2873 to just send incomplete transfer
Code:
        case 0x100D: // SendObject
          if (!SendObject()) {
            return_code = MTP_RESPONSE_INCOMPLETE_TRANSFER;  // what happens if we don't send response...
            //send_Event(
            //    MTP_EVENT_CANCEL_TRANSACTION); // try sending an event to cancel?
          } else {
              printf("SendObject() returned true\n"); Serial.flush();
          }
          len = 12;
          break;

What changed: Hint from earlier fix for faulting... It appears like after the host is done sending data, if I ask for another packet and have not responded, it will send me back a packet with 0 data...
The code above detects this. But not sure if it might do this in other states while still having more data to send... Does not appear to, but the code is set up debug output a another message if it finds a valid packet after a 0 length one.

A transfer debug:
Code:
LP:38701 CMD: 100c(SEND_OBJECT_INFO)l: 20 T:39 : 80001 ffffffff
38701 DATA:100c(SEND_OBJECT_INFO)l: 194 T:39 : 0 3000 14feff4 3000 0
SendObjectInfo: 524289 4294967295 20004560: ST:0 F:3000 0 SZ:22015988 3000 0 0 0 0 0 0 0 0 0 0 : T4LargeIndexedTestfile.txt
Read DateTime string: 20211119T043848.0
>> date/Time: 61972a58 11/19/2021 4:38:48
Created: 61972a58
Read DateTime string: 20211105T072016.0
>> date/Time: 6184db30 11/5/2021 7:20:16
Modified: 6184db30
38705 RESP:2001(RSP:OK)l: 24 T:39 : 80001 ffffffff 17
LP:38705 CMD: 100d(SEND_OBJECT)l: 12 T:3a
MTPD::SendObject: len:22015988

MTPD::SendObject *** USB Read 0 bytes ***

MTPD::SendObject *** USB Read 0 bytes ***
len 1012
 # USB Packets: 42998 total: 2208 avg ms: 0 max: 1
 # Write: 10750 total:824 avg ms: 0 max: 10(2217)
  >> >=100ms: 0 50:0 40:0 30:0 20:0 10:1
  >> Last write times
  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
>>>Total Time: 4058620
42764 RESP:2007(RSP:INCOMPLETE_TRANSFER)l: 12 T:3a
LP:42801 CMD: 1005(GET_STORAGE_INFO)l: 16 T:3b : 80001
524289 7 name:SD_Builtin
524289 7 name:SD_Builtin
42801 RESP:2001(RSP:OK)l: 16 T:3b : 80001

Now when I send the incomplete transfer I still get the failure beep, but the session continues. Note: The incomplete transfer object does not show up in the folder listing...
It will if you send a reset command...

But: by MTP, I believe the correct thing would be for me to delete the partial object in this case. The question is should I?

as for b) This may need some additional help, as in the case of this large file I routinely loose maybe 2 USB packets out of 43000.
 
Quick update: I went ahead and pushed in the above changes.

Also verified, that getting packet drop outs on Ubuntu as well. Interesting Ubuntu actually brings up dialog saying that the transfer failed...
 
KurtE said:
But: by MTP, I believe the correct thing would be for me to delete the partial object in this case. The question is should I?

as for b) This may need some additional help, as in the case of this large file I routinely loose maybe 2 USB packets out of 43000.
I think at some point probably (a) but maybe for now while in development leave option (b) if file size >0. Although when I get incomplete transfers I am seeing the file size as 0 bytes.
 
Mike posted an a version of MTP_TEST I've cleaned up a bit for integrity testing.
> storage_index is static/global so it persists between calls to #1 and #2
> #2 takes a drive #, but #1 does not : Edited in 'help' display as well.
> So '22' picks drive 2, and then '1' will show the active 'STORE:2' .vs. 'store:#' for others
> bigFile works on ALL drives : 25MB if it fits, otherwise Half the drive free space
> cut down the dots to max 60 per file write
> the code from old Integrity didn't use uint64 enough for these large media, so the display was off
> xx was reset in bigFile and that is why the KB/sec calc was off, even after going to millis from micros on long writes

Updated MTP_test that was posted:
Code:
#include <SD.h>
#include <MTP_Teensy.h>

//---------------------------------------------------
// Select drives you want to create
//---------------------------------------------------
#define USE_SD  1         // SDFAT based SDIO and SPI
#ifdef ARDUINO_TEENSY41
#define USE_LFS_RAM 0     // T4.1 PSRAM (or RAM)
#else
#define USE_LFS_RAM 0     // T4.1 PSRAM (or RAM)
#endif
#ifdef ARDUINO_TEENSY_MICROMOD
#define USE_LFS_QSPI 0    // T4.1 QSPI
#define USE_LFS_PROGM 1   // T4.4 Progam Flash
#define USE_LFS_SPI 0     // SPI Flash
#define USE_LFS_NAND 0
#define USE_LFS_QSPI_NAND 0
#define USE_LFS_FRAM 0
#else
#define USE_LFS_QSPI 1    // T4.1 QSPI
#define USE_LFS_PROGM 1   // T4.4 Progam Flash
#define USE_LFS_SPI 1     // SPI Flash
#define USE_LFS_NAND 1
#define USE_LFS_QSPI_NAND 0
#define USE_LFS_FRAM 0
#endif
#define USE_MSC 1    // set to > 0 experiment with MTP (USBHost.t36 + mscFS)
#define USE_SW_PU  0 //set to 1 if SPI devices does not have PUs,
                     // https://www.pjrc.com/better-spi-bus-design-in-3-steps/


#define DBGSerial Serial
FS *myfs;
File dataFile, myFile;  // Specifes that dataFile is of File type
int record_count = 0;
bool write_data = false;
uint8_t current_store = 0;

#define BUFFER_SIZE_INDEX 128
uint8_t write_buffer[BUFFER_SIZE_INDEX];
#define buffer_mult 4
uint8_t buffer_temp[buffer_mult*BUFFER_SIZE_INDEX];

int bytesRead;
uint32_t drive_index = 0;

// These can likely be left unchanged
#define MYBLKSIZE 2048 // 2048
#define SLACK_SPACE  40960 // allow for overhead slack space :: WORKS on FLASH {some need more with small alloc units}
//#define size_bigfile 1024*1024*24  //24 mb file
#define size_bigfile 1024*1024*124  //24 mb file

// Various Globals
const uint32_t lowOffset = 'a' - 'A';
const uint32_t lowShift = 13;
uint32_t errsLFS = 0, warnLFS = 0; // Track warnings or Errors
uint32_t lCnt = 0, LoopCnt = 0; // loop counters
uint64_t rdCnt = 0, wrCnt = 0; // Track Bytes Read and Written
int filecount = 0;
int loopLimit = 0; // -1 continuous, otherwise # to count down to 0
bool bWriteVerify = true;  // Verify on Write Toggle
File file3; // Single FILE used for all functions


//=============================================================================
// Global defines
//=============================================================================

MTPStorage storage;
MTPD    mtpd(&storage);

//=============================================================================
// MSC & SD classes
//=============================================================================
#if USE_SD==1
#define USE_BUILTIN_SDCARD
#if defined(USE_BUILTIN_SDCARD) && defined(BUILTIN_SDCARD)
#define CS_SD  BUILTIN_SDCARD
#else
#define CS_SD 10
#endif
#endif


// SDClasses 
#if USE_SD==1
  // edit SPI to reflect your configuration (following is for T4.1)
  #define SD_MOSI 11
  #define SD_MISO 12
  #define SD_SCK  13

  #define SPI_SPEED SD_SCK_MHZ(16)  // adjust to sd card 

  #if defined (BUILTIN_SDCARD)
    const char *sd_str[]={"sdio","sd1"}; // edit to reflect your configuration
    const int cs[] = {BUILTIN_SDCARD,10}; // edit to reflect your configuration
  #else
    const char *sd_str[]={"sd1"}; // edit to reflect your configuration
    const int cs[] = {10}; // edit to reflect your configuration
  #endif
  const int nsd = sizeof(sd_str)/sizeof(const char *);
  
SDClass sdx[nsd];
#endif

// =======================================================================
// Set up MSC Drive file systems on different storage media
// =======================================================================
#if USE_MSC == 1
#include <USBHost_t36.h>
#include <USBHost_ms.h>

// Add USBHost objectsUsbFs
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub(myusb);

// MSC objects.
msController drive1(myusb);
msController drive2(myusb);
msController drive3(myusb);
msController drive4(myusb);

msFilesystem msFS1(myusb);
msFilesystem msFS2(myusb);
msFilesystem msFS3(myusb);
msFilesystem msFS4(myusb);
msFilesystem msFS5(myusb);

// Quick and dirty
msFilesystem *pmsFS[] = {&msFS1, &msFS2, &msFS3, &msFS4, &msFS5};
#define CNT_MSC  (sizeof(pmsFS)/sizeof(pmsFS[0]))
uint32_t pmsfs_store_ids[CNT_MSC] = {0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL};
char  pmsFS_display_name[CNT_MSC][20];

msController *pdrives[] {&drive1, &drive2, &drive3, &drive4};
#define CNT_DRIVES  (sizeof(pdrives)/sizeof(pdrives[0]))
bool drive_previous_connected[CNT_DRIVES] = {false, false, false, false};
#endif


// =======================================================================
// Set up LittleFS file systems on different storage media
// =======================================================================

#if USE_LFS_FRAM == 1 || USE_LFS_NAND == 1 || USE_LFS_PROGM == 1 || USE_LFS_QSPI == 1 || USE_LFS_QSPI_NAND == 1 || \
  USE_LFS_RAM == 1 || USE_LFS_SPI == 1
#include <LittleFS.h>
#endif

#if USE_LFS_RAM==1
const char *lfs_ram_str[] = {"RAM1", "RAM2"};  // edit to reflect your configuration
const int lfs_ram_size[] = {200'000,4'000'000}; // edit to reflect your configuration
const int nfs_ram = sizeof(lfs_ram_str)/sizeof(const char *);
LittleFS_RAM ramfs[nfs_ram];
#endif

#if USE_LFS_QSPI==1
const char *lfs_qspi_str[]={"QSPI"};     // edit to reflect your configuration
const int nfs_qspi = sizeof(lfs_qspi_str)/sizeof(const char *);
LittleFS_QSPIFlash qspifs[nfs_qspi];
#endif

#if USE_LFS_PROGM==1
const char *lfs_progm_str[]={"PROGM"};     // edit to reflect your configuration
const int lfs_progm_size[] = {1'000'000}; // edit to reflect your configuration
const int nfs_progm = sizeof(lfs_progm_str)/sizeof(const char *);
LittleFS_Program progmfs[nfs_progm];
#endif

#if USE_LFS_SPI==1
const char *lfs_spi_str[]={"sflash5", "sflash6"}; // edit to reflect your configuration
const int lfs_cs[] = {5, 6}; // edit to reflect your configuration
const int nfs_spi = sizeof(lfs_spi_str)/sizeof(const char *);
LittleFS_SPIFlash spifs[nfs_spi];
#endif

#if USE_LFS_NAND == 1
const char *nspi_str[]={"WINBOND1G", "WINBOND2G"};     // edit to reflect your configuration
const int nspi_cs[] = {3, 4}; // edit to reflect your configuration
const int nspi_nsd = sizeof(nspi_cs)/sizeof(int);
LittleFS_SPINAND nspifs[nspi_nsd]; // needs to be declared if LittleFS is used in storage.h
#endif

#if USE_LFS_QSPI_NAND == 1
const char *qnspi_str[]={"WIN-M02"};     // edit to reflect your configuration
const int qnspi_nsd = sizeof(qnspi_str)/sizeof(const char *);
LittleFS_QPINAND qnspifs[qnspi_nsd]; // needs to be declared if LittleFS is used in storage.h
#endif

void storage_configure()
{
  DateTimeFields date;
  breakTime(Teensy3Clock.get(), date);
  const char *monthname[12]={
    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
  DBGSerial.printf("Date: %u %s %u %u:%u:%u\n",
    date.mday, monthname[date.mon], date.year+1900, date.hour, date.min, date.sec);

    
  #if USE_SD==1
    #if defined SD_SCK
      SPI.setMOSI(SD_MOSI);
      SPI.setMISO(SD_MISO);
      SPI.setSCK(SD_SCK);
    #endif

    for(int ii=0; ii<nsd; ii++)
    { 
      #if defined(BUILTIN_SDCARD)
        if(cs[ii] == BUILTIN_SDCARD)
        {
          DBGSerial.printf("!! Try installing BUILTIN SD Card");
          if(!sdx[ii].begin(BUILTIN_SDCARD))
          { 
            Serial.printf("SDIO Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
          }
          else
          {
            storage.addFilesystem(sdx[ii], sd_str[ii]);
            uint64_t totalSize = sdx[ii].totalSize();
            uint64_t usedSize  = sdx[ii].usedSize();
            Serial.printf("SDIO Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
            Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
          }
        }
        else if(cs[ii]<BUILTIN_SDCARD)
      #endif
      {
        pinMode(cs[ii],OUTPUT); digitalWriteFast(cs[ii],HIGH);
        if(!sdx[ii].begin(cs[ii])) 
        { Serial.printf("SD Storage %d %d %s failed or missing",ii,cs[ii],sd_str[ii]);  Serial.println();
        }
        else
        {
          storage.addFilesystem(sdx[ii], sd_str[ii]);
          uint64_t totalSize = sdx[ii].totalSize();
          uint64_t usedSize  = sdx[ii].usedSize();
          Serial.printf("SD Storage %d %d %s ",ii,cs[ii],sd_str[ii]); 
          Serial.print(totalSize); Serial.print(" "); Serial.println(usedSize);
        }
      }
    
    }
    #endif

#if USE_LFS_RAM==1
  for (int ii=0; ii<nfs_ram;ii++) {
    if (!ramfs[ii].begin(lfs_ram_size[ii])) {
      DBGSerial.printf("Ram Storage %d %s failed or missing",ii,lfs_ram_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(ramfs[ii], lfs_ram_str[ii]);
      uint64_t totalSize = ramfs[ii].totalSize();
      uint64_t usedSize  = ramfs[ii].usedSize();
      DBGSerial.printf("RAM Storage %d %s %llu %llu\n", ii, lfs_ram_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_PROGM==1
  for (int ii=0; ii<nfs_progm;ii++) {
    if (!progmfs[ii].begin(lfs_progm_size[ii])) {
      DBGSerial.printf("Program Storage %d %s failed or missing",ii,lfs_progm_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(progmfs[ii], lfs_progm_str[ii]);
      uint64_t totalSize = progmfs[ii].totalSize();
      uint64_t usedSize  = progmfs[ii].usedSize();
      DBGSerial.printf("Program Storage %d %s %llu %llu\n", ii, lfs_progm_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_QSPI==1
  for(int ii=0; ii<nfs_qspi;ii++) {
    if(!qspifs[ii].begin()) {
      DBGSerial.printf("QSPI Storage %d %s failed or missing",ii,lfs_qspi_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(qspifs[ii], lfs_qspi_str[ii]);
      uint64_t totalSize = qspifs[ii].totalSize();
      uint64_t usedSize  = qspifs[ii].usedSize();
      DBGSerial.printf("QSPI Storage %d %s %llu %llu\n", ii, lfs_qspi_str[ii], totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_SPI==1
  for (int ii=0; ii<nfs_spi;ii++) {
    if (USE_SW_PU == 1) {
      pinMode(lfs_cs[ii],OUTPUT);
      digitalWriteFast(lfs_cs[ii],HIGH);
    }
    if (!spifs[ii].begin(lfs_cs[ii], SPI)) {
      DBGSerial.printf("SPIFlash Storage %d %d %s failed or missing",ii,lfs_cs[ii],lfs_spi_str[ii]);      DBGSerial.println();
    } else {
      storage.addFilesystem(spifs[ii], lfs_spi_str[ii]);
      uint64_t totalSize = spifs[ii].totalSize();
      uint64_t usedSize  = spifs[ii].usedSize();
      DBGSerial.printf("SPIFlash Storage %d %d %s %llu %llu\n", ii, lfs_cs[ii], lfs_spi_str[ii],
        totalSize, usedSize);
    }
  }
#endif
#if USE_LFS_NAND == 1
  for(int ii=0; ii<nspi_nsd;ii++) {
    if (USE_SW_PU == 1) {
      pinMode(nspi_cs[ii],OUTPUT);
      digitalWriteFast(nspi_cs[ii],HIGH);
    }
    if(!nspifs[ii].begin(nspi_cs[ii], SPI)) {
      DBGSerial.printf("SPIFlash NAND Storage %d %d %s failed or missing",ii,nspi_cs[ii],nspi_str[ii]);
      DBGSerial.println();
    } else {
      storage.addFilesystem(nspifs[ii], nspi_str[ii]);
      uint64_t totalSize = nspifs[ii].totalSize();
      uint64_t usedSize  = nspifs[ii].usedSize();
      DBGSerial.printf("Storage %d %d %s %llu %llu\n", ii, nspi_cs[ii], nspi_str[ii],
        totalSize, usedSize);
    }
  }
#endif

#if USE_LFS_QSPI_NAND == 1
  for(int ii=0; ii<qnspi_nsd;ii++) {
    if(!qnspifs[ii].begin()) {
       DBGSerial.printf("QSPI NAND Storage %d %s failed or missing",ii,qnspi_str[ii]); DBGSerial.println();
    } else {
      storage.addFilesystem(qnspifs[ii], qnspi_str[ii]);
      uint64_t totalSize = qnspifs[ii].totalSize();
      uint64_t usedSize  = qnspifs[ii].usedSize();
      DBGSerial.printf("Storage %d %s %llu %llu\n", ii, qnspi_str[ii], totalSize, usedSize);
    }
  }
#endif

}

void setup()
{

  // Open serial communications and wait for port to open:
#if defined(USB_MTPDISK_SERIAL)
  while (!Serial && millis() < 5000) {
    // wait for serial port to connect.
  }
#else
  //while(!DBGSerial.available()); // comment if you do not want to wait for
                                   // terminal (otherwise press any key to continue)
  while (!Serial && !DBGSerial.available() && millis() < 5000) 
  //myusb.Task(); // or third option to wait up to 5 seconds and then continue
#endif

  DBGSerial.print(CrashReport);
  DBGSerial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  delay(3000);
  

  //This is mandatory to begin the mtpd session.
  mtpd.begin();
  
  storage_configure();

  #if USE_MSC == 1
  myusb.begin();
  DBGSerial.print("Initializing MSC Drives ...");
  DBGSerial.println("\nInitializing USB MSC drives...");
  DBGSerial.println("MSC and MTP initialized.");
  checkMSCChanges();
  #endif
  DBGSerial.println("\nSetup done");
  menu();
}

int ReadAndEchoSerialChar() {
  int ch = DBGSerial.read();
  if (ch >= ' ') DBGSerial.write(ch);
  return ch;
}

void loop()
{
  static uint8_t storage_index = 0;
  if ( DBGSerial.available() ) {
    uint8_t command = DBGSerial.read();
    int ch = DBGSerial.read();
    if ('2'==command) storage_index = CommandLineReadNextNumber(ch, 0);
    while (ch == ' ') ch = DBGSerial.read();

    uint32_t fsCount;
    switch (command) {
    case '1':
      // first dump list of storages:
      fsCount = storage.getFSCount();
      DBGSerial.printf("\nDump Storage list(%u)\n", fsCount);
      for (uint32_t ii = 0; ii < fsCount; ii++) {
        if ( ii == storage_index )
          DBGSerial.print("STORE");
        else
          DBGSerial.print("store");
        DBGSerial.printf(":%u storage:%x name:%s fs:%x\n", ii, mtpd.Store2Storage(ii),
          storage.getStoreName(ii), (uint32_t)storage.getStoreFS(ii));
      }
      //DBGSerial.println("\nDump Index List");
      //storage.dumpIndexList();
      break;
    case '2':
      if (storage_index < storage.getFSCount()) {
        DBGSerial.printf("Storage Index %u Name: %s Selected\n", storage_index,
          storage.getStoreName(storage_index));
        myfs = storage.getStoreFS(storage_index);
        current_store = storage_index;
      } else {
        DBGSerial.printf("Storage Index %u out of range\n", storage_index);
      }
      break;

    case 'l': listFiles(); break;
    case 'e': eraseFiles(); break;
    case 's':
      DBGSerial.println("\nLogging Data!!!");
      write_data = true;   // sets flag to continue to write data until new command is received
      // opens a file or creates a file if not present,  FILE_WRITE will append data to
      // to the file created.
      dataFile = myfs->open("datalog.txt", FILE_WRITE);
      logData();
      break;
    case 'x': stopLogging(); break;
    case'r':
      DBGSerial.println("Reset");
      mtpd.send_DeviceResetEvent();
      break;
    case 'd': dumpLog(); break;
    case 'b':
      bigFile( 0 ); // delete
      command = 0;
      break;
    case 'B':
      bigFile( 1 ); // CREATE
      command = 0;
      break;
    case 't':
      bigFile2MB( 0 ); // CREATE
      command = 0;
      break;
    case 'S':
      bigFile2MB( 1 ); // CREATE
      command = 0;
     break;
    case 'n': // No Verify on write
      bWriteVerify = !bWriteVerify;
      bWriteVerify ? DBGSerial.print(" Write Verify on: ") : DBGSerial.print(" Write Verify off: ");
     command = 0;
      break;
    case 'i':
      writeIndexFile();
      break;
    case 'R':
      DBGSerial.print(" RESTART Teensy ...");
      delay(100);
      SCB_AIRCR = 0x05FA0004;
      break;
    
    case '\r':
    case '\n':
    case 'h': menu(); break;
    }
    while (DBGSerial.read() != -1) ; // remove rest of characters.
  } else {
    #if USE_MSC == 1
    checkMSCChanges();
    #endif
    mtpd.loop();
  }

  if (write_data) logData();
}

#if USE_MSC == 1
void checkMSCChanges() {
  myusb.Task();

  USBMSCDevice mscDrive;
  PFsLib pfsLIB;
  for (uint8_t i=0; i < CNT_DRIVES; i++) {
    if (*pdrives[i]) {
      if (!drive_previous_connected[i]) {
        if (mscDrive.begin(pdrives[i])) {
          Serial.printf("\nUSB Drive: %u connected\n", i);
          pfsLIB.mbrDmp(&mscDrive, (uint32_t)-1, Serial);
          Serial.printf("\nTry Partition list");
          pfsLIB.listPartitions(&mscDrive, Serial);
          drive_previous_connected[i] = true;
        }
      }
    } else {
      drive_previous_connected[i] = false;
    }
  }
  bool send_device_reset = false;
  for (uint8_t i = 0; i < CNT_MSC; i++) {
    if (*pmsFS[i] && (pmsfs_store_ids[i] == 0xFFFFFFFFUL)) {
      Serial.printf("Found new Volume:%u\n", i); Serial.flush();
      // Lets see if we can get the volume label:
      char volName[20];
      if (pmsFS[i]->mscfs.getVolumeLabel(volName, sizeof(volName)))
        snprintf(pmsFS_display_name[i], sizeof(pmsFS_display_name[i]), "MSC%d-%s", i, volName);
      else
        snprintf(pmsFS_display_name[i], sizeof(pmsFS_display_name[i]), "MSC%d", i);
      pmsfs_store_ids[i] = storage.addFilesystem(*pmsFS[i], pmsFS_display_name[i]);

      // Try to send store added. if > 0 it went through = 0 stores have not been enumerated
      if (mtpd.send_StoreAddedEvent(pmsfs_store_ids[i]) < 0) send_device_reset = true;
    }
    // Or did volume go away?
    else if ((pmsfs_store_ids[i] != 0xFFFFFFFFUL) && !*pmsFS[i] ) {
      if (mtpd.send_StoreRemovedEvent(pmsfs_store_ids[i]) < 0) send_device_reset = true;
      storage.removeFilesystem(pmsfs_store_ids[i]);
      // Try to send store added. if > 0 it went through = 0 stores have not been enumerated
      pmsfs_store_ids[i] = 0xFFFFFFFFUL;
    }
  }
  if (send_device_reset) mtpd.send_DeviceResetEvent();
}
#endif

void logData()
{
  // make a string for assembling the data to log:
  String dataString = "";

  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    int sensor = analogRead(analogPin);
    dataString += String(sensor);
    if (analogPin < 2) {
      dataString += ",";
    }
  }

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    // print to the serial port too:
    DBGSerial.println(dataString);
    record_count += 1;
  } else {
    // if the file isn't open, pop up an error:
    DBGSerial.println("error opening datalog.txt");
  }
  delay(100); // run at a reasonable not-too-fast speed for testing
}

void stopLogging()
{
  DBGSerial.println("\nStopped Logging Data!!!");
  write_data = false;
  // Closes the data file.
  dataFile.close();
  DBGSerial.printf("Records written = %d\n", record_count);
  mtpd.send_DeviceResetEvent();
}


void dumpLog()
{
  DBGSerial.println("\nDumping Log!!!");
  // open the file.
  dataFile = myfs->open("datalog.txt");

  // if the file is available, write to it:
  if (dataFile) {
    while (dataFile.available()) {
      DBGSerial.write(dataFile.read());
    }
    dataFile.close();
  }
  // if the file isn't open, pop up an error:
  else {
    DBGSerial.println("error opening datalog.txt");
  }
}

void menu()
{
  DBGSerial.println();
  DBGSerial.println("Menu Options:");
  DBGSerial.println("\t1 - List Drives (Step 1)");
  DBGSerial.println("\t2# - Select Drive # for Logging (Step 2)");
  DBGSerial.println("\tl - List files on disk");
  DBGSerial.println("\te - Erase files on disk with Format");
  DBGSerial.println("\ts - Start Logging data (Restarting logger will append records to existing log)");
  DBGSerial.println("\tx - Stop Logging data");
  DBGSerial.println("\td - Dump Log");
  DBGSerial.println("\tr - Reset MTP");
  DBGSerial.printf("\n\t%s","R - Restart Teensy");
  DBGSerial.printf("\n\t%s","i - Write Index File to disk");
  DBGSerial.printf("\n\t%s","'B, or b': Make Big file half of free space, or remove all Big files");
  DBGSerial.printf("\n\t%s","'S, or t': Make 2MB file , or remove all 2MB files");
  DBGSerial.printf("\n\t%s","'n' No verify on Write- TOGGLE");

  DBGSerial.println("\th - Menu");
  DBGSerial.println();
}

void listFiles()
{
  DBGSerial.print("\n Space Used = ");
  DBGSerial.println(myfs->usedSize());
  DBGSerial.print("Filesystem Size = ");
  DBGSerial.println(myfs->totalSize());

  printDirectory(myfs);
}

void eraseFiles()
{
  //DBGSerial.println("Formating not supported at this time");
  DBGSerial.println("\n*** Erase/Format started ***");
  myfs->format(1, '.', DBGSerial);
  Serial.println("Completed, sending device reset event");
  mtpd.send_DeviceResetEvent();
}

void printDirectory(FS *pfs) {
  DBGSerial.println("Directory\n---------");
  printDirectory(pfs->open("/"), 0);
  DBGSerial.println();
}

void printDirectory(File dir, int numSpaces) {
  while (true) {
    File entry = dir.openNextFile();
    if (! entry) {
      //DBGSerial.println("** no more files **");
      break;
    }
    printSpaces(numSpaces);
    DBGSerial.print(entry.name());
    if (entry.isDirectory()) {
      DBGSerial.println("/");
      printDirectory(entry, numSpaces + 2);
    } else {
      // files have sizes, directories do not
      printSpaces(36 - numSpaces - strlen(entry.name()));
      DBGSerial.print("  ");
      DBGSerial.println(entry.size(), DEC);
    }
    entry.close();
  }
}

void printSpaces(int num) {
  for (int i = 0; i < num; i++) {
    DBGSerial.print(" ");
  }
}


uint32_t CommandLineReadNextNumber(int &ch, uint32_t default_num) {
  while (ch == ' ') ch = DBGSerial.read();
  if ((ch < '0') || (ch > '9')) return default_num;

  uint32_t return_value = 0;
  while ((ch >= '0') && (ch <= '9')) {
    return_value = return_value * 10 + ch - '0';
    ch = DBGSerial.read();
  }
  return return_value;
}



void readVerify( char szPath[], char chNow ) {
  uint32_t timeMe = micros();
  file3 = myfs->open(szPath);
  if ( 0 == file3 ) {
    Serial.printf( "\tV\t Fail File open %s\n", szPath );
    errsLFS++;
  }
  char mm;
  char chNow2 = chNow + lowOffset;
  uint32_t ii = 0;
  while ( file3.available() ) {
    file3.read( &mm , 1 );
    rdCnt++;
    //Serial.print( mm ); // show chars as read
    ii++;
    if ( 0 == (ii / lowShift) % 2 ) {
      if ( chNow2 != mm ) {
        Serial.printf( "<Bad Byte!  %c! = %c [0x%X] @%u\n", chNow2, mm, mm, ii );
        errsLFS++;
        break;
      }
    }
    else {
      if ( chNow != mm ) {
        Serial.printf( "<Bad Byte!  %c! = %c [0x%X] @%u\n", chNow, mm, mm, ii );
        errsLFS++;
        break;
      }
    }
  }
  Serial.printf( "  Verify %u Bytes ", ii );
  if (ii != file3.size()) {
    Serial.printf( "\n\tRead Count fail! :: read %u != f.size %llu", ii, file3.size() );
    errsLFS++;
  }
  file3.close();
  timeMe = micros() - timeMe;
  Serial.printf( " @KB/sec %5.2f", ii / (timeMe / 1000.0) );
}

bool bigVerify( char szPath[], char chNow ) {
  uint32_t timeMe = micros();
  file3 = myfs->open(szPath);
  uint64_t fSize;
  if ( 0 == file3 ) {
    return false;
  }
  char mm;
  uint32_t ii = 0;
  uint32_t kk = file3.size() / 50;
  fSize = file3.size();
  Serial.printf( "\tVerify %s bytes %llu : ", szPath, fSize );
  while ( file3.available() ) {
    file3.read( &mm , 1 );
    rdCnt++;
    ii++;
    if ( !(ii % kk) ) Serial.print('.');
    if ( chNow != mm ) {
      Serial.printf( "<Bad Byte!  %c! = %c [0x%X] @%u\n", chNow, mm, mm, ii );
      errsLFS++;
      break;
    }
    if ( ii > fSize ) { // catch over length return makes bad loop !!!
      Serial.printf( "\n\tFile LEN Corrupt!  FS returning over %u bytes\n", fSize );
      errsLFS++;
      break;
    }
  }
  if (ii != file3.size()) {
    Serial.printf( "\n\tRead Count fail! :: read %u != f.size %llu\n", ii, file3.size() );
    errsLFS++;
  }
  else
    Serial.printf( "\tGOOD! >>  bytes %lu", ii );
  file3.close();
  timeMe = micros() - timeMe;
  Serial.printf( "\n\tBig read&compare KBytes per second %5.2f \n", ii / (timeMe / 1000.0) );
  if ( 0 == ii ) return false;
  return true;
}



void bigFile( int doThis ) {
  char myFile[] = "/0_bigfile.txt";
  char fileID = '0' - 1;
  DateTimeFields dtf = {0, 10, 7, 0, 22, 7, 121};

  if ( 0 == doThis ) {  // delete File
    Serial.printf( "\nDelete with read verify all #bigfile's\n");
    do {
      fileID++;
      myFile[1] = fileID;
      if ( myfs->exists(myFile) && bigVerify( myFile, fileID) ) {
        filecount--;
        myfs->remove(myFile);
      }
      else break; // no more of these
    } while ( 1 );
  }
  else {  // FILL DISK
    uint32_t resW = 1;
    
    char someData[MYBLKSIZE];
    uint64_t xx, toWrite;
    toWrite = (myfs->totalSize()) - myfs->usedSize();
    if ( toWrite < 65535 ) {
      Serial.print( "Disk too full! DO :: reformat");
      return;
    }
    if ( size_bigfile < toWrite *2 )
      toWrite = size_bigfile;
    else 
      toWrite/=2;
    toWrite -= SLACK_SPACE;
    xx = toWrite;
    Serial.printf( "\nStart Big write of %llu Bytes", xx);
    uint32_t timeMe = millis();
    file3 = nullptr;
    do {
      if ( file3 ) file3.close();
      fileID++;
      myFile[1] = fileID;
      file3 = myfs->open(myFile, FILE_WRITE);
    } while ( fileID < '9' && file3.size() > 0);
    if ( fileID == '9' ) {
      Serial.print( "Disk has 9 halves 0-8! DO :: b or q or F");
      return;
    }
    memset( someData, fileID, MYBLKSIZE );
    uint64_t hh = 0;
    uint64_t kk = toWrite/MYBLKSIZE/60;
    while ( toWrite > MYBLKSIZE && resW > 0 ) {
      resW = file3.write( someData , MYBLKSIZE );
      hh++;
      if ( !(hh % kk) ) Serial.print('.');
      toWrite -= MYBLKSIZE;
    }
    file3.setCreateTime(dtf);
    file3.setModifyTime(dtf);
    file3.close();
    timeMe = millis() - timeMe;
    file3 = myfs->open(myFile, FILE_WRITE);
    if ( file3.size() > 0 ) {
      filecount++;
      Serial.printf( "\nBig write %s took %5.2f Sec for %llu Bytes : file3.size()=%llu", myFile , timeMe / 1000.0, xx, file3.size() );
    }
    if ( file3 != 0 ) file3.close();
    Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1.0) );
    Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs->usedSize(), myfs->totalSize());
    if ( myfs->usedSize() == myfs->totalSize() ) {
      Serial.printf("\n\n\tWARNING: DISK FULL >>>>>  Bytes Used: %llu, Bytes Total:%llu\n\n", myfs->usedSize(), myfs->totalSize());
      warnLFS++;
    }
    if ( resW < 0 ) {
      Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW );
      errsLFS++;
      myfs->remove(myFile);
    }
  }
}

void bigFile2MB( int doThis ) {
  char myFile[] = "/0_2MBfile.txt";
  char fileID = '0' - 1;
  DateTimeFields dtf = {0, 10, 7, 0, 22, 7, 121};

  if ( 0 == doThis ) {  // delete File
    Serial.printf( "\nDelete with read verify all #bigfile's\n");
    do {
      fileID++;
      myFile[1] = fileID;
      if ( myfs->exists(myFile) && bigVerify( myFile, fileID) ) {
        filecount--;
        myfs->remove(myFile);
      }
      else break; // no more of these
    } while ( 1 );
  }
  else {  // FILL DISK
    uint32_t resW = 1;
    
    char someData[2048];
    uint32_t xx, toWrite;
    toWrite = 2048 * 1000;
    if ( toWrite > (65535 + (myfs->totalSize() - myfs->usedSize()) ) ) {
      Serial.print( "Disk too full! DO :: q or F");
      return;
    }
    xx = toWrite;
    Serial.printf( "\nStart Big write of %u Bytes", xx);
    uint32_t timeMe = micros();
    file3 = nullptr;
    do {
      if ( file3 ) file3.close();
      fileID++;
      myFile[1] = fileID;
      file3 = myfs->open(myFile, FILE_WRITE);
    } while ( fileID < '9' && file3.size() > 0);
    if ( fileID == '9' ) {
      Serial.print( "Disk has 9 files 0-8! DO :: b or q or F");
      return;
    }
    memset( someData, fileID, 2048 );
    int hh = 0;
    while ( toWrite >= 2048 && resW > 0 ) {
      resW = file3.write( someData , 2048 );
      hh++;
      if ( !(hh % 40) ) Serial.print('.');
      toWrite -= 2048;
    }
    xx -= toWrite;
    file3.setCreateTime(dtf);
    file3.setModifyTime(dtf);
    file3.close();
    timeMe = micros() - timeMe;
    file3 = myfs->open(myFile, FILE_WRITE);
    if ( file3.size() > 0 ) {
      filecount++;
      Serial.printf( "\nBig write %s took %5.2f Sec for %lu Bytes : file3.size()=%llu", myFile , timeMe / 1000000.0, xx, file3.size() );
    }
    if ( file3 != 0 ) file3.close();
    Serial.printf( "\n\tBig write KBytes per second %5.2f \n", xx / (timeMe / 1000.0) );
    Serial.printf("\nBytes Used: %llu, Bytes Total:%llu\n", myfs->usedSize(), myfs->totalSize());
    if ( myfs->usedSize() == myfs->totalSize() ) {
      Serial.printf("\n\n\tWARNING: DISK FULL >>>>>  Bytes Used: %llu, Bytes Total:%llu\n\n", myfs->usedSize(), myfs->totalSize());
      warnLFS++;
    }
    if ( resW < 0 ) {
      Serial.printf( "\nBig write ERR# %i 0x%X \n", resW, resW );
      errsLFS++;
      myfs->remove(myFile);
    }
  }
}

void writeIndexFile() 
{
  DateTimeFields dtf = {0, 10, 7, 0, 22, 7, 121};
  // open the file.
  Serial.println("Write Large Index File");
  uint32_t timeMe = micros();
  file3 = myfs->open("LargeIndexedTestfile.txt", FILE_WRITE_BEGIN);
  if (file3) {
    file3.truncate(); // Make sure we wipe out whatever was written earlier
    for (uint32_t i = 0; i < 43000*4; i++) {
      memset(write_buffer, 'A'+ (i & 0xf), sizeof(write_buffer));
      
      file3.printf("%06u ", i >> 2);  // 4 per physical buffer
      file3.write(write_buffer, i? 120 : 120-12); // first buffer has other data...
      file3.printf("\n");
    }
    file3.setCreateTime(dtf);
    file3.setModifyTime(dtf);
    file3.close();
    
    timeMe = micros() - timeMe;
    file3 = myfs->open("LargeIndexedTestfile.txt", FILE_WRITE);
    if ( file3.size() > 0 ) {
       Serial.printf( " Total time to write %d byte: %5.2f seconds\n", file3.size(), (timeMe / 1000.0));
       Serial.printf( "\n\tBig write KBytes per second %5.2f \n", file3.size() / (timeMe / 1000.0) );
    }
    if ( file3 != 0 ) file3.close();
    Serial.println("\ndone.");
    
  }
}

<edit>: That's as far as I got testing - didn't update 2MB file write that may have some of the 64 bit issues - that seems to bypass them with small file.
 
Last edited:
I think at some point probably (a) but maybe for now while in development leave option (b) if file size >0. Although when I get incomplete transfers I am seeing the file size as 0 bytes.

Hopefully the code is in place that if it gets the 0 length packet(s) that it exits the loop and closes the file as well as update date/time, which should update the sizes of the file. I was mostly testing with SD cards yesterday,
although did start doing some littleFS on Ubuntu.

It was interesting I was testing some on MicroMod - which updated the bootloader to new one... It looked like it preserved the Program space FS across reprograms. It was also interesting that at least once I crashed the MTP support library on Ubuntu where it asked if it was OK to send out Crashreport.

Also another run, where using Paul's Micromod board, with the sort of kitchen sink build (SD_Program_SPI_QSPI_MTP-logger) sketch, after I changed it to say for Micromod to use SDIO SD... When it came up, When I tried to show the storags, it gave some cryptic message... Will try it again
 
I just rebuilt and ran the sketch mentioned above on Ubuntu again... This time it came up but has some obvious issue?
As you can see in screenshot:
Screenshot from 2021-11-20 05-18-37.png


So I thought maybe the Program disk, was corrupted, but:
using l command:
Code:
 Space Used = 524288
Filesystem Size = 524288
Directory
---------
Screenshot from 2021-09-11 05-46-25.png  115200
Screenshot from 2021-09-11 05-47-49.png  112753
mtpindex.dat                          0

Edit: Should Mention that if I run the Simple sketch, the files show up correctly.
 
Back
Top