Teensyduino File System Integration, including MTP and MSC

@All involved with MTP_Teensy development - Great work:D I had a little time tonight to play with it (UBUNTU). So far everything seems to work flawlessly. Cannot wait to play with it more tomorrow:)
 
MTP Status Update

I've just installed Teensyduino 1.56 (and Arduino 1.8.19) on a newly-purchased M1 Mac Mini. I would like to try out some of my programs that use MTP. I've been using a version of MTP that I customized and put into a local library (on a WIN 10 machine) more than a year ago. I see that MTP + SERIAL is not yet a part of usbdesc.h, so I will either have to edit the T4.1 cores and boards.txt or wait until MTP + SERIAL gets integrated into TeensyDuino. (Just finding the core files in the MAC Teensyduino application package took a bit of exploration.)

Since I've been using my local mod of the MTP library, I haven't kept up with this topic very well.

What is the probability that MTP will be more fully developed and added to TeensyDuino in the next few months?

If MTP won't be fully integrated in the near future, where do I find the current work in progress?

Does the current WIP make it easy to use just MTP + SERIAL? (I have no use for MSC or LittleFS at this time).
 
Current WIP MTP is way better than ever - needs some details attended to for full usability before release.

Getting the WIP MTP takes some code pulled from github, until a beta gets released ... that may be pending in coming month or so, and hopefully include MTP which does have an extended KurtE variation for MTP+Serial.

How what and when MTP gets released will take some effort and decisions from PJRC ...
 
Hi,
this MTP-Thing is looking like a dream!
Just a thought, if not yet implemented: Could you implement something like MtpServer.activate() and MtpServer.sleep()?
The idea is to have a simple means to prevent this server from blocking Teensy and SD-card for tight timing jobs.
What a nightmare, if Windows decides to do a virus scan or just a directory update or whatever....
Christof
 
Hi,
this MTP-Thing is looking like a dream!
Just a thought, if not yet implemented: Could you implement something like MtpServer.activate() and MtpServer.sleep()?
The idea is to have a simple means to prevent this server from blocking Teensy and SD-card for tight timing jobs.
What a nightmare, if Windows decides to do a virus scan or just a directory update or whatever....
Christof

It is dreamy for sure! Though timing and attention to the HOST is time critical so it doesn't bail, and when it starts something with the Teensy, it can take a lot of cycles for the 'duration' of that transfer or lookup.

So, there is a balancing act needed - and that is a task at hand to be resolved.

Now it seems on connect a Windows Host demands attention for the connect on startup to get the device mounted because the USB device presents as MTP capable.

Not sure there is a way to disconnect/Sleep() and restart/Activate() without a Teensy Restart - but that code isn't something I looked at WRT code/function - just observed in use.
 
What a nightmare, if Windows decides to do a virus scan or just a directory update or whatever....
Except for the MSC part, for the MTP part, there is NO way Windows can decide to do a virus scan. The protocol does not foresee this.
of course, once copied file to PC, PC indeed can and will (should?) run a virus-check.
But this does not influence Tensy
 
Not sure there is a way to disconnect/Sleep() and restart/Activate() without a Teensy Restart - but that code isn't something I looked at WRT code/function - just observed in use.
You always can unmount/mount teensy in unix and deinstall/install Teensy portable device with the device manager (it does not touched the com line)
The first event implemented was indeed a programmatic mtp-reset (without touching the device manager)
 
I solved the problem of forcing the PC to notice changes in the MTP disk by using usb_init() to stop and restart the USB link. This has the disadvantage of breaking the USB Serial comms link, which means that the PC Host program has to reopen the serial link.

Code:
// Restarts the USB link to PC.  The PC notices this and has the Teensy
// rebuild the file data base so that new entries get displayed
#define USE_EVENTS 0
void CMRM(void *cmdline) {
#if USE_EVENTS==1
  Serial.println("MTP Reset");
  mtpd.send_DeviceResetEvent();  // my older MTP code doesn't support events
#else
  Serial.println("Reconnect serial port or restart Serial Monitor after USB reset.");
  delay(100);
  usb_init();  // shuts down USB if already started, then restarts
  delay(200);
  Serial.begin(9600);
  delay(200);

  Serial.println("USB disconnected and reconnected to force MTP update");
#endif
}
 
I solved the problem of forcing the PC to notice changes in the MTP disk by using usb_init() to stop and restart the USB link. This has the disadvantage of breaking the USB Serial comms link, which means that the PC Host program has to reopen the serial link.

