diff --git a/CMakeLists.txt b/CMakeLists.txt
index ab32598c5a0..2801ba615ef 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,6 +34,9 @@ set(CORE_SRCS
   cores/esp32/StreamString.cpp
   cores/esp32/USB.cpp
   cores/esp32/USBCDC.cpp
+  cores/esp32/USBMSC.cpp
+  cores/esp32/FirmwareMSC.cpp
+  cores/esp32/firmware_msc_fat.c
   cores/esp32/wiring_pulse.c
   cores/esp32/wiring_shift.c
   cores/esp32/WMath.cpp
@@ -151,6 +154,7 @@ set(includedirs
   libraries/SPI/src
   libraries/Ticker/src
   libraries/Update/src
+  libraries/USB/src
   libraries/WebServer/src
   libraries/WiFiClientSecure/src
   libraries/WiFi/src
diff --git a/boards.txt b/boards.txt
index e8a38a6b650..3f9d992c2e1 100644
--- a/boards.txt
+++ b/boards.txt
@@ -1,5 +1,7 @@
 menu.UploadSpeed=Upload Speed
-menu.SerialMode=Serial Connected To
+menu.CDCOnBoot=USB CDC On Boot
+menu.MSCOnBoot=USB Firmware MSC On Boot
+menu.DFUOnBoot=USB DFU On Boot
 menu.UploadMode=Upload Mode
 menu.CPUFreq=CPU Frequency
 menu.FlashFreq=Flash Frequency
@@ -37,7 +39,7 @@ esp32c3.build.variant=esp32c3
 esp32c3.build.board=ESP32C3_DEV
 esp32c3.build.bootloader_addr=0x0
 
-esp32c3.build.serial=0
+esp32c3.build.cdc_on_boot=0
 esp32c3.build.f_cpu=160000000L
 esp32c3.build.flash_size=4MB
 esp32c3.build.flash_freq=80m
@@ -173,19 +175,31 @@ esp32s2.build.core=esp32
 esp32s2.build.variant=esp32s2
 esp32s2.build.board=ESP32S2_DEV
 
-esp32s2.build.serial=0
+esp32s2.build.cdc_on_boot=0
+esp32s2.build.msc_on_boot=0
+esp32s2.build.dfu_on_boot=0
 esp32s2.build.f_cpu=240000000L
 esp32s2.build.flash_size=4MB
 esp32s2.build.flash_freq=80m
-esp32s2.build.flash_mode=qio
+esp32s2.build.flash_mode=dio
 esp32s2.build.boot=qio
 esp32s2.build.partitions=default
 esp32s2.build.defines=
 
-esp32s2.menu.SerialMode.default=UART0
-esp32s2.menu.SerialMode.default.build.serial=0
-esp32s2.menu.SerialMode.cdc=USB CDC
-esp32s2.menu.SerialMode.cdc.build.serial=1
+esp32s2.menu.CDCOnBoot.default=Disabled
+esp32s2.menu.CDCOnBoot.default.build.cdc_on_boot=0
+esp32s2.menu.CDCOnBoot.cdc=Enabled
+esp32s2.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
+
+esp32s2.menu.MSCOnBoot.default=Disabled
+esp32s2.menu.MSCOnBoot.default.build.msc_on_boot=0
+esp32s2.menu.MSCOnBoot.msc=Enabled
+esp32s2.menu.MSCOnBoot.msc.build.msc_on_boot=1
+
+esp32s2.menu.DFUOnBoot.default=Disabled
+esp32s2.menu.DFUOnBoot.default.build.dfu_on_boot=0
+esp32s2.menu.DFUOnBoot.dfu=Enabled
+esp32s2.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
 
 esp32s2.menu.UploadMode.default=UART0
 esp32s2.menu.UploadMode.default.upload.use_1200bps_touch=false
@@ -635,6 +649,103 @@ pico32.menu.DebugLevel.verbose.build.code_debug=5
 
 ##############################################################
 
+esp32s2usb.name=ESP32S2 Native USB
+esp32s2usb.vid.0=0x303a
+esp32s2usb.pid.0=0x0003
+
+esp32s2usb.upload.tool=esptool_py
+esp32s2usb.upload.maximum_size=1310720
+esp32s2usb.upload.maximum_data_size=327680
+esp32s2usb.upload.flags=
+esp32s2usb.upload.extra_flags=
+esp32s2usb.upload.use_1200bps_touch=true
+esp32s2usb.upload.wait_for_upload_port=true
+esp32s2usb.upload.speed=921600
+
+esp32s2usb.serial.disableDTR=false
+esp32s2usb.serial.disableRTS=false
+
+esp32s2usb.build.tarch=xtensa
+esp32s2usb.build.bootloader_addr=0x1000
+esp32s2usb.build.target=esp32s2
+esp32s2usb.build.mcu=esp32s2
+esp32s2usb.build.core=esp32
+esp32s2usb.build.variant=esp32s2usb
+esp32s2usb.build.board=ESP32S2_USB
+
+esp32s2usb.build.cdc_on_boot=1
+esp32s2usb.build.msc_on_boot=1
+esp32s2usb.build.dfu_on_boot=1
+esp32s2usb.build.f_cpu=240000000L
+esp32s2usb.build.flash_size=4MB
+esp32s2usb.build.flash_freq=80m
+esp32s2usb.build.flash_mode=dio
+esp32s2usb.build.boot=qio
+esp32s2usb.build.partitions=default
+esp32s2usb.build.defines=
+
+esp32s2usb.menu.PSRAM.disabled=Disabled
+esp32s2usb.menu.PSRAM.disabled.build.defines=
+esp32s2usb.menu.PSRAM.enabled=Enabled
+esp32s2usb.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM
+
+esp32s2usb.menu.FlashSize.4M=4MB (32Mb)
+esp32s2usb.menu.FlashSize.4M.build.flash_size=4MB
+esp32s2usb.menu.FlashSize.8M=8MB (64Mb)
+esp32s2usb.menu.FlashSize.8M.build.flash_size=8MB
+esp32s2usb.menu.FlashSize.8M.build.partitions=default_8MB
+esp32s2usb.menu.FlashSize.16M=16MB (128Mb)
+esp32s2usb.menu.FlashSize.16M.build.flash_size=16MB
+
+esp32s2usb.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)
+esp32s2usb.menu.PartitionScheme.default.build.partitions=default
+esp32s2usb.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS)
+esp32s2usb.menu.PartitionScheme.defaultffat.build.partitions=default_ffat
+esp32s2usb.menu.PartitionScheme.default_8MB=8M Flash (3MB APP/1.5MB FAT)
+esp32s2usb.menu.PartitionScheme.default_8MB.build.partitions=default_8MB
+esp32s2usb.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336
+esp32s2usb.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS)
+esp32s2usb.menu.PartitionScheme.minimal.build.partitions=minimal
+esp32s2usb.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS)
+esp32s2usb.menu.PartitionScheme.no_ota.build.partitions=no_ota
+esp32s2usb.menu.PartitionScheme.no_ota.upload.maximum_size=2097152
+esp32s2usb.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS)
+esp32s2usb.menu.PartitionScheme.noota_3g.build.partitions=noota_3g
+esp32s2usb.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576
+esp32s2usb.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS)
+esp32s2usb.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat
+esp32s2usb.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152
+esp32s2usb.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS)
+esp32s2usb.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat
+esp32s2usb.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576
+esp32s2usb.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS)
+esp32s2usb.menu.PartitionScheme.huge_app.build.partitions=huge_app
+esp32s2usb.menu.PartitionScheme.huge_app.upload.maximum_size=3145728
+esp32s2usb.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)
+esp32s2usb.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs
+esp32s2usb.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080
+esp32s2usb.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FAT)
+esp32s2usb.menu.PartitionScheme.fatflash.build.partitions=ffat
+esp32s2usb.menu.PartitionScheme.fatflash.upload.maximum_size=2097152
+esp32s2usb.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9MB FATFS)
+esp32s2usb.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB
+esp32s2usb.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728
+
+esp32s2usb.menu.DebugLevel.none=None
+esp32s2usb.menu.DebugLevel.none.build.code_debug=0
+esp32s2usb.menu.DebugLevel.error=Error
+esp32s2usb.menu.DebugLevel.error.build.code_debug=1
+esp32s2usb.menu.DebugLevel.warn=Warn
+esp32s2usb.menu.DebugLevel.warn.build.code_debug=2
+esp32s2usb.menu.DebugLevel.info=Info
+esp32s2usb.menu.DebugLevel.info.build.code_debug=3
+esp32s2usb.menu.DebugLevel.debug=Debug
+esp32s2usb.menu.DebugLevel.debug.build.code_debug=4
+esp32s2usb.menu.DebugLevel.verbose=Verbose
+esp32s2usb.menu.DebugLevel.verbose.build.code_debug=5
+
+##############################################################
+
 esp32wroverkit.name=ESP32 Wrover Kit (all versions)
 esp32wroverkit.upload.tool=esptool_py
 esp32wroverkit.upload.maximum_size=1310720
@@ -865,7 +976,9 @@ feathers2.build.core=esp32
 feathers2.build.variant=um_feathers2
 feathers2.build.board=FEATHERS2
 
-feathers2.build.serial=1
+feathers2.build.cdc_on_boot=1
+feathers2.build.msc_on_boot=1
+feathers2.build.dfu_on_boot=0
 feathers2.build.f_cpu=240000000L
 feathers2.build.flash_size=16MB
 feathers2.build.flash_freq=80m
@@ -874,10 +987,20 @@ feathers2.build.boot=qio
 feathers2.build.partitions=fatflash
 feathers2.build.defines=
 
-feathers2.menu.SerialMode.cdc=USB CDC
-feathers2.menu.SerialMode.cdc.build.serial=1
-feathers2.menu.SerialMode.default=UART0
-feathers2.menu.SerialMode.default.build.serial=0
+feathers2.menu.CDCOnBoot.cdc=Enabled
+feathers2.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
+feathers2.menu.CDCOnBoot.default=Disabled
+feathers2.menu.CDCOnBoot.default.build.cdc_on_boot=0
+
+feathers2.menu.MSCOnBoot.msc=Enabled
+feathers2.menu.MSCOnBoot.msc.build.msc_on_boot=1
+feathers2.menu.MSCOnBoot.default=Disabled
+feathers2.menu.MSCOnBoot.default.build.msc_on_boot=0
+
+feathers2.menu.DFUOnBoot.default=Disabled
+feathers2.menu.DFUOnBoot.default.build.dfu_on_boot=0
+feathers2.menu.DFUOnBoot.dfu=Enabled
+feathers2.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
 
 feathers2.menu.PSRAM.enabled=Enabled
 feathers2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM
@@ -993,7 +1116,9 @@ tinys2.build.core=esp32
 tinys2.build.variant=um_tinys2
 tinys2.build.board=TINYS2
 
-tinys2.build.serial=1
+tinys2.build.cdc_on_boot=1
+tinys2.build.msc_on_boot=1
+tinys2.build.dfu_on_boot=0
 tinys2.build.f_cpu=240000000L
 tinys2.build.flash_size=4MB
 tinys2.build.flash_freq=80m
@@ -1002,10 +1127,20 @@ tinys2.build.boot=qio
 tinys2.build.partitions=default
 tinys2.build.defines=
 
-tinys2.menu.SerialMode.cdc=USB CDC
-tinys2.menu.SerialMode.cdc.build.serial=1
-tinys2.menu.SerialMode.default=UART0
-tinys2.menu.SerialMode.default.build.serial=0
+tinys2.menu.CDCOnBoot.cdc=Enabled
+tinys2.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
+tinys2.menu.CDCOnBoot.default=Disabled
+tinys2.menu.CDCOnBoot.default.build.cdc_on_boot=0
+
+tinys2.menu.MSCOnBoot.msc=Enabled
+tinys2.menu.MSCOnBoot.msc.build.msc_on_boot=1
+tinys2.menu.MSCOnBoot.default=Disabled
+tinys2.menu.MSCOnBoot.default.build.msc_on_boot=0
+
+tinys2.menu.DFUOnBoot.default=Disabled
+tinys2.menu.DFUOnBoot.default.build.dfu_on_boot=0
+tinys2.menu.DFUOnBoot.dfu=Enabled
+tinys2.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
 
 tinys2.menu.PSRAM.enabled=Enabled
 tinys2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM
@@ -1166,7 +1301,9 @@ micros2.build.core=esp32
 micros2.build.variant=micro_s2
 micros2.build.board=MICROS2
 
-micros2.build.serial=1
+micros2.build.cdc_on_boot=1
+micros2.build.msc_on_boot=1
+micros2.build.dfu_on_boot=0
 micros2.build.f_cpu=240000000L
 micros2.build.flash_size=16MB
 micros2.build.flash_freq=80m
@@ -1175,10 +1312,20 @@ micros2.build.boot=qio
 micros2.build.partitions=fatflash
 micros2.build.defines=
 
-micros2.menu.SerialMode.cdc=USB CDC
-micros2.menu.SerialMode.cdc.build.serial=1
-micros2.menu.SerialMode.default=UART0
-micros2.menu.SerialMode.default.build.serial=0
+micros2.menu.CDCOnBoot.cdc=Enabled
+micros2.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
+micros2.menu.CDCOnBoot.default=Disabled
+micros2.menu.CDCOnBoot.default.build.cdc_on_boot=0
+
+micros2.menu.MSCOnBoot.msc=Enabled
+micros2.menu.MSCOnBoot.msc.build.msc_on_boot=1
+micros2.menu.MSCOnBoot.default=Disabled
+micros2.menu.MSCOnBoot.default.build.msc_on_boot=0
+
+micros2.menu.DFUOnBoot.default=Disabled
+micros2.menu.DFUOnBoot.default.build.dfu_on_boot=0
+micros2.menu.DFUOnBoot.dfu=Enabled
+micros2.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
 
 micros2.menu.PSRAM.enabled=Enabled
 micros2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM
@@ -2025,7 +2172,9 @@ sparkfun_esp32s2_thing_plus.build.core=esp32
 sparkfun_esp32s2_thing_plus.build.variant=esp32s2thing_plus
 sparkfun_esp32s2_thing_plus.build.board=ESP32S2_THING_PLUS
 
-sparkfun_esp32s2_thing_plus.build.serial=0
+sparkfun_esp32s2_thing_plus.build.cdc_on_boot=0
+sparkfun_esp32s2_thing_plus.build.msc_on_boot=0
+sparkfun_esp32s2_thing_plus.build.dfu_on_boot=0
 sparkfun_esp32s2_thing_plus.build.f_cpu=240000000L
 sparkfun_esp32s2_thing_plus.build.flash_size=4MB
 sparkfun_esp32s2_thing_plus.build.flash_freq=80m
