USBHost_t36 USB Mass Storage Driver Experiments

Looks right for FAT32. Fat16 was big enough for MS-DOS as HDD's grew - much more so accounting for var sized clusters with compression factor of 16:1. Even Win95 with FAT32 couldn't fit FAT in single alloc limit of 1MB.

512B/sec and 4B/fat entry is 128 fat entries per sector

4,092,416 B in the 7993 sectors of the FAT!

So read was 0.694 MB/sec ? ... with parsing? { 4MB in 5.9 seconds } - should read faster than that.


PRIOR NOTE p#613: Yes having just a VolumeName.ino would be nice - like i2c scanner just to show finding the attached devices.

Then perhaps read/write testing - the @mjs513 speedBench() for ref?
 
Just messed up my sdfat lib as well just for a another data point for you.:
This is for a SD Card with 2 FAT32 and 1 exfat:
Code:
msc # Partition Table
	part,boot,bgnCHS[3],type,endCHS[3],start,length
FAT32:	1,80,0x0,0x1,0x10,0xC,0x3,0xE0,0xFF,2048,524288
FAT32:	2,0,0xC2,0x23,0x20,0xC,0xFE,0xFF,0xFF,526336,16564224
exFAT:	3,0,0xFE,0xFF,0xFF,0x7,0xFE,0xFF,0xFF,17092608,232642560
pt_#0:	4,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0

USB Fat Type: Fat32
Volume Name: system-boot
2021-01-01 00:00     128890 bench0.txt
2021-01-01 00:00     128890 bench1.txt
2021-01-01 00:00     200000 bench2.txt
2021-01-01 00:00     200000 bench3.txt
2021-01-01 00:00     149000 bench4.txt
2021-01-01 00:00     149000 bench5.txt
###FatPartition::freeClusterCount: FT:32 start:2080 todo:516192
    m_sectorsPerCluster:1
    m_clusterSectorMask:0
    m_sectorsPerClusterShift:0
    m_fatType:32
    m_rootDirEntryCount:0
    m_allocSearchStart:1
    m_sectorsPerFat:4033
    m_dataStartSector:10146
    m_fatStartSector:2080
    m_lastCluster:516191
    m_rootDirStart:2
    m_bytesPerSector:512
FAT16 Partition Total Size:264289280 Used:961024 time us: 2122125
Try calling getUSBPartitionVolumeLabel
FAT32:	>> Volume name:(system-boot)

USB Fat Type: Fat32
Volume Name: NEW VOLUME1
2021-01-01 00:00     128890 bench0.txt
2021-01-01 00:00     128890 bench1.txt
2021-01-01 00:00     200000 bench2.txt
2021-01-01 00:00     200000 bench3.txt
2021-01-01 00:00     149000 bench4.txt
2021-01-01 00:00     149000 bench5.txt
###FatPartition::freeClusterCount: FT:32 start:526814 todo:2066434
    m_sectorsPerCluster:8
    m_clusterSectorMask:7
    m_sectorsPerClusterShift:3
    m_fatType:32
    m_rootDirEntryCount:0
    m_allocSearchStart:1
    m_sectorsPerFat:16145
    m_dataStartSector:559104
    m_fatStartSector:526814
    m_lastCluster:2066433
    m_rootDirStart:2
    m_bytesPerSector:512
FAT16 Partition Total Size:8464105472 Used:983040 time us: 8072750
Try calling getUSBPartitionVolumeLabel
FAT32:	>> Volume name:(NEW VOLUME1)

USB Fat Type: ExFat
Volume Name: New Volume2
2021-01-27 05:36       429216 CommandStation-EX-Arch-v1-0.pdf
2020-12-27 14:47      5725283 VSLAM_and_Navigation_System_of_Unmanned_Ground_Vehicle_Based_on_RGB-D_Camera.pdf
ExFat Partition Total Size:119107747840 Used:7077888 time us: 111003
 
Thanks @mjs513 will probably look more in the morning.

Today I also hacked up that function some more that printed that data, and used digital Write high/low with pin 0 for the whole duration and pin 1 around each call to the cached read...