...

Thanks, I'll add that option for 'usb_init()' to MakeFiles.ino options menu. I saw something in one of the other test sketches - but wasn't sure I saw it work - and it seemed to be some other mechanism.
 
I will look at some of this stuff again soon, but a lot of what may be in will depend on some possible fundamental architectural changes to the code, so sort of put things on hold for now.
 
One issue I've noted with Teensy SD Cards having lots of files that the MTP index is constructed as a file on the SD card in my older MTP code. That is probably a lot slower than building the index in EXTMEM on a T4.1 with PSRAM. Does the WIP code allow the option to build the index in EXTMEM? It would be nice to have a startup option where you passed a buffer pointer and size and the MTP code would use the buffer for the index. If the index exceeded the buffer size, the MTP code could fall back to building the index on the SD card.

Hmmm, perhaps I've just found a good reason to learn about LittleFS. I may try to modify my own code to use LittleFS in PSRAM for the index.

I'm also a bit concerned about the LittleFS startup using EXTMEM malloc. I've been doing a lot of work with cameras where I use image buffers for the CSI in EXTMEM. In several applications, I use a queue of 6 VGA size image buffers so that I have 5 frame intervals to work with an image before it is overwritten by the CSI. If I use LittleFS in EXTMEM, I have to worry about collisions between the camera buffers and LittleFS. My first thought is to modify LittleFS to use and input buffer of specified size, as noted above, instead of the malloc.
 
Yes and No...

That is there is no direct way.... although I have thought about it.

However: You can create a ram
Code:
    LittleFS_RAM lfsRam;

   ... 
   lfsRam.begin(4000000l);
   ...
   uint32_t index = MTP.AddFilesystem(lfsRam, "RAM");
   MTP.useFileSystemIndexFileStore(index);
...

The last line sets which store to use to store the index file in... It defaults to the first FS you add...

All subject to change.
 
@mborgerson,
with LittleFS is should be no issue, simply register RAM based LittleFS as first disk.

Now, the originator of the MTP responder assumed that RAM is very precious and limited, so he put the file onto the disk.

Unfortunately my own applications wanted the EXTMEM as data buffer, so I never ported the index file to RAM.
Also, one has to decide, what to do when index file runs out of memory. (one of my applications has up to 150000 files, so index file goes into a lot of MB if one allows 256byte long filenames, i.e. totalling 256+16 bytes / record)
 
Quick notes:

While having too much fun with the picture viewer, where a few of us, mainly @mjs513 and myself, hacking in support for a lot of different display drivers, plus support for jpeg and gif and the like, I sort of figured that maybe it
was getting too complex of a Sample program for the MTP code base, so I moved the current version to it's own github project: https://github.com/KurtE/mtp_tft_picture_view

I am now in the process of stripping most everything else out of the version in the mtp_teensy (fast start) branch except support for ILI9341_t3 library and bitmap support.

There is probably more we could strip out, as I did leave in some of the more recent stuff we have been experimenting with, like @mjs513 code to scale images down to fit.
And now some code I am currently playing with to allow you to scale up an image... Currently using a simple linear interpolation to fill in the extra rows and columns.

Scale up on RA8875 (new github project):
screenshot.jpg

Scale down on ILI9341 display:
screenshot2.jpg
 
Some miscellaneous thoughts:

1. It seems that running MTP as a thread or pseudo-thread, might solve a lot of the issues with the host PC timing out. The pseudo-thread could have the MTP Loop function called from an interval timer set up by mtp.begin(). To make sure that the loop function will not absorb too much CPU time it might need to be rewritten to save status and exit when waiting on asynchronous USB or file write operations.

2. For the picture viewer, scaling is something that can be done in the background by the PXP once you have a bitmap in memory. Scaling a VGA-size image to QVGA for the ili9341 takes about 25mSec on a T4.1 at 600mHz. The PXP can also handle the conversion from a YUV bitmap to RGB565 for an iLI9341. For a recent camera project, I used the 1LI9341 as a viewfinder. I was able to make updating the viewfinder a totally background process by starting with the CSI end-of-frame interrupt to begin the scaling in the PXP, then using the PXP_done interrupt to start an asynchronous update of the ILI9341 display. The main problem was figuring out how to keep the CSI and PXP from messing up the image when they were both trying to access EXTMEM. I simply shut down the CSI while the PXP was running---which cut my frame rate from 30FPS to 15.
 