@@ -2034,10 +2183,20 @@ sparkfun_esp32s2_thing_plus.build.boot=qio
 sparkfun_esp32s2_thing_plus.build.partitions=default
 sparkfun_esp32s2_thing_plus.build.defines=
 
-sparkfun_esp32s2_thing_plus.menu.SerialMode.default=UART0
-sparkfun_esp32s2_thing_plus.menu.SerialMode.default.build.serial=0
-sparkfun_esp32s2_thing_plus.menu.SerialMode.cdc=USB CDC
-sparkfun_esp32s2_thing_plus.menu.SerialMode.cdc.build.serial=1
+sparkfun_esp32s2_thing_plus.menu.CDCOnBoot.default=Disabled
+sparkfun_esp32s2_thing_plus.menu.CDCOnBoot.default.build.cdc_on_boot=0
+sparkfun_esp32s2_thing_plus.menu.CDCOnBoot.cdc=Enabled
+sparkfun_esp32s2_thing_plus.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
+
+sparkfun_esp32s2_thing_plus.menu.MSCOnBoot.default=Disabled
+sparkfun_esp32s2_thing_plus.menu.MSCOnBoot.default.build.msc_on_boot=0
+sparkfun_esp32s2_thing_plus.menu.MSCOnBoot.msc=Enabled
+sparkfun_esp32s2_thing_plus.menu.MSCOnBoot.msc.build.msc_on_boot=1
+
+sparkfun_esp32s2_thing_plus.menu.DFUOnBoot.default=Disabled
+sparkfun_esp32s2_thing_plus.menu.DFUOnBoot.default.build.dfu_on_boot=0
+sparkfun_esp32s2_thing_plus.menu.DFUOnBoot.dfu=Enabled
+sparkfun_esp32s2_thing_plus.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
 
 sparkfun_esp32s2_thing_plus.menu.PSRAM.disabled=Disabled
 sparkfun_esp32s2_thing_plus.menu.PSRAM.disabled.build.defines=
@@ -3334,7 +3493,9 @@ adafruit_metro_esp32s2.build.core=esp32
 adafruit_metro_esp32s2.build.variant=adafruit_metro_esp32s2
 adafruit_metro_esp32s2.build.board=METRO_ESP32S2
 
-adafruit_metro_esp32s2.build.serial=0
+adafruit_metro_esp32s2.build.cdc_on_boot=1
+adafruit_metro_esp32s2.build.msc_on_boot=0
+adafruit_metro_esp32s2.build.dfu_on_boot=0
 adafruit_metro_esp32s2.build.f_cpu=240000000L
 adafruit_metro_esp32s2.build.flash_size=4MB
 adafruit_metro_esp32s2.build.flash_freq=80m
@@ -3343,10 +3504,20 @@ adafruit_metro_esp32s2.build.boot=qio
 adafruit_metro_esp32s2.build.partitions=default
 adafruit_metro_esp32s2.build.defines=
 
-adafruit_metro_esp32s2.menu.SerialMode.cdc=USB CDC
-adafruit_metro_esp32s2.menu.SerialMode.cdc.build.serial=1
-adafruit_metro_esp32s2.menu.SerialMode.default=UART0
-adafruit_metro_esp32s2.menu.SerialMode.default.build.serial=0
+adafruit_metro_esp32s2.menu.CDCOnBoot.cdc=Enabled
+adafruit_metro_esp32s2.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
+adafruit_metro_esp32s2.menu.CDCOnBoot.default=Disabled
+adafruit_metro_esp32s2.menu.CDCOnBoot.default.build.cdc_on_boot=0
+
+adafruit_metro_esp32s2.menu.MSCOnBoot.default=Disabled
+adafruit_metro_esp32s2.menu.MSCOnBoot.default.build.msc_on_boot=0
+adafruit_metro_esp32s2.menu.MSCOnBoot.msc=Enabled
+adafruit_metro_esp32s2.menu.MSCOnBoot.msc.build.msc_on_boot=1
+
+adafruit_metro_esp32s2.menu.DFUOnBoot.default=Disabled
+adafruit_metro_esp32s2.menu.DFUOnBoot.default.build.dfu_on_boot=0
+adafruit_metro_esp32s2.menu.DFUOnBoot.dfu=Enabled
+adafruit_metro_esp32s2.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
 
 adafruit_metro_esp32s2.menu.PSRAM.enabled=Enabled
 adafruit_metro_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM
@@ -3487,7 +3658,9 @@ adafruit_magtag29_esp32s2.build.core=esp32
 adafruit_magtag29_esp32s2.build.variant=adafruit_magtag29_esp32s2
 adafruit_magtag29_esp32s2.build.board=MAGTAG29_ESP32S2
 
-adafruit_magtag29_esp32s2.build.serial=0
+adafruit_magtag29_esp32s2.build.cdc_on_boot=1
+adafruit_magtag29_esp32s2.build.msc_on_boot=0
+adafruit_magtag29_esp32s2.build.dfu_on_boot=0
 adafruit_magtag29_esp32s2.build.f_cpu=240000000L
 adafruit_magtag29_esp32s2.build.flash_size=4MB
 adafruit_magtag29_esp32s2.build.flash_freq=80m
@@ -3496,10 +3669,20 @@ adafruit_magtag29_esp32s2.build.boot=qio
 adafruit_magtag29_esp32s2.build.partitions=default
 adafruit_magtag29_esp32s2.build.defines=
 
-adafruit_magtag29_esp32s2.menu.SerialMode.cdc=USB CDC
-adafruit_magtag29_esp32s2.menu.SerialMode.cdc.build.serial=1
-adafruit_magtag29_esp32s2.menu.SerialMode.default=UART0
-adafruit_magtag29_esp32s2.menu.SerialMode.default.build.serial=0
+adafruit_magtag29_esp32s2.menu.CDCOnBoot.cdc=Enabled
+adafruit_magtag29_esp32s2.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
+adafruit_magtag29_esp32s2.menu.CDCOnBoot.default=Disabled
+adafruit_magtag29_esp32s2.menu.CDCOnBoot.default.build.cdc_on_boot=0
+
+adafruit_magtag29_esp32s2.menu.MSCOnBoot.default=Disabled
+adafruit_magtag29_esp32s2.menu.MSCOnBoot.default.build.msc_on_boot=0
+adafruit_magtag29_esp32s2.menu.MSCOnBoot.msc=Enabled
+adafruit_magtag29_esp32s2.menu.MSCOnBoot.msc.build.msc_on_boot=1
+
+adafruit_magtag29_esp32s2.menu.DFUOnBoot.default=Disabled
+adafruit_magtag29_esp32s2.menu.DFUOnBoot.default.build.dfu_on_boot=0
+adafruit_magtag29_esp32s2.menu.DFUOnBoot.dfu=Enabled
+adafruit_magtag29_esp32s2.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
 
 adafruit_magtag29_esp32s2.menu.PSRAM.enabled=Enabled
 adafruit_magtag29_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM
@@ -3640,7 +3823,9 @@ adafruit_funhouse_esp32s2.build.core=esp32
 adafruit_funhouse_esp32s2.build.variant=adafruit_funhouse_esp32s2
 adafruit_funhouse_esp32s2.build.board=FUNHOUSE_ESP32S2
 
-adafruit_funhouse_esp32s2.build.serial=0
+adafruit_funhouse_esp32s2.build.cdc_on_boot=1
+adafruit_funhouse_esp32s2.build.msc_on_boot=0
+adafruit_funhouse_esp32s2.build.dfu_on_boot=0
 adafruit_funhouse_esp32s2.build.f_cpu=240000000L
 adafruit_funhouse_esp32s2.build.flash_size=4MB
 adafruit_funhouse_esp32s2.build.flash_freq=80m
@@ -3649,10 +3834,20 @@ adafruit_funhouse_esp32s2.build.boot=qio
 adafruit_funhouse_esp32s2.build.partitions=default
 adafruit_funhouse_esp32s2.build.defines=
 
-adafruit_funhouse_esp32s2.menu.SerialMode.cdc=USB CDC
-adafruit_funhouse_esp32s2.menu.SerialMode.cdc.build.serial=1
-adafruit_funhouse_esp32s2.menu.SerialMode.default=UART0
-adafruit_funhouse_esp32s2.menu.SerialMode.default.build.serial=0
+adafruit_funhouse_esp32s2.menu.CDCOnBoot.cdc=Enabled
+adafruit_funhouse_esp32s2.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
+adafruit_funhouse_esp32s2.menu.CDCOnBoot.default=Disabled
+adafruit_funhouse_esp32s2.menu.CDCOnBoot.default.build.cdc_on_boot=0
+
+adafruit_funhouse_esp32s2.menu.MSCOnBoot.default=Disabled
+adafruit_funhouse_esp32s2.menu.MSCOnBoot.default.build.msc_on_boot=0
+adafruit_funhouse_esp32s2.menu.MSCOnBoot.msc=Enabled
+adafruit_funhouse_esp32s2.menu.MSCOnBoot.msc.build.msc_on_boot=1
+
+adafruit_funhouse_esp32s2.menu.DFUOnBoot.default=Disabled
+adafruit_funhouse_esp32s2.menu.DFUOnBoot.default.build.dfu_on_boot=0
+adafruit_funhouse_esp32s2.menu.DFUOnBoot.dfu=Enabled
+adafruit_funhouse_esp32s2.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
 
 adafruit_funhouse_esp32s2.menu.PSRAM.enabled=Enabled
 adafruit_funhouse_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM
@@ -3793,7 +3988,9 @@ adafruit_feather_esp32s2_nopsram.build.core=esp32
 adafruit_feather_esp32s2_nopsram.build.variant=adafruit_feather_esp32s2
 adafruit_feather_esp32s2_nopsram.build.board=ADAFRUIT_FEATHER_ESP32S2_NOPSRAM
 
-adafruit_feather_esp32s2_nopsram.build.serial=0
+adafruit_feather_esp32s2_nopsram.build.cdc_on_boot=0
+adafruit_feather_esp32s2_nopsram.build.msc_on_boot=0
+adafruit_feather_esp32s2_nopsram.build.dfu_on_boot=0
 adafruit_feather_esp32s2_nopsram.build.f_cpu=240000000L
 adafruit_feather_esp32s2_nopsram.build.flash_size=4MB
 adafruit_feather_esp32s2_nopsram.build.flash_freq=80m
@@ -3802,10 +3999,20 @@ adafruit_feather_esp32s2_nopsram.build.boot=qio
 adafruit_feather_esp32s2_nopsram.build.partitions=default
 adafruit_feather_esp32s2_nopsram.build.defines=
 
-adafruit_feather_esp32s2_nopsram.menu.SerialMode.cdc=USB CDC
-adafruit_feather_esp32s2_nopsram.menu.SerialMode.cdc.build.serial=1
-adafruit_feather_esp32s2_nopsram.menu.SerialMode.default=UART0
-adafruit_feather_esp32s2_nopsram.menu.SerialMode.default.build.serial=0
+adafruit_feather_esp32s2_nopsram.menu.CDCOnBoot.cdc=Enabled
+adafruit_feather_esp32s2_nopsram.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
+adafruit_feather_esp32s2_nopsram.menu.CDCOnBoot.default=Disabled
+adafruit_feather_esp32s2_nopsram.menu.CDCOnBoot.default.build.cdc_on_boot=0
+
+adafruit_feather_esp32s2_nopsram.menu.MSCOnBoot.default=Disabled
+adafruit_feather_esp32s2_nopsram.menu.MSCOnBoot.default.build.msc_on_boot=0
+adafruit_feather_esp32s2_nopsram.menu.MSCOnBoot.msc=Enabled
+adafruit_feather_esp32s2_nopsram.menu.MSCOnBoot.msc.build.msc_on_boot=1
+
+adafruit_feather_esp32s2_nopsram.menu.DFUOnBoot.default=Disabled
+adafruit_feather_esp32s2_nopsram.menu.DFUOnBoot.default.build.dfu_on_boot=0
+adafruit_feather_esp32s2_nopsram.menu.DFUOnBoot.dfu=Enabled
+adafruit_feather_esp32s2_nopsram.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
 
 adafruit_feather_esp32s2_nopsram.menu.PSRAM.disabled=Disabled
 adafruit_feather_esp32s2_nopsram.menu.PSRAM.disabled.build.defines=
