USB Host to read/write data on USB-Stick?

schlank

Active member
Hello!

Is it possible to read/write CSV data on a MSD using the USB-host functionality of the Teensy 4.0 ?

Technically this should work but are there any examples for Arduino? Maybe with the USBHost_t36 libary?

Thanks for your help,

schlank
 
Hello!

Is it possible to read/write CSV data on a MSD using the USB-host functionality of the Teensy 4.0 ?

Technically this should work but are there any examples for Arduino? Maybe with the USBHost_t36 libary?

Thanks for your help,

schlank

Yes it is. With the Teensy 4.0 you will need a breakout board to access USB Host functionality. There are two pads underneath the USB device connector on the bottom side of the T4.0 that provide the D+ and D- signals used for USB Host. If I am correct this requires a little support circuitry as well.
teensy40_card10b_rev2.png

The T4.1 already has access to USB Host through a 5 pin connector onboard.
teensy41_4.png

And PJRC sells a matching cable for use with it.

There are examples in the USBHost_t36 library in the "examples/storage" folder that you can use to get an idea of how to use a USB thumb drive with the T4.x.

Here is an example of MSC usage that shows how to read and write to a file on a USB drive:
Code:
/*
   MSC SdFat usage from SD library

  Starting with Teensyduino 1.54, the SD library is a thin wrapper for SdFat.

  You can access the main SdFat filesystem with ".mscfs".
  This example shows some of the ways to select optimized SdFat drivers and
  how to use special file truncation and pre-allocation for optimized data
  logging.

  This example code is in the public domain.
*/
#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.
// mscController is now USBDrive. 
USBDrive msDrive1(myusb);

// USBFilesystem is a class based on claimed partitions discovered during
// initialization. We are using the first discovered partition here. More
// discovered partitions can be used by adding more instances of the
// 'USBFilesystem' class.
USBFilesystem partition1(myusb);
elapsedMillis mscTimeOut; 

void setup()
{
  Serial.begin(9600);
  while (!Serial); // wait for Arduino Serial Monitor

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

  Serial.print("Initializing MSC drive...");
  uint8_t errCode = MS_INIT_PASS;
  mscTimeOut = 0;
  // Wait for MSC drive to be auto detected. Uses 5 second timeout
  // and no drive detected error. Waits for drive to be inserted.
  while(!msDrive1) {
    if((mscTimeOut > MSC_CONNECT_TIMEOUT) && (msDrive1.errorCode() == MS_NO_MEDIA_ERR)) {
      Serial.println("No drive connected yet!!!!");
      Serial.println("Connect a drive to continue...\n");
      while(!msDrive1);
    } 
	delay(1);
  }
  mscTimeOut = 0;

  while(!partition1) {
    myusb.Task();
    if(mscTimeOut >  MEDIA_READY_TIMEOUT) {
      Serial.println("initialization failed!");
      return;
    }
    delay(1);
  }
  Serial.println("initialization done.");
  Serial.println();

  // After the MSC drive is initialized, you can access it using the ordinary
  // SD library functions, regardless of whether it was initialized by
  // SD library SD.begin() or SdFat library SD.sdfs.begin() or MSC auto
  // initialization. MSC access to SdFat is achieved as in this example:
  // "partition1.mscfs.ls()".
  Serial.println("Print directory using SD functions");
  File root = partition1.open("/");
  while (true) {
    File entry = root.openNextFile();
    if (!entry) break; // no more files
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
    } else {
      printSpaces(40 - strlen(entry.name()));
      Serial.print("  ");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }

  // You can also access the SD card with SdFat's functions
  // 
  Serial.println();
  Serial.println("Print directory using SdFat ls() function");
  partition1.mscfs.ls(LS_R);

  // You can access files using SdFat which uses "FsFile" for open files
  // FsFile offers more capability than regular SD "File".  As shown in this
  // example, you can truncate tiles.  You can also pre-allocate a file on
  // the MSC drive (if it does not yet have any data, the reason we truncate
  // first).  Pre-allocation impoves the speed of writes within the already
  // allocated space while data logging or performing other small writes.
  //
  Serial.println();
  Serial.println("Writing to datalog.bin using SdFat functions");
  FsFile myfile = partition1.mscfs.open("datalog.bin", O_WRITE | O_CREAT);
  unsigned int len = myfile.fileSize();
  Serial.print("datalog.bin started with ");
  Serial.print(len);
  Serial.println(" bytes");
  if (len > 0) {
    // reduce the file to zero if it already had data
    myfile.truncate();
  }
  if (myfile.preAllocate(40*1024*1024)) {
    Serial.print("  Allocate 40 megabytes for datalog.bin");
  } else {
    Serial.print("  unable to preallocate this file");
  }
  myfile.print("Just some test data written to the file (by SdFat functions)");
  myfile.write((uint8_t)'\0'); // add a null byte to mark end of string
  myfile.close();

  // You can also use regular SD functions, even to access the same file.  Just
  // remember to close the SdFat FsFile before opening as a regular SD File.
  //
  Serial.println();
  Serial.println("Reading to datalog.bin using SD functions");
  File f = partition1.open("datalog.bin");
  if (f) {
    char mybuffer[100];
    int index = 0;
    while (f.available()) {
      char c = f.read();
      mybuffer[index] = c;
      if (c == 0) break;  // end of string
      index = index + 1;
      if (index == 99) break; // buffer full
    }
    mybuffer[index] = 0;
    Serial.print("  Read from file: ");
    Serial.println(mybuffer);
  } else {
    Serial.println("unable to open datalog.bin :(");
  }
  f.close();

  // When mixing SD and SdFat file access, remember for writing that
  // SD defaults to appending if you open with FILE_WRITE.  You must
  // use FILE_WRITE_BEGIN if you wish to overwrite the file from the
  // start.  With SdFat, O_WRITE or O_RDWR starts overwriting from the
  // beginning.  You must add O_AT_END if you wish to appead.
}