And the capture of data also confirmed it:
screenshot.jpg
Which shows the two getting free counts for the two FAT partitions.
A close up of the start of the first one shows that the majority of time it is waiting for the sectors to be read:
screenshot2.jpg

One of the interesting things with the new Logic 2 software, is you can use things like shift mouse select over a set of data and it will do things like count how many pulses happened in that region, which helps confirm some of the calculated data.
 
Was there a change I missed for improved SD card reading? I have four partitions on mine and it is only giving this:
Code:
Initialize SD card...SD card is present.

SD Fat Type: Fat32
Volume Name: SD1_8GB_AD 
done...

Swapped in other simple FAT32 SD and no DIR print??

Pulled down the Yesterday change to KurtE/MTP_t4/tree/MEM_send_object_large
 
Quick note: I am thinking it might make sense to add the VolumeName.ino as an example sketch to maybe MscFS.h project. That way we can all keep track of it?

Make sense?

Yes it does. Just trying to figure out which version I should add to UsbMSCFat. I am thinking p#506 as a base sketch, but not sure.
 
Yes it does. Just trying to figure out which version I should add to UsbMSCFat. I am thinking p#506 as a base sketch, but not sure.

I'm using p#522 here - just minor tweaks for cleaner output and a KurtE Edit that works between 506 and 507.
 
@wwatson - Sounds great.