diff --git a/cores/esp32/FirmwareMSC.cpp b/cores/esp32/FirmwareMSC.cpp
new file mode 100644
index 00000000000..c399a013b25
--- /dev/null
+++ b/cores/esp32/FirmwareMSC.cpp
@@ -0,0 +1,423 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstring>
+#include "FirmwareMSC.h"
+#include "esp_partition.h"
+#include "esp_ota_ops.h"
+#include "esp32-hal.h"
+#include "pins_arduino.h"
+#include "firmware_msc_fat.h"
+
+#if CONFIG_TINYUSB_MSC_ENABLED
+
+#ifndef USB_FW_MSC_VENDOR_ID
+#define USB_FW_MSC_VENDOR_ID "ESP32" //max 8 chars
+#endif
+#ifndef USB_FW_MSC_PRODUCT_ID
+#define USB_FW_MSC_PRODUCT_ID "Firmware MSC"//max 16 chars
+#endif
+#ifndef USB_FW_MSC_PRODUCT_REVISION
+#define USB_FW_MSC_PRODUCT_REVISION "1.0" //max 4 chars
+#endif
+#ifndef USB_FW_MSC_VOLUME_NAME
+#define USB_FW_MSC_VOLUME_NAME "ESP32-FWMSC" //max 11 chars
+#endif
+#ifndef USB_FW_MSC_SERIAL_NUMBER
+#define USB_FW_MSC_SERIAL_NUMBER 0x00000000
+#endif
+
+ESP_EVENT_DEFINE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
+esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
+esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
+
+//General Variables
+static uint8_t * msc_ram_disk = NULL;
+static fat_boot_sector_t * msc_boot = NULL;
+static uint8_t * msc_table = NULL;
+static uint16_t msc_table_sectors = 0;
+static uint16_t msc_total_sectors = 0;
+static bool mcs_is_fat16 = false;
+
+//Firmware Read
+static const esp_partition_t* msc_run_partition = NULL;
+static uint16_t fw_start_sector = 0;
+static uint16_t fw_end_sector = 0;
+static size_t fw_size = 0;
+static fat_dir_entry_t * fw_entry = NULL;
+
+//Firmware Write
+typedef enum {
+  MSC_UPDATE_IDLE,
+  MSC_UPDATE_STARTING,
+  MSC_UPDATE_RUNNING,
+  MSC_UPDATE_END
+} msc_update_state_t;
+
+static const esp_partition_t* msc_ota_partition = NULL;
+static msc_update_state_t msc_update_state = MSC_UPDATE_IDLE;
+static uint16_t msc_update_start_sector = 0;
+static uint32_t msc_update_bytes_written = 0;
+static fat_dir_entry_t * msc_update_entry = NULL;
+
+static uint32_t get_firmware_size(const esp_partition_t* partition){
+  esp_image_metadata_t data;
+  const esp_partition_pos_t running_pos  = {
+      .offset = partition->address,
+      .size = partition->size,
+  };
+  data.start_addr = running_pos.offset;
+  esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data);
+  return data.image_len;
+}
+
+//Get number of sectors required based on the size of the firmware and OTA partition
+static size_t msc_update_get_required_disk_sectors(){
+  size_t data_sectors = 16;
+  size_t total_sectors = 0;
+  msc_run_partition = esp_ota_get_running_partition();
+  msc_ota_partition = esp_ota_get_next_update_partition(NULL);
+  if(msc_run_partition){
+    fw_size = get_firmware_size(msc_run_partition);
+    data_sectors += FAT_SIZE_TO_SECTORS(fw_size);
+    log_d("APP size: %u (%u sectors)", fw_size, FAT_SIZE_TO_SECTORS(fw_size));
+  } else {
+    log_w("APP partition not found. Reading disabled");
+  }
+  if(msc_ota_partition){
+    data_sectors += FAT_SIZE_TO_SECTORS(msc_ota_partition->size);
+    log_d("OTA size: %u (%u sectors)", msc_ota_partition->size, FAT_SIZE_TO_SECTORS(msc_ota_partition->size));
+  } else {
+    log_w("OTA partition not found. Writing disabled");
+  }
+  msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, false);
+  total_sectors = data_sectors + msc_table_sectors + 2;
+  if(total_sectors > 0xFF4){
+    log_d("USING FAT16");
+    mcs_is_fat16 = true;
+    total_sectors -= msc_table_sectors;
+    msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, true);
+    total_sectors += msc_table_sectors;
+  } else {
+    log_d("USING FAT12");
+    mcs_is_fat16 = false;
+  }
+  log_d("FAT data sectors: %u", data_sectors);
+  log_d("FAT table sectors: %u", msc_table_sectors);
+  log_d("FAT total sectors: %u (%uKB)", total_sectors, (total_sectors * DISK_SECTOR_SIZE) / 1024);
+  return total_sectors;
+}
+
+//setup the ramdisk and add the firmware download file
+static bool msc_update_setup_disk(const char * volume_label, uint32_t serial_number){
+  msc_total_sectors = msc_update_get_required_disk_sectors();
+  uint8_t ram_sectors = msc_table_sectors + 2;
+  msc_ram_disk = (uint8_t*)calloc(ram_sectors, DISK_SECTOR_SIZE);
+  if(!msc_ram_disk){
+    log_e("Failed to allocate RAM Disk: %u bytes", ram_sectors * DISK_SECTOR_SIZE);
+    return false;
+  }
+  fw_start_sector = ram_sectors;
+  fw_end_sector = fw_start_sector;
+  msc_boot = fat_add_boot_sector(msc_ram_disk, msc_total_sectors, msc_table_sectors, fat_file_system_type(mcs_is_fat16), volume_label, serial_number);
+  msc_table = fat_add_table(msc_ram_disk, msc_boot, mcs_is_fat16);
+  //fat_dir_entry_t * label = fat_add_label(msc_ram_disk, volume_label);
+  if(msc_run_partition){
+    fw_entry = fat_add_root_file(msc_ram_disk, 0, "FIRMWARE", "BIN", fw_size, 2, mcs_is_fat16);
+    fw_end_sector = FAT_SIZE_TO_SECTORS(fw_size) + fw_start_sector;
+  }
+  return true;
+}
+
+static void msc_update_delete_disk(){
+  fw_entry = NULL;
+  fw_size = 0;
+  fw_end_sector = 0;
+  fw_start_sector = 0;
+  msc_table = NULL;
+  msc_boot = NULL;
+  msc_table_sectors = 0;
+  msc_total_sectors = 0;
+  msc_run_partition = NULL;
+  msc_ota_partition = NULL;
+  msc_update_state = MSC_UPDATE_IDLE;
+  msc_update_start_sector = 0;
+  msc_update_bytes_written = 0;
+  msc_update_entry = NULL;
+  free(msc_ram_disk);
+  msc_ram_disk = NULL;
+}
+
+//filter out entries to only include BINs in the root folder
+static fat_dir_entry_t * msc_update_get_root_bin_entry(uint8_t index){
+  fat_dir_entry_t * entry = (fat_dir_entry_t *)(msc_ram_disk + ((msc_boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t)));
+  fat_lfn_entry_t * lfn = (fat_lfn_entry_t*)entry;
+
+  //empty entry
+  if(entry->file_magic == 0){
+    return NULL;
+  }
+  //long file name
+  if(lfn->attr == 0x0F && lfn->type == 0x00 && lfn->first_cluster == 0x0000){
+    return NULL;
+  }
+  //only files marked as archives
+  if(entry->file_attr != FAT_FILE_ATTR_ARCHIVE){
+    return NULL;
+  }
+  //deleted
+  if(entry->file_magic == 0xE5 || entry->file_magic == 0x05){
+    return NULL;
+  }
+  //not bins
+  if(memcmp("BIN", entry->file_extension, 3)){
+    return NULL;
+  }
+  return entry;
+}
+
+//get an empty bin (the host will add an entry for file about to be written with size of zero)
+static fat_dir_entry_t * msc_update_find_new_bin(){
+  for(uint8_t i=16; i;){
+    i--;
+    fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i);
+    if(entry && entry->file_size == 0){
+      return entry;
+    }
+  }
+  return NULL;
+}
+
+//get a bin starting from particular sector
+static fat_dir_entry_t * msc_update_find_bin(uint16_t sector){
+  for(uint8_t i=16; i; ){
+    i--;
+    fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i);
+    if(entry && entry->data_start_sector == (sector - msc_boot->sectors_per_alloc_table)){
+      return entry;
+    }
+  }
+  return NULL;
+}
+
+//write the new data and erase the flash blocks when necessary
+static esp_err_t msc_update_write(const esp_partition_t *partition, uint32_t offset, void *data, size_t size){
+  esp_err_t err = ESP_OK;
+  if((offset & (SPI_FLASH_SEC_SIZE-1)) == 0){
+    err = esp_partition_erase_range(partition, offset, SPI_FLASH_SEC_SIZE);
+    log_v("ERASE[0x%08X]: %s", offset, (err != ESP_OK)?"FAIL":"OK");
+    if(err != ESP_OK){
+      return err;
+    }
+  }
+  return esp_partition_write(partition, offset, data, size);
+}
+
+//called when error was encountered while updating
+static void msc_update_error(){
+  log_e("UPDATE_ERROR: %u", msc_update_bytes_written);
+  arduino_firmware_msc_event_data_t p = {0};
+  p.error.size = msc_update_bytes_written;
+  arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_ERROR_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
+  msc_update_state = MSC_UPDATE_IDLE;
+  msc_update_entry = NULL;
+  msc_update_bytes_written = 0;
+  msc_update_start_sector = 0;
+}
+
+//called when all firmware bytes have been received
+static void msc_update_end(){
+  log_d("UPDATE_END: %u", msc_update_entry->file_size);
+  msc_update_state = MSC_UPDATE_END;
+  size_t ota_size = get_firmware_size(msc_ota_partition);
+  if(ota_size != msc_update_entry->file_size){
+    log_e("OTA SIZE MISMATCH %u != %u", ota_size, msc_update_entry->file_size);
+    msc_update_error();
+    return;
+  }
+  if(!ota_size || esp_ota_set_boot_partition(msc_ota_partition) != ESP_OK){
+    log_e("ENABLING OTA PARTITION FAILED");
+    msc_update_error();
+    return;
+  }
+  arduino_firmware_msc_event_data_t p = {0};
+  p.end.size = msc_update_entry->file_size;
+  arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_END_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
+}
+
+static int32_t msc_write(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){
+  //log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
+  if(lba < fw_start_sector){
+    //write to sectors that are in RAM
+    memcpy(msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, buffer, bufsize);
+    if(msc_ota_partition && lba == (fw_start_sector - 1)){
+      //monitor the root folder table
+      if(msc_update_state <= MSC_UPDATE_RUNNING){
+        fat_dir_entry_t * update_entry = msc_update_find_new_bin();
+        if(update_entry) {
+          if(msc_update_entry) {
+            log_v("REPLACING ENTRY");
+          } else {
+            log_v("ASSIGNING ENTRY");
+          }
+          if(msc_update_state <= MSC_UPDATE_STARTING){
+            msc_update_state = MSC_UPDATE_STARTING;
+            msc_update_bytes_written = 0;
+            msc_update_start_sector = 0;
+          }
+          msc_update_entry = update_entry;
+        } else if(msc_update_state == MSC_UPDATE_RUNNING){
+          if(!msc_update_entry && msc_update_start_sector){
+            msc_update_entry = msc_update_find_bin(msc_update_start_sector);
+          }
+          if(msc_update_entry && msc_update_bytes_written >= msc_update_entry->file_size){
+            msc_update_end();
+          }
+        }
+      }
+    }
+  } else if(msc_ota_partition && lba >= msc_update_start_sector){
+    //handle writes to the region where the new firmware will be uploaded
+    arduino_firmware_msc_event_data_t p = {0};
+    if(msc_update_state <= MSC_UPDATE_STARTING && buffer[0] == 0xE9){
+      msc_update_state = MSC_UPDATE_RUNNING;
+      msc_update_start_sector = lba;
+      msc_update_bytes_written = 0;
+      log_d("UPDATE_START: %u (0x%02X)", lba, lba - msc_boot->sectors_per_alloc_table);
+      arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_START_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
+      if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){
+        log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
+        msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
+        p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
+        p.write.size = bufsize;
+        arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
+      } else {
+        msc_update_error();
+        return 0;
+      }
+    } else if(msc_update_state == MSC_UPDATE_RUNNING){
+      if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written < msc_update_entry->file_size && (msc_update_bytes_written + bufsize) >= msc_update_entry->file_size){
+        bufsize = msc_update_entry->file_size - msc_update_bytes_written;
+      }
+      if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){
+        log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize);
+        msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize;
+        p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset;
+        p.write.size = bufsize;
+        arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
+        if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written >= msc_update_entry->file_size){
+          msc_update_end();
+        }
+      } else {
+        msc_update_error();
+        return 0;
+      }
+    }
+  }
+  return bufsize;
+}
+
+static int32_t msc_read(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){
+  //log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize);
+  if(lba < fw_start_sector){
+    memcpy(buffer, msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, bufsize);
+  } else if(msc_run_partition && lba < fw_end_sector){
+    //read the currently running firmware
+    if(esp_partition_read(msc_run_partition, ((lba - fw_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) != ESP_OK){
+      return 0;
+    }
+  } else {
+    memset(buffer, 0, bufsize);
+  }
+  return bufsize;
+}
+
+static bool msc_start_stop(uint8_t power_condition, bool start, bool load_eject){
+  //log_d("power: %u, start: %u, eject: %u", power_condition, start, load_eject);
+  arduino_firmware_msc_event_data_t p = {0};
+  p.power.power_condition = power_condition;
+  p.power.start = start;
+  p.power.load_eject = load_eject;
+  arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_POWER_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY);
+  return true;
+}
+
+static volatile TaskHandle_t msc_task_handle = NULL;
+static void msc_task(void *pvParameters){
+  for (;;) {
+    if(msc_update_state == MSC_UPDATE_END){
+      delay(100);
+      esp_restart();
+    }
+    delay(100);
+  }
+  msc_task_handle = NULL;
+  vTaskDelete(NULL);
+}
+
+FirmwareMSC::FirmwareMSC():msc(){}
+
+FirmwareMSC::~FirmwareMSC(){
+  end();
+}
+
+bool FirmwareMSC::begin(){
+  if(msc_ram_disk){
+    return true;
+  }
+
+  if(!msc_update_setup_disk(USB_FW_MSC_VOLUME_NAME, USB_FW_MSC_SERIAL_NUMBER)){
+    return false;
+  }
+
+  if(!msc_task_handle){
+    xTaskCreateUniversal(msc_task, "msc_disk", 1024, NULL, 2, (TaskHandle_t*)&msc_task_handle, 0);
+    if(!msc_task_handle){
+      msc_update_delete_disk();
+      return false;
+    }
+  }
+
+  msc.vendorID(USB_FW_MSC_VENDOR_ID);
+  msc.productID(USB_FW_MSC_PRODUCT_ID);
+  msc.productRevision(USB_FW_MSC_PRODUCT_REVISION);
+  msc.onStartStop(msc_start_stop);
+  msc.onRead(msc_read);
+  msc.onWrite(msc_write);
+  msc.mediaPresent(true);
+  msc.begin(msc_boot->fat12_sector_num, DISK_SECTOR_SIZE);
+  return true;
+}
+
+void FirmwareMSC::end(){
+  msc.end();
+  if(msc_task_handle){
+    vTaskDelete(msc_task_handle);
+    msc_task_handle = NULL;
+  }
+  msc_update_delete_disk();
+}
+
+void FirmwareMSC::onEvent(esp_event_handler_t callback){
+    onEvent(ARDUINO_FIRMWARE_MSC_ANY_EVENT, callback);
+}
+void FirmwareMSC::onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback){
+    arduino_usb_event_handler_register_with(ARDUINO_FIRMWARE_MSC_EVENTS, event, callback, this);
+}
+
+#if ARDUINO_USB_MSC_ON_BOOT
+FirmwareMSC MSC_Update;
+#endif
+
+#endif /* CONFIG_USB_MSC_ENABLED */
diff --git a/cores/esp32/FirmwareMSC.h b/cores/esp32/FirmwareMSC.h
new file mode 100644
index 00000000000..3caaf6a0e64
--- /dev/null
+++ b/cores/esp32/FirmwareMSC.h
@@ -0,0 +1,69 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+#include <stdbool.h>
+#include "USBMSC.h"
+#if CONFIG_TINYUSB_MSC_ENABLED
+
+#include "esp_event.h"
+
+ESP_EVENT_DECLARE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS);
+
+typedef enum {
+    ARDUINO_FIRMWARE_MSC_ANY_EVENT = ESP_EVENT_ANY_ID,
+    ARDUINO_FIRMWARE_MSC_START_EVENT = 0,
+    ARDUINO_FIRMWARE_MSC_WRITE_EVENT,
+    ARDUINO_FIRMWARE_MSC_END_EVENT,
+    ARDUINO_FIRMWARE_MSC_ERROR_EVENT,
+    ARDUINO_FIRMWARE_MSC_POWER_EVENT,
+    ARDUINO_FIRMWARE_MSC_MAX_EVENT,
+} arduino_firmware_msc_event_t;
+
+typedef union {
+    struct {
+            size_t offset;
+            size_t size;
+    } write;
+    struct {
+            uint8_t power_condition;
+            bool start;
+            bool load_eject;
+    } power;
+    struct {
+            size_t size;
+    } end;
+    struct {
+            size_t size;
+    } error;
+} arduino_firmware_msc_event_data_t;
+
+class FirmwareMSC {
+private:
+  USBMSC msc;
+
+public:
+  FirmwareMSC();
+  ~FirmwareMSC();
+  bool begin();
+  void end();
+  void onEvent(esp_event_handler_t callback);
+  void onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback);
+};
+
+#if ARDUINO_USB_MSC_ON_BOOT
+extern FirmwareMSC MSC_Update;
+#endif
+
+#endif /* CONFIG_TINYUSB_MSC_ENABLED */
diff --git a/cores/esp32/HardwareSerial.cpp b/cores/esp32/HardwareSerial.cpp
index 7eb75354670..2f1cb25d5b8 100644
--- a/cores/esp32/HardwareSerial.cpp
+++ b/cores/esp32/HardwareSerial.cpp
@@ -37,7 +37,7 @@
 #endif
 
 #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