void loop()
{
  // nothing happens after setup finishes.
}


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

Hopefully this will give you a starting point...
 
Hi wwatson,

Thanks for your reply.
I just ordered the PCBs of this tiny board to have a solid connection to any USB device:

https://www.partsnotincluded.com/teensy-4-0-minimalist-usb-host-shield/

Unfortunately the code provided by you is not compiling without error in Platform I/O.


Code:
[env:teensy40]
platform = teensy
board = teensy40
framework = arduino
lib_deps = paulstoffregen/USBHost_t36@^0.2

Compiler output:

Code:
src/main.cpp:28:1: error: 'USBDrive' does not name a type
 USBDrive msDrive1(myusb);

I installed the library in Platform I/O library installer.

Any idea?


Thanks,

schlank
 
Using IDE 2.1 with TD 1.58.1 the post #2 code built here and created the indicated 40MB file with just one line.

Didn't seem to activate MSC connect to Host PC ... but it built fine with and without USB having MSC included.

One warning about unused errCode in setup()

It shows this for used libraries:
Code:
Using library USBHost_t36 at version 0.2 in folder: C:\Users\Tim\AppData\Local\Arduino15\packages\teensy\hardware\avr\1.58.1\libraries\USBHost_t36 
Using library SdFat at version 2.1.2 in folder: C:\Users\Tim\AppData\Local\Arduino15\packages\teensy\hardware\avr\1.58.1\libraries\SdFat 
Using library SPI at version 1.0 in folder: C:\Users\Tim\AppData\Local\Arduino15\packages\teensy\hardware\avr\1.58.1\libraries\SPI 
Using library EEPROM at version 2.0 in folder: C:\Users\Tim\AppData\Local\Arduino15\packages\teensy\hardware\avr\1.58.1\libraries\EEPROM

Not sure where the PIO is on released Teensy sources?

Update from 1.58 to 1.58.1 IIRC would not affect this.
 
Good luck:)

Yeah thanks :)

I managed the code to compile (not yet tested) after getting the library and teensy-platform directly from Git:

Code:
[env:teensy40]
platform = https://github.com/platformio/platform-teensy.git
board = teensy40
framework = arduino
lib_deps = 
	https://github.com/PaulStoffregen/USBHost_t36.git
 
Hello!

I am about to do some coding using the library.
To get the size of a file os not a big deal:
Code:
File configFile = partition1.open("/config/config.txt", O_READ);
..
Serial.println(configFile.size());

This works. But I want to use getCreateTime() to get the time of creation. How can I manage that?

Thanks,

schlank
 
Hello!