@all as I mentioned over in the MTP thread (https://forum.pjrc.com/threads/43050-MTP-Responder-Contribution?p=271721&viewfull=1#post271721)

I am playing around with seeing about the SDFat/Fslib working with volumes. So I started hacking up a fsPartition.h file which I have it compiling within my version of the volumeName sketch.

But still not far enough to make work...

Mainly now because of the class structures...

That is:
for Fat we have: class FatVolume : public FatPartition {
for ExFat we have: class ExFatVolume : public ExFatPartition {

but Fs we have: class FsVolume {

Which knows nothing about partition...
But let me see if I can hack around it......
Wonder if @Paul and/or @Bill would mind if the FsVolume::begin took optional parameter for part... WIll try that out.
 
Hello,
was wondering, what is the current status of USBHost_t36 vs. reading mass storage devices?
In particular, I would like to read a "raw" SD card inserted into a USB card reader (which I would connect to the USB Host port of the T3.6).
"Raw" as in "no filesystem": would read it the raw sectors (long story).
 
Ok here is the fix for extFat:
Code:
Waiting up to 5 seconds for USB drive
Initialize USB drive...USB drive 1 is present.


msc1 Partition Table
part,boot,bgnCHS[3],type,endCHS[3],start,length
FAT32: 1,80,0x0,0x1,0x10,0xC,0x3,0xE0,0xFF,2048,524288
FAT32: 2,0,0xC2,0x23,0x20,0xC,0xFE,0xFF,0xFF,526336,16564224
exFAT: 3,0,0xFE,0xFF,0xFF,0x7,0xFE,0xFF,0xFF,17092608,232642560
4,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0

Fat Type: Fat32
Volume Name: system-boot
2021-01-01 00:00     128890 bench0.txt
2021-01-01 00:00     128890 bench1.txt
2021-01-01 00:00     200000 bench2.txt
2021-01-01 00:00     200000 bench3.txt
2021-01-01 00:00     149000 bench4.txt
2021-01-01 00:00     149000 bench5.txt

Fat Type: Fat32
Volume Name: NEW VOLUME1
2021-01-01 00:00     128890 bench0.txt
2021-01-01 00:00     128890 bench1.txt
2021-01-01 00:00     200000 bench2.txt
2021-01-01 00:00     200000 bench3.txt
2021-01-01 00:00     149000 bench4.txt
2021-01-01 00:00     149000 bench5.txt

Fat Type: ExFat
Volume Name: New Volume2
2021-01-27 05:36       429216 CommandStation-EX-Arch-v1-0.pdf
2020-12-27 14:47      5725283 VSLAM_and_Navigation_System_of_Unmanned_Ground_Vehicle_Based_on_RGB-D_Camera.pdf
No or Not Supported Partition

Initialize SD card...initialization failed.

Fat Type: ExFAT
Failed to get volume label
done...
Press any key to run again

Code:
[CODE]//  VolumeName.ino
//  An example of how to retrieve Fat32 and ExFat volume names using SdFat.
//  Works with SD cards and USB mass storage drives.

#include "Arduino.h"
#include "mscFS.h"

// Setup USBHost_t36 and as many HUB ports as needed.
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
USBHub hub4(myusb);

// Setup MSC for the number of USB Drives you are using. (Two for this example)
// Mutiple  USB drives can be used. Hot plugging is supported. There is a slight
// delay after a USB MSC device is plugged in. This is waiting for initialization
// but after it is initialized ther should be no delay.
msController msDrive1(myusb);
msController msDrive2(myusb);

#define sdDrive 1
#define msDrive 2

#define SD_CONFIG SdioConfig(FIFO_SDIO)

// set up variables using the mscFS utility library functions:
UsbFs msc1;
SdFs sd;
//FatVolume partVol;

//create holding array for partions
uint8_t partitionTable[4];
int32_t dataStart[4];

// Get ExFat volume name.
bool getExFatVolumeLabel(uint8_t  drvType, uint8_t part) {
  uint8_t buf[32];
  UsbExFat volName;
  SdExFat volName1;
  ExFatFile root;
  msController *mscDrive;
  DirLabel_t *dir;

  ExFatVolume expartVol;

  if (drvType == msDrive) {
    mscDrive = &msDrive1;
    //if (!volName.begin(&msDrive1)) {
   //   Serial.println("EXFat volName.begin failed");
   //   return false;
   // }
    expartVol.begin(msc1.usbDrive(), true, part);
    expartVol.chvol();
    if (!root.openRoot(&expartVol)) {
      Serial.println("openRoot failed");
      return false;
    }
  }
  if (drvType == sdDrive) {
    if (!volName1.begin(SD_CONFIG)) {
      return false;
    }
    if (!root.openRoot(&volName1)) {
      Serial.println("openRoot failed");
      return false;
    }
  }

  root.read(buf, 32);
  dir = reinterpret_cast<DirLabel_t*>(buf);
  Serial.print(F("Volume Name: "));
  for (size_t i = 0; i < dir->labelLength; i++) {
    Serial.write(dir->unicode[2 * i]);
  }
  Serial.println();
  expartVol.ls(LS_SIZE | LS_DATE | LS_R);
  return true;
}

// Get Fat32 volume name.
bool getFat32VolumeLabel(uint8_t  drvType, uint8_t part) {
  FatVolume partVol;

  uint8_t buf[512];

  partVol.begin(msc1.usbDrive(), true, part);
  partVol.chvol();
  if (drvType == msDrive) {
    msc1.usbDrive()->readSector(partVol.dataStartSector(), buf);
  }

  if (drvType == sdDrive) {
    sd.card()->readSector(sd.dataStartSector(), buf);
  }
  Serial.print(F("Volume Name: "));
  for (size_t i = 0; i < 11; i++) {
    Serial.write(buf[i]);
  }
  Serial.println();
  partVol.ls(LS_SIZE | LS_DATE | LS_R);

  return true;
}

// Get Fat16 volume name.
bool getFat16VolumeLabel(uint8_t  drvType, uint8_t part) {
  FatVolume partVol;
  MbrSector_t mbr;

  uint8_t buf[512];

  partVol.begin(msc1.usbDrive(), true, part);
  partVol.chvol();
  if (drvType == msDrive) {
    msc1.usbDrive()->readSector(partVol.rootDirStart(), buf);
//    print_hexbytes(buf, 512);
  }

  if (drvType == sdDrive) {
    sd.card()->readSector(sd.dataStartSector(), buf);
    print_hexbytes(buf, 512);
  }
  Serial.print(F("Volume Name: "));
  for (size_t i = 0; i < 11; i++) {
    Serial.write(buf[i]);
  }
  Serial.println();
  partVol.ls(LS_SIZE | LS_DATE | LS_R);

  return true;
}



bool mbrDmp() {
  MbrSector_t mbr;
  bool valid = true;
  if (!msc1.usbDrive()->readSector(0, (uint8_t*)&mbr)) {
    Serial.print("\nread MBR failed.\n");
    //errorPrint();
    return false;
  }
  Serial.print("\nmsc1 Partition Table\n");
  Serial.print("part,boot,bgnCHS[3],type,endCHS[3],start,length\n");
  for (uint8_t ip = 1; ip < 5; ip++) {
    MbrPart_t *pt = &mbr.part[ip - 1];
    //    if ((pt->boot != 0 && pt->boot != 0X80) ||
    //        getLe32(pt->relativeSectors) > sdCardCapacity(&m_csd)) {
    //      valid = false;
    //    }
    switch (pt->type) {
      case 4:
      case 6:
      case 0xe:
        Serial.print("FAT16: ");
        break;
      case 11:
      case 12:
        Serial.print("FAT32: ");
        break;
      case 7:
        Serial.print("exFAT: ");
        break;
    }
    partitionTable[ip - 1] = pt->type;
    dataStart[ip - 1] = getLe32(pt->relativeSectors);
    Serial.print( int(ip)); Serial.print( ',');
    Serial.print(int(pt->boot), HEX); Serial.print( ',');
    for (int i = 0; i < 3; i++ ) {
      Serial.print("0x"); Serial.print(int(pt->beginCHS[i]), HEX); Serial.print( ',');
    }
    Serial.print("0x"); Serial.print(int(pt->type), HEX); Serial.print( ',');
    for (int i = 0; i < 3; i++ ) {
      Serial.print("0x"); Serial.print(int(pt->endCHS[i]), HEX); Serial.print( ',');
    }
    Serial.print(getLe32(pt->relativeSectors), DEC); Serial.print(',');
    Serial.println(getLe32(pt->totalSectors));
  }
  return true;
}

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    SysCall::yield(); // wait for serial port to connect.
  }

  // Start USBHost_t36, HUB(s) and USB devices.
  myusb.begin();


}

void loop(void) {
  //--------------------------------------------------
  myusb.Task();
  if (!msDrive1) {
    Serial.println("Waiting up to 5 seconds for USB drive");
    elapsedMillis em = 0;
    while (!msDrive1 && (em < 5000) )  myusb.Task();
  }
  if (msDrive1) {
    Serial.printf("Initialize USB drive...");

    if (!msc1.begin(&msDrive1)) {
      msc1.errorPrint(&Serial);
      Serial.println("initialization failed.\n");
    } else {
      Serial.println("USB drive 1 is present.\n");
    }

    mbrDmp();
    /*
      if(msc1.fatType() == 32) {
        Serial.printf("Fat Type: Fat32\n");
        if(!getFat32VolumeLabel(msDrive))
          Serial.printf("Failed to get volume label\n");
      } else {
        Serial.printf("Fat Type: ExFat\n");
      if(!getExFatVolumeLabel(msDrive,1))
        Serial.printf("Failed to get volume label\n");
      }
      msc1.ls(LS_SIZE | LS_DATE | LS_R);
    */

    for (uint8_t i = 1; i < 5; i++) {
      switch (partitionTable[i - 1]) {
        case 11:
        case 12:
          Serial.printf("\nFat Type: Fat32\n");
          if (!getFat32VolumeLabel(msDrive, i))
            Serial.printf("Failed to get volume label\n");
          break;
        case 4:
        case 6:
        case 0xe:
          Serial.printf("\nFat Type: Fat16\n");
          if (!getFat16VolumeLabel(msDrive, i))
            Serial.printf("Failed to get volume label\n");
          break;
        case 7:
          Serial.printf("\nFat Type: ExFat\n");
          if (!getExFatVolumeLabel(msDrive, i))
            Serial.printf("Failed to get volume label\n");
          break;
        default:
          Serial.println("No or Not Supported Partition");
      }
    }
  }
  //--------------------------------------------------
  Serial.printf("\nInitialize SD card...");

  if (!sd.begin(SD_CONFIG)) {
    Serial.println("initialization failed.\n");
  } else {
    Serial.println("SD card is present.\n");
  }

  if (sd.fatType() == 32) {
    Serial.printf("Fat Type: Fat32\n");
    if (!getFat32VolumeLabel(sdDrive, 1))
      Serial.printf("Failed to get volume label\n");
  } else {
    Serial.printf("Fat Type: ExFAT\n");
    if (!getExFatVolumeLabel(sdDrive, 1)) {
      Serial.printf("Failed to get volume label\n");
    }
  }
  //sd.ls(LS_SIZE | LS_DATE | LS_R);

  Serial.println("done...");

  Serial.println("Press any key to run again");
  while (Serial.read() == -1);
  while (Serial.read() != -1);
}

void print_hexbytes(const void *ptr, int len)
{
  if (ptr == NULL || len <= 0) return;
  const uint8_t *p = (const uint8_t *)ptr;
  while (len) {
    for (uint8_t i = 0; i < 32; i++) {
      if (i > len) break;
      Serial.printf("%02X ", p[i]);
    }
    Serial.print(":");
    for (uint8_t i = 0; i < 32; i++) {
      if (i > len) break;
      Serial.printf("%c", ((p[i] >= ' ') && (p[i] <= '~')) ? p[i] : '.');
    }
    Serial.println();
    p += 32;
    len -= 32;
  }
}
[/CODE]

Thank you very much MJS
 
Hello,
was wondering, what is the current status of USBHost_t36 vs. reading mass storage devices?
In particular, I would like to read a "raw" SD card inserted into a USB card reader (which I would connect to the USB Host port of the T3.6).
"Raw" as in "no filesystem": would read it the raw sectors (long story).

MSC is now part of Teensyduino as of TD1.54B6. It sounds like you are wanting to do direct sector reads. To do this you can use the 'msReadBlocks()' function.

Here is an example sketch that does a direct sector read of sector 0 an does a hexdump of it.

Code:
#include "Arduino.h"
#include "USBHost_t36.h"

// Setup USBHost_t36 and as many HUB ports as needed.
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
USBHub hub4(myusb);

// Setup MSC for the number of USB Drives you are using. (Two for this example)
// Mutiple  USB drives can be used. Hot plugging is supported. There is a slight
// delay after a USB MSC device is plugged in. This is waiting for initialization
// but after it is initialized ther should be no delay.
msController usbSDCard(myusb);
//msController msDrive2(myusb);


// A small hex dump function
void hexDump(const void *ptr, uint32_t len)
{
  uint32_t  i = 0, j = 0;
  uint8_t   c = 0;
  const uint8_t *p = (const uint8_t *)ptr;

  Serial.printf("BYTE      00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n");
  Serial.printf("---------------------------------------------------------\n");
  for(i = 0; i <= (len-1); i+=16) {
   Serial.printf("%4.4x      ",i);
   for(j = 0; j < 16; j++) {
      c = p[i+j];
      Serial.printf("%2.2x ",c);
    }
    Serial.printf("  ");
    for(j = 0; j < 16; j++) {
      c = p[i+j];
      if(c > 31 && c < 127)
        Serial.printf("%c",c);
      else
        Serial.printf(".");
    }
    Serial.printf("\n");
  }

}

static uint8_t mscError = 0;
uint8_t rdBuf[512];

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
  }

  // Start USBHost_t36, HUB(s) and USB devices.
  myusb.begin();

  Serial.printf("Initialize USB drive...\n");
  if(mscError = usbSDCard.mscInit())
	Serial.printf(F("usbSDCard not connected: Code: %d\n\n"),  mscError);
  else
	Serial.printf(F("usbSDCard connected\n"));

  // Read sector 0 of SD card.
  usbSDCard.msReadBlocks(0, 1, 512, rdBuf);

  // Hex dump rdBuf[].
  hexDump(rdBuf, 512);
  
  Serial.println("done...");

}