-#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
+#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
 HardwareSerial Serial0(0);
 #else
 HardwareSerial Serial(0);
diff --git a/cores/esp32/HardwareSerial.h b/cores/esp32/HardwareSerial.h
index 1ea76917fa0..795b8b68c6d 100644
--- a/cores/esp32/HardwareSerial.h
+++ b/cores/esp32/HardwareSerial.h
@@ -113,10 +113,10 @@ class HardwareSerial: public Stream
 extern void serialEventRun(void) __attribute__((weak));
 
 #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
-#ifndef ARDUINO_SERIAL_PORT
-#define ARDUINO_SERIAL_PORT 0
+#ifndef ARDUINO_USB_CDC_ON_BOOT
+#define ARDUINO_USB_CDC_ON_BOOT 0
 #endif
-#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
+#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
 #include "USB.h"
 #include "USBCDC.h"
 extern HardwareSerial Serial0;
diff --git a/cores/esp32/USB.cpp b/cores/esp32/USB.cpp
index 4ce28a6cf05..1221707cb0a 100644
--- a/cores/esp32/USB.cpp
+++ b/cores/esp32/USB.cpp
@@ -31,14 +31,16 @@
 #ifndef USB_SERIAL
 #define USB_SERIAL "0"
 #endif
+#ifndef USB_WEBUSB_ENABLED
+#define USB_WEBUSB_ENABLED false
+#endif
+#ifndef USB_WEBUSB_URL
+#define USB_WEBUSB_URL "https://espressif.github.io/arduino-esp32/webusb.html"
+#endif
 
 #if CFG_TUD_DFU_RUNTIME
 static uint16_t load_dfu_descriptor(uint8_t * dst, uint8_t * itf)
 {
-#define DFU_ATTR_CAN_DOWNLOAD              1
-#define DFU_ATTR_CAN_UPLOAD                2
-#define DFU_ATTR_MANIFESTATION_TOLERANT    4
-#define DFU_ATTR_WILL_DETACH               8
 #define DFU_ATTRS (DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_CAN_UPLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
 
     uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB DFU_RT");
@@ -120,8 +122,8 @@ ESPUSB::ESPUSB(size_t task_stack_size, uint8_t event_task_priority)
 ,usb_protocol(MISC_PROTOCOL_IAD)
 ,usb_attributes(TUSB_DESC_CONFIG_ATT_SELF_POWERED)
 ,usb_power_ma(500)
-,webusb_enabled(false)
-,webusb_url("https://espressif.github.io/arduino-esp32/webusb.html")
+,webusb_enabled(USB_WEBUSB_ENABLED)
+,webusb_url(USB_WEBUSB_URL)
 ,_started(false)
 ,_task_stack_size(task_stack_size)
 ,_event_task_priority(event_task_priority)
diff --git a/cores/esp32/USB.h b/cores/esp32/USB.h
index 2a5fdb7182e..d64962c7fc7 100644
--- a/cores/esp32/USB.h
+++ b/cores/esp32/USB.h
@@ -18,9 +18,11 @@
 
 #include "Arduino.h"
 #include "USBCDC.h"
-
+#include "common/tusb_common.h"
 #include "esp_event.h"
 
+#define ARDUINO_USB_ON_BOOT (ARDUINO_USB_CDC_ON_BOOT|ARDUINO_USB_MSC_ON_BOOT|ARDUINO_USB_DFU_ON_BOOT)
+
 ESP_EVENT_DECLARE_BASE(ARDUINO_USB_EVENTS);
 
 typedef enum {
diff --git a/cores/esp32/USBCDC.cpp b/cores/esp32/USBCDC.cpp
index 9dcd6bfbf5f..7a9cf5e011c 100644
--- a/cores/esp32/USBCDC.cpp
+++ b/cores/esp32/USBCDC.cpp
@@ -28,7 +28,6 @@ USBCDC * devices[MAX_USB_CDC_DEVICES] = {NULL, NULL};
 static uint16_t load_cdc_descriptor(uint8_t * dst, uint8_t * itf)
 {
     uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB CDC");
-    // Interface number, string index, attributes, detach timeout, transfer size */
     uint8_t descriptor[TUD_CDC_DESC_LEN] = {
             // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
             TUD_CDC_DESCRIPTOR(*itf, str_index, 0x85, 64, 0x03, 0x84, 64)
@@ -41,7 +40,6 @@ static uint16_t load_cdc_descriptor(uint8_t * dst, uint8_t * itf)
 // Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE
 void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
 {
-    //isr_log_v("itf: %u, dtr: %u, rts: %u", itf, dtr, rts);
     if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
         devices[itf]->_onLineState(dtr, rts);
     }
@@ -50,7 +48,6 @@ void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
 // Invoked when line coding is change via SET_LINE_CODING
 void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
 {
-    //isr_log_v("itf: %u, bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u", itf, p_line_coding->bit_rate, p_line_coding->data_bits, p_line_coding->stop_bits, p_line_coding->parity);
     if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
         devices[itf]->_onLineCoding(p_line_coding->bit_rate, p_line_coding->stop_bits, p_line_coding->parity, p_line_coding->data_bits);
     }
@@ -59,7 +56,6 @@ void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
 // Invoked when received new data
 void tud_cdc_rx_cb(uint8_t itf)
 {
-    //isr_log_v("itf: %u", itf);
     if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
         devices[itf]->_onRX();
     }
@@ -72,15 +68,14 @@ void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms){
 
 // Invoked when space becomes available in TX buffer
 void tud_cdc_tx_complete_cb(uint8_t itf){
-    //isr_log_v("itf: %u", itf);
-    if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL){
+    if(itf < MAX_USB_CDC_DEVICES && devices[itf] != NULL && devices[itf]->tx_sem != NULL){
         xSemaphoreGive(devices[itf]->tx_sem);
         devices[itf]->_onTX();
     }
 }
 
 static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size){
-    if(itf >= MAX_USB_CDC_DEVICES){
+    if(itf >= MAX_USB_CDC_DEVICES || devices[itf] == NULL || devices[itf]->tx_sem == NULL){
         return 0;
     }
     if(!tud_cdc_n_connected(itf)){
@@ -90,8 +85,15 @@ static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size)
     while(tosend){
         uint32_t space = tud_cdc_n_write_available(itf);
         if(!space){
-            delay(1);
-            continue;
+            //make sure that we do not get previous semaphore
+            xSemaphoreTake(devices[itf]->tx_sem, 0);
+            //wait for tx_complete
+            if(xSemaphoreTake(devices[itf]->tx_sem, 200 / portTICK_PERIOD_MS) == pdTRUE){
+                space = tud_cdc_n_write_available(itf);
+            }
+            if(!space){
+                return sofar;
+            }
         }
         if(tosend < space){
             space = tosend;
@@ -103,7 +105,7 @@ static size_t tinyusb_cdc_write(uint8_t itf, const uint8_t *buffer, size_t size)
         sofar += sent;
         tosend -= sent;
         tud_cdc_n_write_flush(itf);
-        xSemaphoreTake(devices[itf]->tx_sem, portMAX_DELAY);
+        //xSemaphoreTake(devices[itf]->tx_sem, portMAX_DELAY);
     }
     return sofar;
 }
@@ -113,21 +115,21 @@ static void ARDUINO_ISR_ATTR cdc0_write_char(char c)
     tinyusb_cdc_write(0, (const uint8_t *)&c, 1);
 }
 
-//void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
-
 static void usb_unplugged_cb(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
     ((USBCDC*)arg)->_onUnplugged();
 }
 
-USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false),  rts(false), connected(false), reboot_enable(true), rx_queue(NULL) {
+USBCDC::USBCDC(uint8_t itfn) : itf(itfn), bit_rate(0), stop_bits(0), parity(0), data_bits(0), dtr(false),  rts(false), connected(false), reboot_enable(true), rx_queue(NULL), tx_sem(NULL) {
     tinyusb_enable_interface(USB_INTERFACE_CDC, TUD_CDC_DESC_LEN, load_cdc_descriptor);
     if(itf < MAX_USB_CDC_DEVICES){
-        devices[itf] = this;
-        tx_sem = NULL;
         arduino_usb_event_handler_register_with(ARDUINO_USB_EVENTS, ARDUINO_USB_STOPPED_EVENT, usb_unplugged_cb, this);
     }
 }
 
+USBCDC::~USBCDC(){
+    end();
+}
+
 void USBCDC::onEvent(esp_event_handler_t callback){
     onEvent(ARDUINO_USB_CDC_ANY_EVENT, callback);
 }
@@ -137,6 +139,10 @@ void USBCDC::onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback
 
 size_t USBCDC::setRxBufferSize(size_t rx_queue_len){
     if(rx_queue){
+        if(!rx_queue_len){
+            vQueueDelete(rx_queue);
+            rx_queue = NULL;
+        }
         return 0;
     }
     rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
@@ -148,15 +154,19 @@ size_t USBCDC::setRxBufferSize(size_t rx_queue_len){
 
 void USBCDC::begin(unsigned long baud)
 {
-    setRxBufferSize(256);//default if not preset
     if(tx_sem == NULL){
         tx_sem = xSemaphoreCreateBinary();
         xSemaphoreTake(tx_sem, 0);
     }
+    setRxBufferSize(256);//default if not preset
+    devices[itf] = this;
 }
 
 void USBCDC::end()
 {
+    connected = false;
+    devices[itf] = NULL;
+    setRxBufferSize(0);
     if (tx_sem != NULL) {
         vSemaphoreDelete(tx_sem);
         tx_sem = NULL;
@@ -176,6 +186,11 @@ void USBCDC::_onUnplugged(void){
 enum { CDC_LINE_IDLE, CDC_LINE_1, CDC_LINE_2, CDC_LINE_3 };
 void USBCDC::_onLineState(bool _dtr, bool _rts){
     static uint8_t lineState = CDC_LINE_IDLE;
+
+    if(dtr == _dtr && rts == _rts){
+        return; // Skip duplicate events
+    }
+
     dtr = _dtr;
     rts = _rts;
 
@@ -183,6 +198,11 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
         if(!dtr && rts){
             if(lineState == CDC_LINE_IDLE){
                 lineState++;
+                if(connected){
+                    connected = false;
+                    arduino_usb_cdc_event_data_t p = {0};
+                    arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
+                }
             } else {
                 lineState = CDC_LINE_IDLE;
             }
@@ -212,7 +232,7 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
             connected = true;
             arduino_usb_cdc_event_data_t p = {0};
             arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_CONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
-        } else if(!dtr && !rts && connected){
+        } else if(!dtr && connected){
             connected = false;
             arduino_usb_cdc_event_data_t p = {0};
             arduino_usb_event_post(ARDUINO_USB_CDC_EVENTS, ARDUINO_USB_CDC_DISCONNECTED_EVENT, &p, sizeof(arduino_usb_cdc_event_data_t), portMAX_DELAY);
@@ -228,7 +248,7 @@ void USBCDC::_onLineState(bool _dtr, bool _rts){
 void USBCDC::_onLineCoding(uint32_t _bit_rate, uint8_t _stop_bits, uint8_t _parity, uint8_t _data_bits){
     if(bit_rate != _bit_rate || data_bits != _data_bits || stop_bits != _stop_bits || parity != _parity){
         // ArduinoIDE sends LineCoding with 1200bps baud to reset the device
-        if(_bit_rate == 1200){
+        if(reboot_enable && _bit_rate == 1200){
             usb_persist_restart(RESTART_BOOTLOADER);
         } else {
             bit_rate = _bit_rate;
@@ -317,7 +337,7 @@ size_t USBCDC::read(uint8_t *buffer, size_t size)
 
 void USBCDC::flush(void)
 {
-    if(itf >= MAX_USB_CDC_DEVICES){
+    if(itf >= MAX_USB_CDC_DEVICES || tx_sem == NULL){
         return;
     }
     tud_cdc_n_write_flush(itf);
@@ -325,7 +345,7 @@ void USBCDC::flush(void)
 
 int USBCDC::availableForWrite(void)
 {
-    if(itf >= MAX_USB_CDC_DEVICES){
+    if(itf >= MAX_USB_CDC_DEVICES || tx_sem == NULL){
         return -1;
     }
     return tud_cdc_n_write_available(itf);
@@ -364,7 +384,7 @@ USBCDC::operator bool() const
     return connected;
 }
 
-#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
+#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
 USBCDC Serial(0);
 #endif
 
diff --git a/cores/esp32/USBCDC.h b/cores/esp32/USBCDC.h
index d9a6376d316..26df8a3c81c 100644
--- a/cores/esp32/USBCDC.h
+++ b/cores/esp32/USBCDC.h
@@ -54,6 +54,7 @@ class USBCDC: public Stream
 {
 public:
     USBCDC(uint8_t itf=0);
+    ~USBCDC();
 
     void onEvent(esp_event_handler_t callback);
     void onEvent(arduino_usb_cdc_event_t event, esp_event_handler_t callback);
@@ -129,7 +130,7 @@ class USBCDC: public Stream
     
 };
 
-#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
+#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC
 extern USBCDC Serial;
 #endif
 
diff --git a/cores/esp32/USBMSC.cpp b/cores/esp32/USBMSC.cpp
new file mode 100644
index 00000000000..c6327ecdee0
--- /dev/null
+++ b/cores/esp32/USBMSC.cpp
@@ -0,0 +1,260 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp32-hal.h"
+#include "esp32-hal-tinyusb.h"
+#include "USBMSC.h"
+
+#if CFG_TUD_MSC
+extern "C" uint16_t tusb_msc_load_descriptor(uint8_t * dst, uint8_t * itf)
+{
+    uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MSC");
+    uint8_t ep_num = tinyusb_get_free_duplex_endpoint();
+    TU_VERIFY (ep_num != 0);
+    uint8_t descriptor[TUD_MSC_DESC_LEN] = {
+        // Interface number, string index, EP Out & EP In address, EP size
+        TUD_MSC_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), 64)
+    };
+    *itf+=1;
+    memcpy(dst, descriptor, TUD_MSC_DESC_LEN);
+    return TUD_MSC_DESC_LEN;
+}
+
+typedef struct {
+    bool media_present;
+    uint8_t vendor_id[8];
+    uint8_t product_id[16];
+    uint8_t product_rev[4];
+    uint16_t block_size;
+    uint32_t block_count;
+    bool (*start_stop)(uint8_t power_condition, bool start, bool load_eject);
+    int32_t (*read)(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
+    int32_t (*write)(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
+} msc_lun_t;
+
+static const uint8_t MSC_MAX_LUN = 3;
+static uint8_t MSC_ACTIVE_LUN = 0;
+static msc_lun_t msc_luns[MSC_MAX_LUN];
+
+static void cplstr(void *dst, const void * src, size_t max_len){
+    if(!src || !dst || !max_len){
+        return;
+    }
+    size_t l = strlen((const char *)src);
+    if(l > max_len){
+        l = max_len;
+    }
+    memcpy(dst, src, l);
+}
+
+// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
+uint8_t tud_msc_get_maxlun_cb(void)
+{
+    log_v("%u", MSC_ACTIVE_LUN);
+    return MSC_ACTIVE_LUN;
+}
+
+// Invoked when received SCSI_CMD_INQUIRY
+// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
+{
+    log_v("[%u]", lun);
+    cplstr(vendor_id  , msc_luns[lun].vendor_id, 8);
+    cplstr(product_id , msc_luns[lun].product_id, 16);
+    cplstr(product_rev, msc_luns[lun].product_rev, 4);
+}
+
+// Invoked when received Test Unit Ready command.
+// return true allowing host to read/write this LUN e.g SD card inserted
+bool tud_msc_test_unit_ready_cb(uint8_t lun)
+{
+    log_v("[%u]: %u", lun, msc_luns[lun].media_present);
+    return msc_luns[lun].media_present; // RAM disk is always ready
+}
+
+// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
+// Application update block count and block size
+void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
+{
+    log_v("[%u]", lun);
+    if(!msc_luns[lun].media_present){
+        *block_count = 0;
+        *block_size  = 0;
+        return;
+    }
+
+    *block_count = msc_luns[lun].block_count;
+    *block_size  = msc_luns[lun].block_size;
+}
+
+// Invoked when received Start Stop Unit command
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
+{
+    log_v("[%u] power: %u, start: %u, eject: %u", lun, power_condition, start, load_eject);
+    if(msc_luns[lun].start_stop){
+        return msc_luns[lun].start_stop(power_condition, start, load_eject);
+    }
+    return true;
+}
+
+// Callback invoked when received READ10 command.
+// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
+{
+    log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
+    if(!msc_luns[lun].media_present){
+        return 0;
+    }
+    if(msc_luns[lun].read){
+        return msc_luns[lun].read(lba, offset, buffer, bufsize);
+    }
+    return 0;
+}
+
+// Callback invoked when received WRITE10 command.
+// Process data in buffer to disk's storage and return number of written bytes
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
+{
+    log_v("[%u], lba: %u, offset: %u, bufsize: %u", lun, lba, offset, bufsize);
+    if(!msc_luns[lun].media_present){
+        return 0;
+    }
+    if(msc_luns[lun].write){
+        return msc_luns[lun].write(lba, offset, buffer, bufsize);
+    }
+    return 0;
+}
+
+// Callback invoked when received an SCSI command not in built-in list below
+// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
+// - READ10 and WRITE10 has their own callbacks
+int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
+{
+    // read10 & write10 has their own callback and MUST not be handled here
+    log_v("[%u] cmd: %u, bufsize: %u", lun, scsi_cmd[0], bufsize);
+
+    void const* response = NULL;
+    uint16_t resplen = 0;
+
+    // most scsi handled is input
+    bool in_xfer = true;
+    
+    if(!msc_luns[lun].media_present){
+        return -1;
+    }
+
+    switch (scsi_cmd[0]) {
+        case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+            // Host is about to read/write etc ... better not to disconnect disk
+            resplen = 0;
+            break;
+
+        default:
+            // Set Sense = Invalid Command Operation
+            tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+
+            // negative means error -> tinyusb could stall and/or response with failed status
+            resplen = -1;
+            break;
+    }
+
+    // return resplen must not larger than bufsize
+    if (resplen > bufsize) resplen = bufsize;
+
+    if (response && (resplen > 0)) {
+        if (in_xfer) {
+            memcpy(buffer, response, resplen);
+        } else {
+            // SCSI output
+        }
+    }
+
+    return resplen;
+}
+
+USBMSC::USBMSC(){
+    if(MSC_ACTIVE_LUN < MSC_MAX_LUN){
+        _lun = MSC_ACTIVE_LUN;
+        MSC_ACTIVE_LUN++;
+        msc_luns[_lun].media_present = false;
+        msc_luns[_lun].vendor_id[0] = 0;
+        msc_luns[_lun].product_id[0] = 0;
+        msc_luns[_lun].product_rev[0] = 0;
+        msc_luns[_lun].block_size = 0;
+        msc_luns[_lun].block_count = 0;
+        msc_luns[_lun].start_stop = NULL;
+        msc_luns[_lun].read = NULL;
+        msc_luns[_lun].write = NULL;
+    }
+    if(_lun == 0){
+        tinyusb_enable_interface(USB_INTERFACE_MSC, TUD_MSC_DESC_LEN, tusb_msc_load_descriptor);
+    }
+}
+
+USBMSC::~USBMSC(){
+  end();
+}
+
+bool USBMSC::begin(uint32_t block_count, uint16_t block_size){
+    msc_luns[_lun].block_size = block_size;
+    msc_luns[_lun].block_count = block_count;
+    if(!msc_luns[_lun].block_size || !msc_luns[_lun].block_count || !msc_luns[_lun].read || !msc_luns[_lun].write){
+        return false;
+    }
+    return true;
+}
+
+void USBMSC::end(){
+    msc_luns[_lun].media_present = false;
+    msc_luns[_lun].vendor_id[0] = 0;
+    msc_luns[_lun].product_id[0] = 0;
+    msc_luns[_lun].product_rev[0] = 0;
+    msc_luns[_lun].block_size = 0;
+    msc_luns[_lun].block_count = 0;
+    msc_luns[_lun].start_stop = NULL;
+    msc_luns[_lun].read = NULL;
+    msc_luns[_lun].write = NULL;
+}
+
+void USBMSC::vendorID(const char * vid){
+    cplstr(msc_luns[_lun].vendor_id, vid, 8);
+}
+
+void USBMSC::productID(const char * pid){
+    cplstr(msc_luns[_lun].product_id, pid, 16);
+}
+
+void USBMSC::productRevision(const char * rev){
+    cplstr(msc_luns[_lun].product_rev, rev, 4);
+}
+
+void USBMSC::onStartStop(msc_start_stop_cb cb){
+    msc_luns[_lun].start_stop = cb;
+}
+
+void USBMSC::onRead(msc_read_cb cb){
+    msc_luns[_lun].read = cb;
+}
+
+void USBMSC::onWrite(msc_write_cb cb){
+    msc_luns[_lun].write = cb;
+}
+
+void USBMSC::mediaPresent(bool media_present){
+    msc_luns[_lun].media_present = media_present;
+}
+
+#endif /* CONFIG_USB_MSC_ENABLED */
diff --git a/cores/esp32/USBMSC.h b/cores/esp32/USBMSC.h
new file mode 100644
index 00000000000..799322a30b5
--- /dev/null
+++ b/cores/esp32/USBMSC.h
@@ -0,0 +1,49 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp32-hal.h"
+#if CONFIG_TINYUSB_MSC_ENABLED
+
+// Invoked when received Start Stop Unit command
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
+typedef bool (*msc_start_stop_cb)(uint8_t power_condition, bool start, bool load_eject);
+
+// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
+typedef int32_t (*msc_read_cb)(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
+
+// Process data in buffer to disk's storage and return number of written bytes
+typedef int32_t (*msc_write_cb)(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
+
+class USBMSC
+{
+public:
+    USBMSC();
+    ~USBMSC();
+    bool begin(uint32_t block_count, uint16_t block_size);
+    void end();
+    void vendorID(const char * vid);//max 8 chars
+    void productID(const char * pid);//max 16 chars
+    void productRevision(const char * ver);//max 4 chars
+    void mediaPresent(bool media_present);
+    void onStartStop(msc_start_stop_cb cb);
+    void onRead(msc_read_cb cb);
+    void onWrite(msc_write_cb cb);
+private:
+	uint8_t _lun;
+};
+#endif
diff --git a/cores/esp32/esp32-hal-tinyusb.c b/cores/esp32/esp32-hal-tinyusb.c
index 6e8ad868301..0da912b0210 100644
--- a/cores/esp32/esp32-hal-tinyusb.c
+++ b/cores/esp32/esp32-hal-tinyusb.c
@@ -72,20 +72,15 @@ static void configure_pins(usb_hal_context_t *usb)
 
 esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
 {
-    log_i("Driver installation...");
-
-    // Hal init
     usb_hal_context_t hal = {
         .use_external_phy = config->external_phy
     };
     usb_hal_init(&hal);
     configure_pins(&hal);
-
     if (!tusb_init()) {
         log_e("Can't initialize the TinyUSB stack.");
         return ESP_FAIL;
     }
-    log_i("Driver installed");
     return ESP_OK;
 }
 
@@ -106,6 +101,7 @@ static tusb_str_t WEBUSB_URL              = "";
 static tusb_str_t USB_DEVICE_PRODUCT      = "";
 static tusb_str_t USB_DEVICE_MANUFACTURER = "";
 static tusb_str_t USB_DEVICE_SERIAL       = "";
+static tusb_str_t USB_DEVICE_LANGUAGE     = "\x09\x04";//English (0x0409)
 
 static uint8_t USB_DEVICE_ATTRIBUTES     = 0;
 static uint16_t USB_DEVICE_POWER         = 0;
@@ -140,7 +136,7 @@ static tusb_desc_device_t tinyusb_device_descriptor = {
 static uint32_t tinyusb_string_descriptor_len = 4;
 static char * tinyusb_string_descriptor[MAX_STRING_DESCRIPTORS] = {
         // array of pointer to string descriptors
-        "\x09\x04",   // 0: is supported language is English (0x0409)
+        USB_DEVICE_LANGUAGE,    // 0: is supported language
         USB_DEVICE_MANUFACTURER,// 1: Manufacturer
         USB_DEVICE_PRODUCT,     // 2: Product
         USB_DEVICE_SERIAL,      // 3: Serials, should use chip ID
@@ -563,31 +559,37 @@ static void usb_device_task(void *param) {
 /*
  * PUBLIC API
  * */
+static const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "MIDI", "CUSTOM"};
+
+static bool tinyusb_is_initialized = false;
 
 esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb)
 {
+    if(tinyusb_is_initialized){
+        log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
+        return ESP_FAIL;
+    }
     if((interface >= USB_INTERFACE_MAX) || (tinyusb_loaded_interfaces_mask & (1U << interface))){
-        log_e("Interface %u not enabled", interface);
+        log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
         return ESP_FAIL;
     }
     tinyusb_loaded_interfaces_mask |= (1U << interface);
     tinyusb_config_descriptor_len += descriptor_len;
     tinyusb_loaded_interfaces_callbacks[interface] = cb;
-    log_d("Interface %u enabled", interface);
+    log_d("Interface %s enabled", tinyusb_interface_names[interface]);
     return ESP_OK;
 }
 
 esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
-    static bool initialized = false;
-    if(initialized){
+    if(tinyusb_is_initialized){
         return ESP_OK;
     }
-    initialized = true;
+    tinyusb_is_initialized = true;
     
     tinyusb_endpoints.val = 0;
     tinyusb_apply_device_config(config);
     if (!tinyusb_load_enabled_interfaces()) {
-        initialized = false;
+        tinyusb_is_initialized = false;
         return ESP_FAIL;
     }
 
@@ -605,7 +607,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
     }
 
     if (esp_register_shutdown_handler(usb_persist_shutdown_handler) != ESP_OK) {
-        initialized = false;
+        tinyusb_is_initialized = false;
         return ESP_FAIL;
     }
 
@@ -614,7 +616,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
     };
     esp_err_t err = tinyusb_driver_install(&tusb_cfg);
     if (err != ESP_OK) {
-        initialized = false;
+        tinyusb_is_initialized = false;
         return err;
     }
     xTaskCreate(usb_device_task, "usbd", 4096, NULL, configMAX_PRIORITIES - 1, NULL);
@@ -690,84 +692,4 @@ uint8_t tinyusb_get_free_out_endpoint(void){
     return 0;
 }
 
-/*
-void usb_dw_reg_dump(void)
-{
-#define USB_PRINT_REG(r) printf("USB0." #r " = 0x%x;\n", USB0.r)
-#define USB_PRINT_IREG(i, r) printf("USB0.in_ep_reg[%u]." #r " = 0x%x;\n", i, USB0.in_ep_reg[i].r)
-#define USB_PRINT_OREG(i, r) printf("USB0.out_ep_reg[%u]." #r " = 0x%x;\n", i, USB0.out_ep_reg[i].r)
-    uint8_t i;
-    USB_PRINT_REG(gotgctl);
-    USB_PRINT_REG(gotgint);
-    USB_PRINT_REG(gahbcfg);
-    USB_PRINT_REG(gusbcfg);
-    USB_PRINT_REG(grstctl);
-    USB_PRINT_REG(gintsts);
-    USB_PRINT_REG(gintmsk);
-    USB_PRINT_REG(grxstsr);
-    USB_PRINT_REG(grxstsp);
-    USB_PRINT_REG(grxfsiz);
-    USB_PRINT_REG(gnptxsts);
-    USB_PRINT_REG(gpvndctl);
-    USB_PRINT_REG(ggpio);
-    USB_PRINT_REG(guid);
-    USB_PRINT_REG(gsnpsid);
-    USB_PRINT_REG(ghwcfg1);
-    USB_PRINT_REG(ghwcfg2);
-    USB_PRINT_REG(ghwcfg3);
-    USB_PRINT_REG(ghwcfg4);
-    USB_PRINT_REG(glpmcfg);
-    USB_PRINT_REG(gpwrdn);
-    USB_PRINT_REG(gdfifocfg);
-    USB_PRINT_REG(gadpctl);
-    USB_PRINT_REG(hptxfsiz);
-    USB_PRINT_REG(hcfg);
-    USB_PRINT_REG(hfir);
-    USB_PRINT_REG(hfnum);
-    USB_PRINT_REG(hptxsts);
-    USB_PRINT_REG(haint);
-    USB_PRINT_REG(haintmsk);
-    USB_PRINT_REG(hflbaddr);
-    USB_PRINT_REG(hprt);
-    USB_PRINT_REG(dcfg);
-    USB_PRINT_REG(dctl);
-    USB_PRINT_REG(dsts);
-    USB_PRINT_REG(diepmsk);
-    USB_PRINT_REG(doepmsk);
-    USB_PRINT_REG(daint);
-    USB_PRINT_REG(daintmsk);
-    USB_PRINT_REG(dtknqr1);
-    USB_PRINT_REG(dtknqr2);
-    USB_PRINT_REG(dvbusdis);
-    USB_PRINT_REG(dvbuspulse);
-    USB_PRINT_REG(dtknqr3_dthrctl);
-    USB_PRINT_REG(dtknqr4_fifoemptymsk);
-    USB_PRINT_REG(deachint);
-    USB_PRINT_REG(deachintmsk);
-    USB_PRINT_REG(pcgctrl);
-    USB_PRINT_REG(pcgctrl1);
-    USB_PRINT_REG(gnptxfsiz);
-    for (i = 0; i < 4; i++) {
-        printf("USB0.dieptxf[%u] = 0x%x;\n", i, USB0.dieptxf[i]);
-    }
-//    for (i = 0; i < 16; i++) {
-//        printf("USB0.diepeachintmsk[%u] = 0x%x;\n", i, USB0.diepeachintmsk[i]);
-//    }
-//    for (i = 0; i < 16; i++) {
-//        printf("USB0.doepeachintmsk[%u] = 0x%x;\n", i, USB0.doepeachintmsk[i]);
-//    }
-    for (i = 0; i < 7; i++) {
-        printf("// EP %u:\n", i);
-        USB_PRINT_IREG(i, diepctl);
-        USB_PRINT_IREG(i, diepint);
-        USB_PRINT_IREG(i, dieptsiz);
-        USB_PRINT_IREG(i, diepdma);
-        USB_PRINT_IREG(i, dtxfsts);
-        USB_PRINT_OREG(i, doepctl);
-        USB_PRINT_OREG(i, doepint);
-        USB_PRINT_OREG(i, doeptsiz);
-        USB_PRINT_OREG(i, doepdma);
-    }
-}
- */
 #endif /* CONFIG_TINYUSB_ENABLED */
diff --git a/cores/esp32/esp32-hal-tinyusb.h b/cores/esp32/esp32-hal-tinyusb.h
index abef6410b13..7332887a2b0 100644
--- a/cores/esp32/esp32-hal-tinyusb.h
+++ b/cores/esp32/esp32-hal-tinyusb.h
@@ -82,11 +82,11 @@ void usb_persist_restart(restart_type_t mode);
 
 // The following definitions and functions are to be used only by the drivers
 typedef enum {
-    USB_INTERFACE_CDC,
+    USB_INTERFACE_MSC,
     USB_INTERFACE_DFU,
     USB_INTERFACE_HID,
     USB_INTERFACE_VENDOR,
-    USB_INTERFACE_MSC,
+    USB_INTERFACE_CDC,
     USB_INTERFACE_MIDI,
     USB_INTERFACE_CUSTOM,
     USB_INTERFACE_MAX
diff --git a/cores/esp32/firmware_msc_fat.c b/cores/esp32/firmware_msc_fat.c
new file mode 100644
index 00000000000..bc7ad550b22
--- /dev/null
+++ b/cores/esp32/firmware_msc_fat.c
@@ -0,0 +1,204 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "firmware_msc_fat.h"
+//copy up to max_len chars from src to dst and do not terminate
+static size_t cplstr(void *dst, const void * src, size_t max_len){
+    if(!src || !dst || !max_len){
+        return 0;
+    }
+    size_t l = strlen((const char *)src);
+    if(l > max_len){
+        l = max_len;
+    }
+    memcpy(dst, src, l);
+    return l;
+}
+
+//copy up to max_len chars from src to dst, adding spaces up to max_len. do not terminate
+static void cplstrsp(void *dst, const void * src, size_t max_len){
+    size_t l = cplstr(dst, src, max_len);
+    for(l; l < max_len; l++){
+      ((uint8_t*)dst)[l] = 0x20;
+    }
+}
+
+// FAT12
+static const char * FAT12_FILE_SYSTEM_TYPE = "FAT12";
+
+static uint16_t fat12_sectors_per_alloc_table(uint32_t sector_num){
+  uint32_t required_bytes = (((sector_num * 3)+1)/2);
+  return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & DISK_SECTOR_SIZE)?1:0);
+}
+
+static uint8_t * fat12_add_table(uint8_t * dst, fat_boot_sector_t * boot){
+  memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE);
+  uint8_t * d = dst + DISK_SECTOR_SIZE;
+  d[0] = 0xF8;
+  d[1] = 0xFF;
+  d[2] = 0xFF;
+  return d;
+}
+
+static void fat12_set_table_index(uint8_t * table, uint16_t index, uint16_t value){
+  uint16_t offset = (index >> 1) * 3;
+  uint8_t * data = table + offset;
+  if(index & 1){
+    data[2] = (value >> 4) & 0xFF;
+    data[1] = (data[1] & 0xF) | ((value & 0xF) << 4);
+  } else {
+    data[0] = value & 0xFF;
+    data[1] = (data[1] & 0xF0) | ((value >> 8) & 0xF);
+  }
+}
+
+//FAT16
+static const char * FAT16_FILE_SYSTEM_TYPE = "FAT16";
+
+static uint16_t fat16_sectors_per_alloc_table(uint32_t sector_num){
+  uint32_t required_bytes = sector_num * 2;
+  return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & DISK_SECTOR_SIZE)?1:0);
+}
+
+static uint8_t * fat16_add_table(uint8_t * dst, fat_boot_sector_t * boot){
+  memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE);
+  uint16_t * d = (uint16_t *)(dst + DISK_SECTOR_SIZE);
+  d[0] = 0xFFF8;
+  d[1] = 0xFFFF;
+  return (uint8_t *)d;
+}
+
+static void fat16_set_table_index(uint8_t * table, uint16_t index, uint16_t value){
+  uint16_t offset = index * 2;
+  *(uint16_t *)(table + offset) = value;
+}
+
+//Interface
+const char * fat_file_system_type(bool fat16) {
+	return ((fat16)?FAT16_FILE_SYSTEM_TYPE:FAT12_FILE_SYSTEM_TYPE);
+}
+
+uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16){
+  if(fat16){
+    return fat16_sectors_per_alloc_table(sector_num);
+  }
+  return fat12_sectors_per_alloc_table(sector_num);
+}
+
+uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16){
+  if(fat16){
+    return fat16_add_table(dst, boot);
+  }
+  return fat12_add_table(dst, boot);
+}
+
+void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16){
+  if(fat16){
+    fat16_set_table_index(table, index, value);
+  } else {
+    fat12_set_table_index(table, index, value);
+  }
+}
+
+fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number){
+  fat_boot_sector_t *boot = (fat_boot_sector_t*)dst;
+  boot->jump_instruction[0] = 0xEB;
+  boot->jump_instruction[1] = 0x3C;
+  boot->jump_instruction[2] = 0x90;
+  cplstr(boot->oem_name, "MSDOS5.0", 8);
+  boot->bytes_per_sector = DISK_SECTOR_SIZE;
+  boot->sectors_per_cluster = 1;
+  boot->reserved_sectors_count = 1;
+  boot->file_alloc_tables_num = 1;
+  boot->max_root_dir_entries = 16;
+  boot->fat12_sector_num = sector_num;
+  boot->media_descriptor = 0xF8;
+  boot->sectors_per_alloc_table = table_sectors;
+  boot->sectors_per_track = 1;
+  boot->num_heads = 1;
+  boot->hidden_sectors_count = 0;
+  boot->total_sectors_32 = 0;
+  boot->physical_drive_number = 0x00;
+  boot->reserved0 = 0x00;
+  boot->extended_boot_signature = 0x29;
+  boot->serial_number = serial_number;
+  cplstrsp(boot->volume_label, volume_label, 11);
+  memset(boot->reserved, 0, 448);
+  cplstrsp(boot->file_system_type, file_system_type, 8);
+  boot->signature = 0xAA55;
+  return boot;
+}
+
+fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label){
+  fat_boot_sector_t * boot = (fat_boot_sector_t *)dst;
+  fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE));
+  memset(entry, 0, sizeof(fat_dir_entry_t));
+  cplstrsp(entry->volume_label, volume_label, 11);
+  entry->file_attr = FAT_FILE_ATTR_VOLUME_LABEL;
+  return entry;
+}
+
+fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16){
+  fat_boot_sector_t * boot = (fat_boot_sector_t *)dst;
+  uint8_t * table = dst + DISK_SECTOR_SIZE;
+  fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t)));
+  memset(entry, 0, sizeof(fat_dir_entry_t));
+  cplstrsp(entry->file_name, file_name, 8);
+  cplstrsp(entry->file_extension, file_extension, 3);
+  entry->file_attr = FAT_FILE_ATTR_ARCHIVE;
+  entry->file_size = file_size;
+  entry->data_start_sector = data_start_sector;
+  entry->extended_attr = 0;
+
+  uint16_t file_sectors = file_size / DISK_SECTOR_SIZE;
+  if(file_size % DISK_SECTOR_SIZE){
+    file_sectors++;
+  }
+
+  uint16_t data_end_sector = data_start_sector + file_sectors;
+  for(uint16_t i=data_start_sector; i<(data_end_sector-1); i++){
+    fat_set_table_index(table, i, i+1, is_fat16);
+  }
+  fat_set_table_index(table, data_end_sector-1, 0xFFFF, is_fat16);
+
+  //Set Firmware Date based on the build time
+  static const char * month_names_short[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+  char mstr[8] = {'\0',};
+  const char *str = __DATE__ " " __TIME__;
+  int ms=0, seconds=0, minutes=0, hours=0, year=0, date=0, month=0;
+  int r = sscanf(str,"%s %d %d %d:%d:%d", mstr, &date, &year, &hours, &minutes, &seconds);
+  if(r >= 0){
+    for(int i=0; i<12; i++){
+      if(!strcmp(mstr, month_names_short[i])){
+        month = i;
+        break;
+      }
+    }
+    entry->creation_time_ms = FAT_MS2V(seconds, ms);
+    entry->creation_time_hms = FAT_HMS2V(hours, minutes, seconds);
+    entry->creation_time_ymd = FAT_YMD2V(year, month, date);
+    entry->last_access_ymd = entry->creation_time_ymd;
+    entry->last_modified_hms = entry->creation_time_hms;
+    entry->last_modified_ymd = entry->creation_time_ymd;
+  }
+  return entry;
+}
+
+uint8_t fat_lfn_checksum(const uint8_t *short_filename){
+   uint8_t sum = 0;
+   for (uint8_t i = 11; i; i--) {
+      sum = ((sum & 1) << 7) + (sum >> 1) + *short_filename++;
+   }
+   return sum;
+}
diff --git a/cores/esp32/firmware_msc_fat.h b/cores/esp32/firmware_msc_fat.h
new file mode 100644
index 00000000000..dd88cdc7a0d
--- /dev/null
+++ b/cores/esp32/firmware_msc_fat.h
@@ -0,0 +1,141 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#define FAT_U8(v) ((v) & 0xFF)
+#define FAT_U16(v) FAT_U8(v), FAT_U8((v) >> 8)
+#define FAT_U32(v) FAT_U8(v), FAT_U8((v) >> 8), FAT_U8((v) >> 16), FAT_U8((v) >> 24)
+
+#define FAT12_TBL2B(l,h)    FAT_U8(l), FAT_U8(((l >> 8) & 0xF) | ((h << 4) & 0xF0)), FAT_U8(h >> 4)
+
+#define FAT_MS2B(s,ms)    FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
+#define FAT_HMS2B(h,m,s)  FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)),      FAT_U8((((m) >> 3) & 0x7)|((h) << 3))
+#define FAT_YMD2B(y,m,d)  FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)),    FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1))
+
+#define FAT_MS2V(s,ms)    FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
+#define FAT_HMS2V(h,m,s)  (FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x7)|((h) << 3)) << 8))
+#define FAT_YMD2V(y,m,d)  (FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1)) << 8))
+
+#define FAT_B2HMS(hms)    ((hms >> 11) & 0x1F), ((hms >> 5) & 0x3F), ((hms & 0x1F) << 1)
+#define FAT_B2YMD(ymd)    (((ymd >> 9) & 0x7F) + 1980), ((ymd >> 5) & 0x0F), (ymd & 0x1F)
+
+#define FAT_FILE_ATTR_READ_ONLY     0x01
+#define FAT_FILE_ATTR_HIDDEN        0x02
+#define FAT_FILE_ATTR_SYSTEM        0x04
+#define FAT_FILE_ATTR_VOLUME_LABEL  0x08
+#define FAT_FILE_ATTR_SUBDIRECTORY  0x10
+#define FAT_FILE_ATTR_ARCHIVE       0x20
+#define FAT_FILE_ATTR_DEVICE        0x40
+
+static const uint16_t DISK_SECTOR_SIZE = 512;
+
+#define FAT_SIZE_TO_SECTORS(bytes) ((bytes) / DISK_SECTOR_SIZE) + (((bytes) % DISK_SECTOR_SIZE)?1:0)
+
+typedef struct __attribute__ ((packed)) {
+  uint8_t jump_instruction[3];
+  char oem_name[8];//padded with spaces (0x20)
+  uint16_t bytes_per_sector;//DISK_SECTOR_SIZE usually 512
+  uint8_t sectors_per_cluster;//Allowed values are 1, 2, 4, 8, 16, 32, 64, and 128
+  uint16_t reserved_sectors_count;//At least 1 for this sector, usually 32 for FAT32
+  uint8_t file_alloc_tables_num;//Almost always 2; RAM disks might use 1
+  uint16_t max_root_dir_entries;//FAT12 and FAT16
+  uint16_t fat12_sector_num;//DISK_SECTOR_NUM FAT12 and FAT16
+  uint8_t media_descriptor;
+  uint16_t sectors_per_alloc_table;//FAT12 and FAT16
+  uint16_t sectors_per_track;//A value of 0 may indicate LBA-only access
+  uint16_t num_heads;
+  uint32_t hidden_sectors_count;
+  uint32_t total_sectors_32;
+  uint8_t physical_drive_number;//0x00 for (first) removable media, 0x80 for (first) fixed disk
+  uint8_t reserved0;
+  uint8_t extended_boot_signature;//should be 0x29
+  uint32_t serial_number;//0x1234 => 1234
+  char volume_label[11];//padded with spaces (0x20)
+  char file_system_type[8];//padded with spaces (0x20)
+  uint8_t reserved[448];
+  uint16_t signature;//should be 0xAA55
+} fat_boot_sector_t;
+
+typedef struct __attribute__ ((packed)) {
+  union {
+    struct {
+      char file_name[8];//padded with spaces (0x20)
+      char file_extension[3];//padded with spaces (0x20)
+    };
+    struct {
+      uint8_t file_magic;// 0xE5:deleted, 0x05:will_be_deleted, 0x00:end_marker, 0x2E:dot_marker(. or ..)
+      char file_magic_data[10];
+    };
+    char volume_label[11];//padded with spaces (0x20)
+  };
+  uint8_t file_attr;//mask of FAT_FILE_ATTR_*
+  uint8_t reserved;//always 0
+  uint8_t creation_time_ms;//ms * 10; max 1990 (1s 990ms)
+  uint16_t creation_time_hms; // [5:6:5] => h:m:(s/2)
+  uint16_t creation_time_ymd; // [7:4:5] => (y+1980):m:d
+  uint16_t last_access_ymd;
+  uint16_t extended_attr;
+  uint16_t last_modified_hms;
+  uint16_t last_modified_ymd;
+  uint16_t data_start_sector;
+  uint32_t file_size;
+} fat_dir_entry_t;
+
+typedef struct __attribute__ ((packed)) {
+  union {
+    struct {
+      uint8_t number:5;
+      uint8_t reserved0:1;
+      uint8_t llfp:1;
+      uint8_t reserved1:1;
+    } seq;
+    uint8_t seq_num; //0xE5: Deleted Entry
+  };
+  uint16_t name0[5];
+  uint8_t attr; //ALWAYS 0x0F
+  uint8_t type; //ALWAYS 0x00
+  uint8_t dos_checksum;
+  uint16_t name1[6];
+  uint16_t first_cluster; //ALWAYS 0x0000
+  uint16_t name2[2];
+} fat_lfn_entry_t;
+
+typedef union {
+  fat_dir_entry_t dir;
+  fat_lfn_entry_t lfn;
+} fat_entry_t;
+
+const char * fat_file_system_type(bool fat16);
+uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16);
+uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16);
+void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16);
+fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number);
+fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label);
+fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16);
+uint8_t fat_lfn_checksum(const uint8_t *short_filename);
+
+#ifdef __cplusplus
+ }
+#endif
diff --git a/cores/esp32/main.cpp b/cores/esp32/main.cpp
index 4e49f5b9580..903e80f1748 100644
--- a/cores/esp32/main.cpp
+++ b/cores/esp32/main.cpp
@@ -2,8 +2,11 @@
 #include "freertos/task.h"
 #include "esp_task_wdt.h"
 #include "Arduino.h"