I am about to do some coding using the library.
To get the size of a file os not a big deal:
Code:
File configFile = partition1.open("/config/config.txt", O_READ);
..
Serial.println(configFile.size());

This works. But I want to use getCreateTime() to get the time of creation. How can I manage that?

Thanks,

schlank

Maybe something like:

Code:
    DateTimeFields dtf;
    configFile.getCreateTime(dtf);
    Serial.printf("Updated Options file found date: M: %02u/%02u/%04u %02u:%02u\n",
         dtf.mon + 1, dtf.mday, dtf.year + 1900, dtf.hour, dtf.min );
 
Hi Kurt,

Thanks for this. This works.

However could you maybe tell me HOW I can set now the internal RCT clock of the Teensy 4.0 to the time of the date read from the file?

This is important if the battery runs out out energy.

Thanks!
 
I have a sneaking suspicion there is some code in the Teensy Loader app (the one that uploads code onto the device) that sets the RTC based on the host PC's time - I've never had a battery connected but have noticed the Teensy's clock is often set correctly regardless...
 
I am an artist and I want to give the project to a museum. If the batter runs low they should be able to set the time again by them selves.
 
I have a sneaking suspicion there is some code in the Teensy Loader app (the one that uploads code onto the device) that sets the RTC based on the host PC's time - I've never had a battery connected but have noticed the Teensy's clock is often set correctly regardless...

Yes, when you program the Teensy using the Arduino IDE, it resets the Teensy real time clock.
 
Hello Friends!

I am using this snippet from the example:

Code:
      if ((mscTimeOut > MSC_CONNECT_TIMEOUT) &&
          (msDrive1.errorCode() == MS_NO_MEDIA_ERR)) {
          Serial.println("No drive connected!");
          return false;  // Return false if no drive is connected yet
      }
      delay(1);
  }

However is it possible to check for a attached or removed MSD in a non-blocking way? E.g interrupt based?

Thanks,

schlank.
 
Hello Friends!

I am using this snippet from the example:

Code:
      if ((mscTimeOut > MSC_CONNECT_TIMEOUT) &&
          (msDrive1.errorCode() == MS_NO_MEDIA_ERR)) {
          Serial.println("No drive connected!");
          return false;  // Return false if no drive is connected yet
      }
      delay(1);
  }

However is it possible to check for a attached or removed MSD in a non-blocking way? E.g interrupt based?

Thanks,

schlank.

Sorry for the delayed response. There is a way to check for that at the driver level. The claim() function and disconnect() function are interrupt driven.
Here is a small sketch that demonstrates this:
Code:
#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);

// Instances for the number of USB drives you are using.
USBDrive myDrive1(myusb);

void setup()
{
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for Arduino Serial Monitor to connect.
  }

  myusb.begin();
  delay(1000);  // Give USBHost a little time to startup
}

void loop() {
  myusb.Task();

  //===================================================================
  // myDrive1.msDriveInfo.connected is set or cleared by USBHost_t36
  // interrupts in the claim() and disconnect() functions. See 
  // MassStorageDriver.cpp at line 141 for claim() and line 175 in the 
  // disconnect() function.
  //===================================================================

  if(myDrive1.msDriveInfo.connected) 
    Serial.printf(F("       myDrive1 is connected...\n"));
  else
    Serial.printf(F("       myDrive1 is NOT connected...\n"));
  delay(10); // Slow it down a bit...
}
Run this sketch and plug/unplug a USB drive to demonstrate hot plugging.

Each instance of USBDrive has a struct called msDriveInfo so each USB drive that you connect will have this struct available:
Code:
// MSC Drive status/info struct
typedef struct {
	bool connected;    // Device is connected
	bool initialized;  // Device is initialized
	bool mounted;      // Device is mounted
	const char * drvName;
	uint32_t bufferSize;
	uint8_t hubNumber;
	uint8_t hubPort;
	uint8_t deviceAddress;
	uint16_t idVendor;
	uint16_t idProduct;
	msSCSICapacity_t capacity;
	msInquiryResponse_t inquiry;	
} __attribute__((packed)) msDriveInfo_t;

Check out the example sketch in "USBHost_t36/storage/DriveInfo". This prints out the drive information. Also checkout "USBHost_t36/storage/DriveInfoAll" sketch which prints out even more information about all connected USB drives.

Hope this is what you are after...
 
Back
Top