void loop(void) {
}

I tested it with a USB SD card reader I have. One thing you may have to change is in 'USBHost_t36/utility/msc.h'. The timeout for waiting for a USB drive to become ready is to small. It is set at 1000ms and should be at least 3000ms. It is around line number 75. I was getting an timeout error with one of my SD cards so I bumped the timeout to 3000ms which took care of the problem.

I need to do a PR on it.
Hopefully this is what you are looking for:)
 
Last edited:
By the way: does it work with Teensy 4.1 as well? Maybe a silly question, but... just to be sure. :)
 
Hello, is it possible to get a T4.1 to enter mass storage device mode on the USB client port? I'm running it as a Serial + MIDI + Audio port, so I suspect it would have to reboot in some way to enter this mode and reboot to get out of it. Thanks.
 
... sorry, to make this clear - I want to access the SD card on the T4.1 as a mass storage device.
 
... sorry, to make this clear - I want to access the SD card on the T4.1 as a mass storage device.

AFAIK, this thread addresses the use of Teensy as a host to a mass storage device, and not the teensy or its microSD card as a mass storage device.
I have not seen that someone implemented a mass storage device driver on Teensy 3.x or 4.x.
We have MTP implemented, but that is not what you asked for.
 
Since I played with this and setting type 'USB MTP...' on my T3.6 now whenever I plug the T3.6 into my mac the photo application opens. Not sure how to break the association on the mac. If I change the type to 'No USB' the photo app no longer opens but the serial is gone. Returning type to 'serial' makes the photos open again.