Thanks for the thoughts/suggestions.
Some miscellaneous thoughts:

1. It seems that running MTP as a thread or pseudo-thread, might solve a lot of the issues with the host PC timing out. The pseudo-thread could have the MTP Loop function called from an interval timer set up by mtp.begin(). To make sure that the loop function will not absorb too much CPU time it might need to be rewritten to save status and exit when waiting on asynchronous USB or file write operations.
As you mentioned, a lot of this might be made a lot easier if we were using some form of threads or tasks. So far I have not gone that way with any of the code here as so far there is no standard agreed upon way to do this yet on the Teensy.

The code does use an intervalTimer at startup as to try to handle several of the initial messages quick enough at startup to keep the host from giving up on us. It is not eloquent, but gets that job done. Will probably need to expand on it.
Alternatively many of these messages could probably be handled easily off of the callback with the new message, but some are way to time consuming.

Will see which way Paul would like to go with some of this.

2. For the picture viewer, scaling is something that can be done in the background by the PXP once you have a bitmap in memory. Scaling a VGA-size image to QVGA for the ili9341 takes about 25mSec on a T4.1 at 600mHz. The PXP can also handle the conversion from a YUV bitmap to RGB565 for an iLI9341. For a recent camera project, I used the 1LI9341 as a viewfinder. I was able to make updating the viewfinder a totally background process by starting with the CSI end-of-frame interrupt to begin the scaling in the PXP, then using the PXP_done interrupt to start an asynchronous update of the ILI9341 display. The main problem was figuring out how to keep the CSI and PXP from messing up the image when they were both trying to access EXTMEM. I simply shut down the CSI while the PXP was running---which cut my frame rate from 30FPS to 15.

The picture viewer code we left in here is pretty basic. More of just an example of something else one might do with MTP that is not another data logger.

The scale up/down code is pretty basic, although the scale down does use the sum of the squares of the cells to be replaced. The scale up code is doing simple linear interpolation between the cells.
 
@wwatson @mjs513 @Paul... I was curious about from other thread https://forum.pjrc.com/threads/6982...-serial-device-Keybow2040?p=302928#post302928

About plugging in a Circuit Python board into the USB Host port, if we would be able to read the logical drive on the circuit python board.

So to start with a programmed a T4.1 with latest micro python release.
I verified it worked by updating the code on the drive to the blink example....

Then I went to our mtp_teensy project and the USB_MTP_Logger sketch. Note I am still building with an oddball setup:
where USBHost_t36 using master instead of our current one of this thread
Using the UsbMsc decouple branch - where I am running without the MSC support withing usbhost and instead library. Also removed using the msc object within usbhost...

I then built the sketch on MicroMod... When I ran it with simple thumb drive I see in debug information...
Code:
@@@@@@@@@@@@@@@ NEW Drives @@@@@@@@@@@
	 ## new drive

USB Drive: 0 connected

msc # Partition Table
	part,boot,bgnCHS[3],type,endCHS[3],start,length
FAT32:	1,0,0x82,0x3,0x0,0xB,0xFE,0xFF,0xD2,8192,15719443
pt_#0:	2,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	3,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	4,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0

Try Partition list

PART	Type	Start	Count	(MBR	Part)	Volume Type
1	M	8192	15719443	0	0	Fat32:

@@@@@@@@@@@@@@@ NEW Drives  Completed. @@@@@@@@@@@
Found new Volume:0
*MTP_class::send_Event(4004) 30001
*MTP_class::send_Event(4004) 30001
And the volume shows up...


But if instead I plug in the T41 with Circuit Python I see:
Code:
3633 RESP:2001(RSP:OK)l: 12 T:7

@@@@@@@@@@@@@@@ NEW Drives @@@@@@@@@@@
	 ## new drive

USB Drive: 0 connected

msc # Partition Table
	part,boot,bgnCHS[3],type,endCHS[3],start,length
pt_#1:	1,0,0xFF,0xFF,0xFF,0x1,0xFF,0xFF,0xFF,1,14328
pt_#0:	2,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	3,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	4,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0

Try Partition list

PART	Type	Start	Count	(MBR	Part)	Volume Type
1	M	1	14328	0	0	Fat16:

@@@@@@@@@@@@@@@ NEW Drives  Completed. @@@@@@@@@@@
And no new volume added to MTP...