-#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
+#if (ARDUINO_USB_CDC_ON_BOOT|ARDUINO_USB_MSC_ON_BOOT|ARDUINO_USB_DFU_ON_BOOT)
 #include "USB.h"
+#if ARDUINO_USB_MSC_ON_BOOT
+#include "FirmwareMSC.h"
+#endif
 #endif
 
 #ifndef ARDUINO_LOOP_STACK_SIZE
@@ -47,9 +50,17 @@ void loopTask(void *pvParameters)
 
 extern "C" void app_main()
 {
-#if ARDUINO_SERIAL_PORT //Serial used for USB CDC
-    USB.begin();
+#if ARDUINO_USB_CDC_ON_BOOT
     Serial.begin();
+#endif
+#if ARDUINO_USB_MSC_ON_BOOT
+    MSC_Update.begin();
+#endif
+#if ARDUINO_USB_DFU_ON_BOOT
+    USB.enableDFU();
+#endif
+#if ARDUINO_USB_ON_BOOT
+    USB.begin();
 #endif
     loopTaskWDTEnabled = false;
     initArduino();
diff --git a/libraries/USB/examples/FirmwareMSC/.skip.esp32 b/libraries/USB/examples/FirmwareMSC/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/FirmwareMSC/.skip.esp32c3 b/libraries/USB/examples/FirmwareMSC/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/FirmwareMSC/FirmwareMSC.ino b/libraries/USB/examples/FirmwareMSC/FirmwareMSC.ino
new file mode 100644
index 00000000000..afcd2783b40
--- /dev/null
+++ b/libraries/USB/examples/FirmwareMSC/FirmwareMSC.ino
@@ -0,0 +1,74 @@
+#include "USB.h"
+#include "FirmwareMSC.h"
+
+#if !ARDUINO_USB_MSC_ON_BOOT
+FirmwareMSC MSC_Update;
+#endif
+#if ARDUINO_USB_CDC_ON_BOOT
+#define HWSerial Serial0
+#define USBSerial Serial
+#else
+#define HWSerial Serial
+USBCDC USBSerial;
+#endif
+
+static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
+  if(event_base == ARDUINO_USB_EVENTS){
+    arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_STARTED_EVENT:
+        HWSerial.println("USB PLUGGED");
+        break;
+      case ARDUINO_USB_STOPPED_EVENT:
+        HWSerial.println("USB UNPLUGGED");
+        break;
+      case ARDUINO_USB_SUSPEND_EVENT:
+        HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
+        break;
+      case ARDUINO_USB_RESUME_EVENT:
+        HWSerial.println("USB RESUMED");
+        break;
+      
+      default:
+        break;
+    }
+  } else if(event_base == ARDUINO_FIRMWARE_MSC_EVENTS){
+    arduino_firmware_msc_event_data_t * data = (arduino_firmware_msc_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_FIRMWARE_MSC_START_EVENT:
+        HWSerial.println("MSC Update Start");
+        break;
+      case ARDUINO_FIRMWARE_MSC_WRITE_EVENT:
+        //HWSerial.printf("MSC Update Write %u bytes at offset %u\n", data->write.size, data->write.offset);
+        HWSerial.print(".");
+        break;
+      case ARDUINO_FIRMWARE_MSC_END_EVENT:
+        HWSerial.printf("\nMSC Update End: %u bytes\n", data->end.size);
+        break;
+      case ARDUINO_FIRMWARE_MSC_ERROR_EVENT:
+        HWSerial.printf("MSC Update ERROR! Progress: %u bytes\n", data->error.size);
+        break;
+      case ARDUINO_FIRMWARE_MSC_POWER_EVENT:
+        HWSerial.printf("MSC Update Power: power: %u, start: %u, eject: %u", data->power.power_condition, data->power.start, data->power.load_eject);
+        break;
+      
+      default:
+        break;
+    }
+  }
+}
+
+void setup() {
+  HWSerial.begin(115200);
+  HWSerial.setDebugOutput(true);
+
+  USB.onEvent(usbEventCallback);
+  MSC_Update.onEvent(usbEventCallback);
+  MSC_Update.begin();
+  USBSerial.begin();
+  USB.begin();
+}
+
+void loop() {
+  // put your main code here, to run repeatedly
+}
diff --git a/libraries/USB/examples/USBMSC/.skip.esp32 b/libraries/USB/examples/USBMSC/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/USBMSC/.skip.esp32c3 b/libraries/USB/examples/USBMSC/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/USBMSC/USBMSC.ino b/libraries/USB/examples/USBMSC/USBMSC.ino
new file mode 100644
index 00000000000..0249c3ce14e
--- /dev/null
+++ b/libraries/USB/examples/USBMSC/USBMSC.ino
@@ -0,0 +1,192 @@
+#include "USB.h"
+#include "USBMSC.h"
+
+#if ARDUINO_USB_CDC_ON_BOOT
+#define HWSerial Serial0
+#define USBSerial Serial
+#else
+#define HWSerial Serial
+USBCDC USBSerial;
+#endif
+
+USBMSC MSC;
+
+#define FAT_U8(v) ((v) & 0xFF)
+#define FAT_U16(v) FAT_U8(v), FAT_U8((v) >> 8)
+#define FAT_U32(v) FAT_U8(v), FAT_U8((v) >> 8), FAT_U8((v) >> 16), FAT_U8((v) >> 24)
+#define FAT_MS2B(s,ms)    FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10)
+#define FAT_HMS2B(h,m,s)  FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)),      FAT_U8((((m) >> 3) & 0x7)|((h) << 3))
+#define FAT_YMD2B(y,m,d)  FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)),    FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1))
+#define FAT_TBL2B(l,h)    FAT_U8(l), FAT_U8(((l >> 8) & 0xF) | ((h << 4) & 0xF0)), FAT_U8(h >> 4)
+
+#define README_CONTENTS "This is tinyusb's MassStorage Class demo.\r\n\r\nIf you find any bugs or get any questions, feel free to file an\r\nissue at github.com/hathach/tinyusb"
+
+static const uint32_t DISK_SECTOR_COUNT = 2 * 8; // 8KB is the smallest size that windows allow to mount
+static const uint16_t DISK_SECTOR_SIZE = 512;    // Should be 512
+static const uint16_t DISC_SECTORS_PER_TABLE = 1; //each table sector can fit 170KB (340 sectors)
+
+static uint8_t msc_disk[DISK_SECTOR_COUNT][DISK_SECTOR_SIZE] =
+{
+  //------------- Block0: Boot Sector -------------//
+  {
+    // Header (62 bytes)
+    0xEB, 0x3C, 0x90, //jump_instruction
+    'M' , 'S' , 'D' , 'O' , 'S' , '5' , '.' , '0' , //oem_name
+    FAT_U16(DISK_SECTOR_SIZE), //bytes_per_sector
+    FAT_U8(1),    //sectors_per_cluster
+    FAT_U16(1),   //reserved_sectors_count
+    FAT_U8(1),    //file_alloc_tables_num
+    FAT_U16(16),  //max_root_dir_entries
+    FAT_U16(DISK_SECTOR_COUNT), //fat12_sector_num
+    0xF8,         //media_descriptor
+    FAT_U16(DISC_SECTORS_PER_TABLE),   //sectors_per_alloc_table;//FAT12 and FAT16
+    FAT_U16(1),   //sectors_per_track;//A value of 0 may indicate LBA-only access
+    FAT_U16(1),   //num_heads
+    FAT_U32(0),   //hidden_sectors_count
+    FAT_U32(0),   //total_sectors_32
+    0x00,         //physical_drive_number;0x00 for (first) removable media, 0x80 for (first) fixed disk
+    0x00,         //reserved
+    0x29,         //extended_boot_signature;//should be 0x29
+    FAT_U32(0x1234), //serial_number: 0x1234 => 1234
+    'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , //volume_label padded with spaces (0x20)
+    'F' , 'A' , 'T' , '1' , '2' , ' ' , ' ' , ' ' ,  //file_system_type padded with spaces (0x20)
+
+    // Zero up to 2 last bytes of FAT magic code (448 bytes)
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+     //boot signature (2 bytes)
+    0x55, 0xAA
+  },
+
+  //------------- Block1: FAT12 Table -------------//
+  {
+    FAT_TBL2B(0xFF8, 0xFFF), FAT_TBL2B(0xFFF, 0x000) // first 2 entries must be 0xFF8 0xFFF, third entry is cluster end of readme file
+  },
+
+  //------------- Block2: Root Directory -------------//
+  {
+    // first entry is volume label
+    'E' , 'S' , 'P' , '3' , '2' , 'S' , '2' , ' ' , 
+    'M' , 'S' , 'C' , 
+    0x08, //FILE_ATTR_VOLUME_LABEL
+    0x00, 
+    FAT_MS2B(0,0), 
+    FAT_HMS2B(0,0,0),
+    FAT_YMD2B(0,0,0), 
+    FAT_YMD2B(0,0,0), 
+    FAT_U16(0), 
+    FAT_HMS2B(13,42,30),  //last_modified_hms
+    FAT_YMD2B(2018,11,5), //last_modified_ymd
+    FAT_U16(0), 
+    FAT_U32(0),
+    
+    // second entry is readme file
+    'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' ,//file_name[8]; padded with spaces (0x20)
+    'T' , 'X' , 'T' ,     //file_extension[3]; padded with spaces (0x20)
+    0x20,                 //file attributes: FILE_ATTR_ARCHIVE
+    0x00,                 //ignore
+    FAT_MS2B(1,980),      //creation_time_10_ms (max 199x10 = 1s 990ms)
+    FAT_HMS2B(13,42,36),  //create_time_hms [5:6:5] => h:m:(s/2)
+    FAT_YMD2B(2018,11,5), //create_time_ymd [7:4:5] => (y+1980):m:d
+    FAT_YMD2B(2020,11,5), //last_access_ymd
+    FAT_U16(0),           //extended_attributes
+    FAT_HMS2B(13,44,16),  //last_modified_hms
+    FAT_YMD2B(2019,11,5), //last_modified_ymd
+    FAT_U16(2),           //start of file in cluster
+    FAT_U32(sizeof(README_CONTENTS) - 1) //file size
+  },
+
+  //------------- Block3: Readme Content -------------//
+  README_CONTENTS
+};
+
+static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){
+  HWSerial.printf("MSC WRITE: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize);
+  memcpy(msc_disk[lba] + offset, buffer, bufsize);
+  return bufsize;
+}
+
+static int32_t onRead(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){
+  HWSerial.printf("MSC READ: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize);
+  memcpy(buffer, msc_disk[lba] + offset, bufsize);
+  return bufsize;
+}
+
+static bool onStartStop(uint8_t power_condition, bool start, bool load_eject){
+  HWSerial.printf("MSC START/STOP: power: %u, start: %u, eject: %u\n", power_condition, start, load_eject);
+  return true;
+}
+
+static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
+  if(event_base == ARDUINO_USB_EVENTS){
+    arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_STARTED_EVENT:
+        HWSerial.println("USB PLUGGED");
+        break;
+      case ARDUINO_USB_STOPPED_EVENT:
+        HWSerial.println("USB UNPLUGGED");
+        break;
+      case ARDUINO_USB_SUSPEND_EVENT:
+        HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
+        break;
+      case ARDUINO_USB_RESUME_EVENT:
+        HWSerial.println("USB RESUMED");
+        break;
+      
+      default:
+        break;
+    }
+  }
+}
+
+void setup() {
+  HWSerial.begin(115200);
+  HWSerial.setDebugOutput(true);
+
+  USB.onEvent(usbEventCallback);
+  MSC.vendorID("ESP32");//max 8 chars
+  MSC.productID("USB_MSC");//max 16 chars
+  MSC.productRevision("1.0");//max 4 chars
+  MSC.onStartStop(onStartStop);
+  MSC.onRead(onRead);
+  MSC.onWrite(onWrite);
+  MSC.mediaPresent(true);
+  MSC.begin(DISK_SECTOR_COUNT, DISK_SECTOR_SIZE);
+  USBSerial.begin();
+  USB.begin();
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+}
diff --git a/libraries/USB/examples/USBSerial/USBSerial.ino b/libraries/USB/examples/USBSerial/USBSerial.ino
index c3094fd8713..bb23418f507 100644
--- a/libraries/USB/examples/USBSerial/USBSerial.ino
+++ b/libraries/USB/examples/USBSerial/USBSerial.ino
@@ -1,6 +1,6 @@
 #include "USB.h"
 