None of many other Teensy's have this problem. What can I change on this T3.6 to make it good again. This is with 1.54 beta 7 with any program.
 
Not sure of the ways of on mac? How it associates and can change that ...
> Plug it in holding button, perhaps on a different USB port - then upload the Serial only sketch?
> Do that with a second T_3.6 (if there is one at hand) and do the above?
> Program it with Dual Serial for something else different?

> Maybe do a 15s Restore? Press program Button a timed 15 seconds, then release and wait for Blink to start.
 
thanks defragster
Tried messing with two 3.6s. Then both have the issue!
then
For some reason loading the example 'AnalogReadSerial.ino' and switching between type serial and MTP serial and trying Teensy ports and regular port with two T3.6 attached one of them finally showed up as a device in the photo app! Toggled a 'load photos checkbox' that appeared in the app on then off and closed photos. That seemed to return everything back to normal... computers.... cheezz
 
Hi, I am trying to copy a file from the SD card of the Teensy to a Mass storage device connected to the USB Host port of the Teensy. Haven't got any luck. I am thinking of using the MSC2, but getting compilation error, while compiling the example:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36/USBHost_t36.h:2045:2: error: 'msSCSICapacity_t' does not name a type

msSCSICapacity_t msCapacity;

^

Any help will be highly appreciated.
 