Now time to debug some
 
Quick follow on to previous post.
pt_#1: 1,0,0xFF,0xFF,0xFF,0x1,0xFF,0xFF,0xFF,1,14328

Partition type 1 is Fat12, which we don't currently support.

Tried and enabled it in SDFatConfig

Code:
//------------------------------------------------------------------------------
/**
 * Set FAT12_SUPPORT nonzero to enable use if FAT12 volumes.
 * FAT12 has not been well tested and requires additional flash.
 */
#ifndef FAT12_SUPPORT
#define FAT12_SUPPORT 1
#endif  // FAT12_SUPPORT

Still does not work...

Note: This is also true with an Adafruit Feather RP2040 board, also setup for Circuit python... so does not show up either...
 
Last edited:
2nd follow up...

in the function bool FatPartition::init(BlockDevice* dev, uint8_t part) {
Code:
  if (!pbs /*|| bpb->fatCount != 2 */|| getLe16(bpb->bytesPerSector) != 512) {
    DBG_FAIL_MACRO;
    goto fail;
  }
I needed to comment out the fatcount test as these only have 1...
So the drive shows up in the mtp browser but not seeing any files...

edit: more progress changed hard coded 2 to bbp->fatCount

Code:
  // directory start for FAT16 dataStart for FAT32
  m_rootDirStart = m_fatStartSector + bpb->fatCount * m_sectorsPerFat;

screenshot.jpg
 
Last edited:
@KurtE - After downloading the latest version of MTP_Teensy-fast_start, UsbMscFat-decouple and USBHost_t36-main from TD1.56 I used one my MicroMod ATP and a T4.1 to duplicate what you did in P#1147. The results were the same:
Screenshot at 2022-03-24 17-44-00.png

This is really interesting. I have never really played with Circuitpython. I have been working with my file manager that is based on the Buy Display 10.1" RA8876 TFT. Particularly allocating and reallocating memory for directory listings when changing directories or storage devices. I have it working but think it is not optimal at all. I remember a post that I cannot find now, you were talking about the same thing not having a heavy stack impact.

I needed a break from it as I have been working on it for a couple of years now. This is a nice distraction:)

EDIT: Just for the fun of it I plugged in a USB HUB to the MM ATP and then plugged in the T4.1 and a thumb drive.
Remounted MTP and result is this:
Screenshot at 2022-03-24 19-23-27.jpg
NICE...

Additional: Hot plugged a second thumb drive with two partitions into the HUB:
Screenshot at 2022-03-24 19-43-38.jpg

Very Nice.... OK I'm done:)
 
Last edited:
Quick follow up:

I pushed up test sketch, which is sort of copy of the usb_.. test sketch minus the logging, minus bogus fs
And then added in several other USB Host objects like HIDS, keyboard, mouse, joystick, serial, seremu...
in the project/branch https://github.com/KurtE/MTP_Teensy/tree/fast_start

It looks like beyond the drive it has mouse and keyboard:
Code:
*** end Interval Timer ***
loop:3634 CMD: 1005(GET_STORAGE_INFO)l: 16 T:6 : 10001
65537 0 name:RAM
3634 RESP:2001(RSP:OK)l: 12 T:6
*** Device HID1 239a:80ae - connected ***
  manufacturer: PJRC
  product: Teensy 4.1
  Serial: 2F057F67D28111299000005002004200
*** Device KB1 239a:80ae - connected ***
  manufacturer: PJRC
  product: Teensy 4.1
  Serial: 2F057F67D28111299000005002004200
*** HID Device Mouse1 239a:80ae - connected ***
  manufacturer: PJRC
  product: Teensy 4.1
  Serial: 2F057F67D28111299000005002004200
PFsVolume::begin(2000d83c, 1)
I have not connected any code to these yet...

I pushed in some SDFat changes into the branch: https://github.com/KurtE/SdFat/tree/FAT12
This turned on the option, plus handles 1 FAT table instead of requiring 2...