-#if ARDUINO_SERIAL_PORT
+#if ARDUINO_USB_CDC_ON_BOOT
 #define HWSerial Serial0
 #define USBSerial Serial
 #else
@@ -66,14 +66,8 @@ void setup() {
   USB.onEvent(usbEventCallback);
   USBSerial.onEvent(usbEventCallback);
   
-#if !ARDUINO_SERIAL_PORT
-  USB.enableDFU();
-  USB.webUSB(true);
-  USB.webUSBURL("http://localhost/webusb");
-  USB.productName("ESP32S2-USB");
-  USB.begin();
   USBSerial.begin();
-#endif
+  USB.begin();
 }
 
 void loop() {
diff --git a/libraries/USB/keywords.txt b/libraries/USB/keywords.txt
index 8b1442dfb2b..932f72efece 100644
--- a/libraries/USB/keywords.txt
+++ b/libraries/USB/keywords.txt
@@ -8,6 +8,7 @@
 
 USB	KEYWORD1
 USBCDC	KEYWORD1
+USBMSC	KEYWORD1
 
 #######################################
 # Methods and Functions (KEYWORD2)
@@ -18,6 +19,14 @@ end	KEYWORD2
 onEvent	KEYWORD2
 enableReset	KEYWORD2
 
+vendorID	KEYWORD2
+productID	KEYWORD2
+productRevision	KEYWORD2
+mediaPresent	KEYWORD2
+onStartStop	KEYWORD2
+onRead	KEYWORD2
+onWrite	KEYWORD2
+
 #######################################
 # Constants (LITERAL1)
 #######################################
diff --git a/libraries/USB/src/USB_NOT.h b/libraries/USB/src/USB_NOT.h
index e69de29bb2d..f349ad19817 100644
--- a/libraries/USB/src/USB_NOT.h
+++ b/libraries/USB/src/USB_NOT.h
@@ -0,0 +1,15 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
diff --git a/platform.txt b/platform.txt
index ee51f533e51..2fa972dce8f 100644
--- a/platform.txt
+++ b/platform.txt
@@ -30,7 +30,7 @@ compiler.cpp.flags.esp32=-mlongcalls -Wno-frame-address -ffunction-sections -fda
 compiler.S.flags.esp32=-ffunction-sections -fdata-sections -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-sign-compare -ggdb -O2 -Wwrite-strings -fstack-protector -fstrict-volatile-bitfields -Wno-error=unused-but-set-variable -fno-jump-tables -fno-tree-switch-conversion  -x assembler-with-cpp -MMD -c
 compiler.c.elf.flags.esp32=-T esp32.rom.redefined.ld -T memory.ld -T sections.ld -T esp32.rom.ld -T esp32.rom.api.ld -T esp32.rom.libgcc.ld -T esp32.rom.newlib-data.ld -T esp32.rom.syscalls.ld -T esp32.peripherals.ld  -mlongcalls -Wno-frame-address -Wl,--cref -Wl,--gc-sections -fno-rtti -fno-lto -u _Z5setupv -u _Z4loopv -Wl,--wrap=mbedtls_mpi_exp_mod -u esp_app_desc -u pthread_include_pthread_impl -u pthread_include_pthread_cond_impl -u pthread_include_pthread_local_storage_impl -u ld_include_panic_highint_hdl -u start_app -u start_app_other_cores -u __ubsan_include -Wl,--wrap=longjmp -u __assert_func -u vfs_include_syscalls_impl -Wl,--undefined=uxTopUsedPriority -u app_main -u newlib_include_heap_impl -u newlib_include_syscalls_impl -u newlib_include_pthread_impl -u __cxa_guard_dummy 
 compiler.ar.flags.esp32=cr
-build.extra_flags.esp32=-DARDUINO_SERIAL_PORT=0
+build.extra_flags.esp32=-DARDUINO_USB_CDC_ON_BOOT=0
 #
 # ESP32 Support End
 #
@@ -45,7 +45,7 @@ compiler.cpp.flags.esp32s2=-mlongcalls -ffunction-sections -fdata-sections -Wno-
 compiler.S.flags.esp32s2=-ffunction-sections -fdata-sections -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-sign-compare -ggdb -O2 -Wwrite-strings -fstack-protector -fstrict-volatile-bitfields -Wno-error=unused-but-set-variable -fno-jump-tables -fno-tree-switch-conversion  -x assembler-with-cpp -MMD -c
 compiler.c.elf.flags.esp32s2=-T memory.ld -T sections.ld -T esp32s2.rom.ld -T esp32s2.rom.api.ld -T esp32s2.rom.libgcc.ld -T esp32s2.rom.newlib-funcs.ld -T esp32s2.rom.newlib-data.ld -T esp32s2.rom.spiflash.ld -T esp32s2.peripherals.ld  -mlongcalls -Wl,--cref -Wl,--gc-sections -fno-rtti -fno-lto -u _Z5setupv -u _Z4loopv -u esp_app_desc -u pthread_include_pthread_impl -u pthread_include_pthread_cond_impl -u pthread_include_pthread_local_storage_impl -u ld_include_panic_highint_hdl -u start_app -u __ubsan_include -Wl,--wrap=longjmp -u __assert_func -u vfs_include_syscalls_impl -Wl,--undefined=uxTopUsedPriority -u app_main -u newlib_include_heap_impl -u newlib_include_syscalls_impl -u newlib_include_pthread_impl -u __cxa_guard_dummy 
 compiler.ar.flags.esp32s2=cr
-build.extra_flags.esp32s2=-DARDUINO_SERIAL_PORT={build.serial}
+build.extra_flags.esp32s2=-DARDUINO_USB_CDC_ON_BOOT={build.cdc_on_boot} -DARDUINO_USB_MSC_ON_BOOT={build.msc_on_boot} -DARDUINO_USB_DFU_ON_BOOT={build.dfu_on_boot}
 #
 # ESP32S2 Support End
 #
@@ -60,7 +60,7 @@ compiler.cpp.flags.esp32c3=-march=rv32imc -ffunction-sections -fdata-sections -W
 compiler.S.flags.esp32c3=-ffunction-sections -fdata-sections -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wno-unused-parameter -Wno-sign-compare -ggdb -Wno-error=format= -nostartfiles -Wno-format -Og -fstrict-volatile-bitfields -Wno-error=unused-but-set-variable -fno-jump-tables -fno-tree-switch-conversion  -x assembler-with-cpp -MMD -c
 compiler.c.elf.flags.esp32c3=-T memory.ld -T sections.ld -T esp32c3.rom.ld -T esp32c3.rom.api.ld -T esp32c3.rom.libgcc.ld -T esp32c3.rom.newlib.ld -T esp32c3.rom.version.ld -T esp32c3.peripherals.ld  -nostartfiles -march=rv32imc --specs=nosys.specs -Wl,--cref -Wl,--gc-sections -fno-rtti -fno-lto -u _Z5setupv -u _Z4loopv -Wl,--wrap=mbedtls_mpi_exp_mod -u esp_app_desc -u pthread_include_pthread_impl -u pthread_include_pthread_cond_impl -u pthread_include_pthread_local_storage_impl -u start_app -u __ubsan_include -u __assert_func -u vfs_include_syscalls_impl -Wl,--undefined=uxTopUsedPriority -u app_main -u newlib_include_heap_impl -u newlib_include_syscalls_impl -u newlib_include_pthread_impl -Wl,--wrap=_Unwind_SetEnableExceptionFdeSorting -Wl,--wrap=__register_frame_info_bases -Wl,--wrap=__register_frame_info -Wl,--wrap=__register_frame -Wl,--wrap=__register_frame_info_table_bases -Wl,--wrap=__register_frame_info_table -Wl,--wrap=__register_frame_table -Wl,--wrap=__deregister_frame_info_bases -Wl,--wrap=__deregister_frame_info -Wl,--wrap=_Unwind_Find_FDE -Wl,--wrap=_Unwind_GetGR -Wl,--wrap=_Unwind_GetCFA -Wl,--wrap=_Unwind_GetIP -Wl,--wrap=_Unwind_GetIPInfo -Wl,--wrap=_Unwind_GetRegionStart -Wl,--wrap=_Unwind_GetDataRelBase -Wl,--wrap=_Unwind_GetTextRelBase -Wl,--wrap=_Unwind_SetIP -Wl,--wrap=_Unwind_SetGR -Wl,--wrap=_Unwind_GetLanguageSpecificData -Wl,--wrap=_Unwind_FindEnclosingFunction -Wl,--wrap=_Unwind_Resume -Wl,--wrap=_Unwind_RaiseException -Wl,--wrap=_Unwind_DeleteException -Wl,--wrap=_Unwind_ForcedUnwind -Wl,--wrap=_Unwind_Resume_or_Rethrow -Wl,--wrap=_Unwind_Backtrace -Wl,--wrap=__cxa_call_unexpected -Wl,--wrap=__gxx_personality_v0 -u __cxa_guard_dummy -u __cxx_fatal_exception 
 compiler.ar.flags.esp32c3=cr
-build.extra_flags.esp32c3=-DARDUINO_SERIAL_PORT=0
+build.extra_flags.esp32c3=-DARDUINO_USB_CDC_ON_BOOT=0
 #
 # ESP32C3 Support End
 #
diff --git a/variants/esp32s2usb/pins_arduino.h b/variants/esp32s2usb/pins_arduino.h
new file mode 100644
index 00000000000..f3acb5c9540
--- /dev/null
+++ b/variants/esp32s2usb/pins_arduino.h
@@ -0,0 +1,81 @@
+#ifndef Pins_Arduino_h
+#define Pins_Arduino_h
+
+#include <stdint.h>
+
+// Default USB Settings
+#define USB_VID 			0x303A
+#define USB_PID 			0x0003
+#define USB_MANUFACTURER 	"Espressif Systems"
+#define USB_PRODUCT 		"ESP32-S2-USB"
+#define USB_SERIAL 			"0"
+#define USB_WEBUSB_ENABLED	false
+#define USB_WEBUSB_URL 		"https://espressif.github.io/arduino-esp32/webusb.html"
+
+// Default USB FirmwareMSC Settings
+#define USB_FW_MSC_VENDOR_ID 		"ESP32-S2" 		//max 8 chars
+#define USB_FW_MSC_PRODUCT_ID 		"Firmware MSC"	//max 16 chars
+#define USB_FW_MSC_PRODUCT_REVISION	"1.23" 			//max 4 chars
+#define USB_FW_MSC_VOLUME_NAME 		"S2-Firmware" 	//max 11 chars
+#define USB_FW_MSC_SERIAL_NUMBER 	0x00000000
+
+
+#define EXTERNAL_NUM_INTERRUPTS 46
+#define NUM_DIGITAL_PINS        48
+#define NUM_ANALOG_INPUTS       20
+
+#define analogInputToDigitalPin(p)  (((p)<20)?(esp32_adc2gpio[(p)]):-1)
+#define digitalPinToInterrupt(p)    (((p)<48)?(p):-1)
+#define digitalPinHasPWM(p)         (p < 46)
+
+static const uint8_t TX = 43;
+static const uint8_t RX = 44;
+
+static const uint8_t SDA = 8;
+static const uint8_t SCL = 9;
+
+static const uint8_t SS    = 34;
+static const uint8_t MOSI  = 35;
+static const uint8_t MISO  = 37;
+static const uint8_t SCK   = 36;
+
+static const uint8_t A0 = 1;
+static const uint8_t A1 = 2;
+static const uint8_t A2 = 3;
+static const uint8_t A3 = 4;
+static const uint8_t A4 = 5;
+static const uint8_t A5 = 6;
+static const uint8_t A6 = 7;
+static const uint8_t A7 = 8;
+static const uint8_t A8 = 9;
+static const uint8_t A9 = 10;
+static const uint8_t A10 = 11;
+static const uint8_t A11 = 12;
+static const uint8_t A12 = 13;
+static const uint8_t A13 = 14;
+static const uint8_t A14 = 15;
+static const uint8_t A15 = 16;
+static const uint8_t A16 = 17;
+static const uint8_t A17 = 18;
+static const uint8_t A18 = 19;
+static const uint8_t A19 = 20;
+
+static const uint8_t T1 = 1;
+static const uint8_t T2 = 2;
+static const uint8_t T3 = 3;
+static const uint8_t T4 = 4;
+static const uint8_t T5 = 5;
+static const uint8_t T6 = 6;
+static const uint8_t T7 = 7;
+static const uint8_t T8 = 8;
+static const uint8_t T9 = 9;
+static const uint8_t T10 = 10;
+static const uint8_t T11 = 11;
+static const uint8_t T12 = 12;
+static const uint8_t T13 = 13;
+static const uint8_t T14 = 14;
+
+static const uint8_t DAC1 = 17;
+static const uint8_t DAC2 = 18;
+
+#endif /* Pins_Arduino_h */