Hi, I am trying to copy a file from the SD card of the Teensy to a Mass storage device connected to the USB Host port of the Teensy. Haven't got any luck. I am thinking of using the MSC2, but getting compilation error, while compiling the example:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36/USBHost_t36.h:2045:2: error: 'msSCSICapacity_t' does not name a type

msSCSICapacity_t msCapacity;

^

Any help will be highly appreciated.

I am sorry that I have been so slow at updating the MSC library. MSC is pretty much outdated and not functional with the latest versions of Teensyduino starting with version 1.53 and up. The MSC driver is now included with Teensyduino starting with version 1.53 and up. In an effort to standardize SD which was the original file system for Teensy and Arduino and create a level of abstraction for the various mass storage devices being used with the Teensy Paul at PJRC created FS.h which allowed using SdFat and LittleFS with the SD API. Both SD and SdFat API's can be mixed. I took that and created a library that is compatible with MSC, SD and SdFat.

Here:

https://github.com/wwatson4506/UsbMscFat

And @KurtE and @mjs513 developed it further adding a lot more functionality. The latest being:

https://github.com/wwatson4506/UsbMscFat/tree/UsbMscFat-FS_DATES

This added the time an date functionality. Some of the added ability is using Fat32 and EXFat partitions, partition labels, creating and formatting both Fat32 and EXFat partitions.

Check out the examples in the UsbMscFat library. It has a lot of examples in both versions of the library including copying files between USB thumb drives and SD cards.
I noticed that I do not have an example of copying a file from SD to MSC using non-SdFat methods and using file access abstraction. TODO:)

Actually will do:)
 
Back
Top