Next up I may try to get Fat12 code to work better. Note some code in the MSC plus in SDFat needs to be updated as they if # clusters
Code:
  // FAT type is determined by cluster count
  if (clusterCount < 4085) {
    m_fatType = 12;
    if (!FAT12_SUPPORT) {
      DBG_FAIL_MACRO;
      goto fail;
    }


But the Fat12 created on T4.1 and likewise on Adafruit feather RP2040 exceed that number:
Code:
msc # Partition Table
	part,boot,bgnCHS[3],type,endCHS[3],start,length
FAT12:	1,0,0xFF,0xFF,0xFF,0x1,0xFF,0xFF,0xFF,1,14328
pt_#0:	2,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	3,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	4,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0

Try Partition list

PART	Type	Start	Count	(MBR	Part)	Volume Type
1	M	1	14328	0	0
 bytesPerSector:512
 sectorsPerCluster:2
 reservedSectorCount:1
 fatCount:1
 rootDirEntryCount:512
 totalSectors16:14328
 mediaType:248
 sectorsPerFat16:28
 sectorsPerTrtack:63
 headCount:255
 hidddenSectors:1
 totalSectors32:0
 sectorsPerFat32:2687104
 fat32Flags:8448
 fat32Version:20204
 fat32RootCluster:1095639119
 fat32FSInfoSector:17741
 fat32BackBootSector:8224
 physicalDriveNumber:0
 extSignature:0
 volumeSerialNumber:0
	Before sectorsPerCluster
tot sec:14328 shift:1 cluster count: [B]7133	[/B]Fat16:

So need to change the partition checking code to probably return the drive type if MBR drive.
Note: creating plugging in Circuit Python on T4, does have < count of clusters and shows up as Fat12:
Code:
tot sec:2040 shift:0 cluster count: 2001	Fat12:

But looks like then some methods within fat or our code need help:
screenshot.jpg

But does show file:
screenshot2.jpg


And then work
 
Quick update:

I updated the code that walked through MBR/Extended/GPT partitions and in case of MBR I optionally return the FS type of the mbr. I use this to detect == 1 as Fat12
As the code that if < N clusters was not correct in these cases.

Updated it both in my SDFat/Fat12 fork/branch as well as my usbmscFat/decouple fork/branch.

Also added code in the usbmsc... To try to walk the fat12 fat to get free cluster count... Could still need tweaks, but at least closer.
that is in the fat every 3 bytes is 2 clusters info So in 512 byte sector 3 bytes does not divide ... so do the cluster info span sectors or waste a couple of bytes. I am guessing waste a few byes...

screenshot.jpg

Code:
Menu Options:
	1 - List USB Drives (Step 1)
	2 - Select USB Drive
	l - List files on disk
	e - Erase files on disk
	r - reset MTP
	h - Menu

*** end Interval Timer ***
loop:3660 CMD: 1005(GET_STORAGE_INFO)l: 16 T:6 : 10001
65537 0 name:RAM
3660 RESP:2001(RSP:OK)l: 12 T:6
*** Device HID1 239a:80f2 - connected ***
  manufacturer: Adafruit
  product: Feather RP2040
  Serial: 463638383513980C
*** Device KB1 239a:80f2 - connected ***
  manufacturer: Adafruit
  product: Feather RP2040
  Serial: 463638383513980C

@@@@@@@@@@@@@@@ NEW Drives @@@@@@@@@@@
	 ## new drive

USB Drive: 0 connected

msc # Partition Table
	part,boot,bgnCHS[3],type,endCHS[3],start,length
FAT12:	1,0,0xFF,0xFF,0xFF,0x1,0xFF,0xFF,0xFF,1,14336
pt_#0:	2,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	3,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0
pt_#0:	4,0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0,0

Try Partition list

PART	Type	Start	Count	(MBR	Part	Type)	Volume Type
1	M	1	14336	0	0	1	Fat12:

@@@@@@@@@@@@@@@ NEW Drives  Completed. @@@@@@@@@@@
Found new Volume:0[CODE]
*MTP_class::send_Event(4004) 20001
*MTP_class::send_Event(4004) 20001
*** HID Device Mouse1 239a:80f2 - connected ***
manufacturer: Adafruit
product: Feather RP2040
Serial: 463638383513980C[/CODE]

New update:
Uploaded newer version of sketch, that checks for keyboard data from circuit python, did not see any yet, also double checked and sure enough it does create a serial object,

I did quick and dirty in loop see if anything there...
Code:
$$USerial:4608
$$USerial:4640
$$USerial:4672
$$USerial:4704
$$USerial:4736
$$USerial:4768
$$USerial:4800
$$USerial:4832
$$USerial:4864
So it is getting simple output for loop count where I only output every 32...
 
Last edited:
Back
Top