Skip to content

Commit 6ee8621

Browse files
Add USB drive mode to TinyUSB, SingleFileDisk
SingleFileDisk allows for exporting a file from the onboard LittleFS filesystem to a PC through an emulated FAT drive when connected. The PC can open and copy the file, as well as delete it, but the PC has no access to the main onboard LittleFS and no actual on-flash FAT structures are used. This is handy for things like data loggers. They can run connected to USB power for some time, and then connected to a PC to dowmload the CSV log recorded.
1 parent d717b58 commit 6ee8621

File tree

13 files changed

+611
-12
lines changed

13 files changed

+611
-12
lines changed

cores/rp2040/RP2040USB.cpp

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ static int __usb_task_irq;
6868
#define USBD_STR_SERIAL (0x03)
6969
#define USBD_STR_CDC (0x04)
7070

71-
7271
#define EPNUM_HID 0x83
7372

73+
#define USBD_MSC_EPOUT 0x03
74+
#define USBD_MSC_EPIN 0x84
75+
#define USBD_MSC_EPSIZE 64
7476

7577
const uint8_t *tud_descriptor_device_cb(void) {
7678
static tusb_desc_device_t usbd_desc_device = {
@@ -89,7 +91,7 @@ const uint8_t *tud_descriptor_device_cb(void) {
8991
.iSerialNumber = USBD_STR_SERIAL,
9092
.bNumConfigurations = 1
9193
};
92-
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick) {
94+
if (__USBInstallSerial && !__USBInstallKeyboard && !__USBInstallMouse && !__USBInstallJoystick && !__USBInstallMassStorage) {
9395
// Can use as-is, this is the default USB case
9496
return (const uint8_t *)&usbd_desc_device;
9597
}
@@ -103,6 +105,9 @@ const uint8_t *tud_descriptor_device_cb(void) {
103105
if (__USBInstallJoystick) {
104106
usbd_desc_device.idProduct |= 0x0100;
105107
}
108+
if (__USBInstallMassStorage) {
109+
usbd_desc_device.idProduct ^= 0x2000;
110+
}
106111
// Set the device class to 0 to indicate multiple device classes
107112
usbd_desc_device.bDeviceClass = 0;
108113
usbd_desc_device.bDeviceSubClass = 0;
@@ -223,7 +228,7 @@ void __SetupUSBDescriptor() {
223228
if (!usbd_desc_cfg) {
224229
bool hasHID = __USBInstallKeyboard || __USBInstallMouse || __USBInstallJoystick;
225230

226-
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0);
231+
uint8_t interface_count = (__USBInstallSerial ? 2 : 0) + (hasHID ? 1 : 0) + (__USBInstallMassStorage ? 1 : 0);
227232

228233
uint8_t cdc_desc[TUD_CDC_DESC_LEN] = {
229234
// Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval
@@ -238,7 +243,12 @@ void __SetupUSBDescriptor() {
238243
TUD_HID_DESCRIPTOR(hid_itf, 0, HID_ITF_PROTOCOL_NONE, hid_report_len, EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10)
239244
};
240245

241-
int usbd_desc_len = TUD_CONFIG_DESC_LEN + (__USBInstallSerial ? sizeof(cdc_desc) : 0) + (hasHID ? sizeof(hid_desc) : 0);
246+
uint8_t msd_itf = interface_count - 1;
247+
uint8_t msd_desc[TUD_MSC_DESC_LEN] = {
248+
TUD_MSC_DESCRIPTOR(msd_itf, 0, USBD_MSC_EPOUT, USBD_MSC_EPIN, USBD_MSC_EPSIZE)
249+
};
250+
251+
int usbd_desc_len = TUD_CONFIG_DESC_LEN + (__USBInstallSerial ? sizeof(cdc_desc) : 0) + (hasHID ? sizeof(hid_desc) : 0) + (__USBInstallMassStorage ? sizeof(msd_desc) : 0);
242252

243253
uint8_t tud_cfg_desc[TUD_CONFIG_DESC_LEN] = {
244254
// Config number, interface count, string index, total length, attribute, power in mA
@@ -260,6 +270,10 @@ void __SetupUSBDescriptor() {
260270
memcpy(ptr, hid_desc, sizeof(hid_desc));
261271
ptr += sizeof(hid_desc);
262272
}
273+
if (__USBInstallMassStorage) {
274+
memcpy(ptr, msd_desc, sizeof(msd_desc));
275+
ptr += sizeof(msd_desc);
276+
}
263277
}
264278
}
265279
}
@@ -367,4 +381,55 @@ extern "C" void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_r
367381
(void) bufsize;
368382
}
369383

384+
extern "C" int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) __attribute__((weak));
385+
extern "C" int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) {
386+
(void) lun;
387+
(void) lba;
388+
(void) offset;
389+
(void) buffer;
390+
(void) bufsize;
391+
return -1;
392+
}
393+
394+
extern "C" bool tud_msc_test_unit_ready_cb(uint8_t lun) __attribute__((weak));
395+
extern "C" bool tud_msc_test_unit_ready_cb(uint8_t lun) {
396+
(void) lun;
397+
return false;
398+
}
399+
400+
extern "C" int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) __attribute__((weak));
401+
extern "C" int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
402+
(void) lun;
403+
(void) lba;
404+
(void) offset;
405+
(void) buffer;
406+
(void) bufsize;
407+
return -1;
408+
}
409+
410+
extern "C" int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) __attribute__((weak));
411+
extern "C" int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) {
412+
(void) lun;
413+
(void) scsi_cmd;
414+
(void) buffer;
415+
(void) bufsize;
416+
return 0;
417+
}
418+
419+
extern "C" void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) __attribute__((weak));
420+
extern "C" void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) {
421+
(void) lun;
422+
*block_count = 0;
423+
*block_size = 0;
424+
}
425+
426+
extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) __attribute__((weak));
427+
extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
428+
(void) lun;
429+
vendor_id[0] = 0;
430+
product_id[0] = 0;
431+
product_rev[0] = 0;
432+
}
433+
434+
370435
#endif

cores/rp2040/RP2040USB.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extern void __USBInstallSerial() __attribute__((weak));
2626
extern void __USBInstallKeyboard() __attribute__((weak));
2727
extern void __USBInstallJoystick() __attribute__((weak));
2828
extern void __USBInstallMouse() __attribute__((weak));
29+
extern void __USBInstallMassStorage() __attribute__((weak));
2930

3031
// Big, global USB mutex, shared with all USB devices to make sure we don't
3132
// have multiple cores updating the TUSB state in parallel

include/tusb_config.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,15 @@
7272
//------------- CLASS -------------//
7373
#define CFG_TUD_HID (2)
7474
#define CFG_TUD_CDC (1)
75-
#define CFG_TUD_MSC (0)
75+
#define CFG_TUD_MSC (1)
7676
#define CFG_TUD_MIDI (0)
7777
#define CFG_TUD_VENDOR (0)
7878

7979
#define CFG_TUD_CDC_RX_BUFSIZE (256)
8080
#define CFG_TUD_CDC_TX_BUFSIZE (256)
8181

82-
#define CFG_TUD_MIDI_RX_BUFSIZE (64)
83-
#define CFG_TUD_MIDI_TX_BUFSIZE (64)
82+
#define CFG_TUD_MSC_EP_BUFSIZE (512)
83+
8484

8585
// HID buffer size Should be sufficient to hold ID (if any) + Data
8686
#define CFG_TUD_HID_EP_BUFSIZE (64)

lib/libpico-ipv6.a

49.1 KB
Binary file not shown.

lib/libpico.a

49.1 KB
Binary file not shown.

libraries/PicoOTA/library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
55
sentence=Configures requests for OTA bootloader
66
paragraph=Example repository for Ethernet drivers
77
category=Device Control
8-
url=https://github.com/earlephilhower.arduino-pico
8+
url=https://github.com/earlephilhower/arduino-pico
99
architectures=rp2040
1010
dot_a_linkage=true
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Simple logger with USB uplaod to PC
2+
// Uses SingleFileDrive to export an onboard LittleFS file to the computer
3+
// The PC can open/copy the file, and then the user can delete it to restart
4+
5+
// Released to the public domain, 2022 - Earle F. Philhower, III
6+
7+
#include <SingleFileDrive.h>
8+
#include <LittleFS.h>
9+
10+
uint32_t cnt = 0;
11+
bool okayToWrite = true;
12+
13+
// Make the CSV file and give it a simple header
14+
void headerCSV() {
15+
File f = LittleFS.open("data.csv", "w");
16+
f.printf("sample,millis,temp,rand\n");
17+
f.close();
18+
cnt = 0;
19+
}
20+
21+
// Called when the USB stick connected to a PC and the drive opened
22+
// Note this is from a USB IRQ so no printing to SerialUSB/etc.
23+
void plug(uint32_t i) {
24+
(void) i;
25+
okayToWrite = false;
26+
}
27+
28+
// Called when the USB is ejected or removed from a PC
29+
// Note this is from a USB IRQ so no printing to SerialUSB/etc.
30+
void unplug(uint32_t i) {
31+
(void) i;
32+
okayToWrite = true;
33+
}
34+
35+
// Called when the PC tries to delete the single file
36+
// Note this is from a USB IRQ so no printing to SerialUSB/etc.
37+
void deleteCSV(uint32_t i) {
38+
(void) i;
39+
LittleFS.remove("data.csv");
40+
headerCSV();
41+
}
42+
43+
void setup() {
44+
Serial.begin();
45+
delay(5000);
46+
47+
LittleFS.begin();
48+
49+
// Set up the USB disk share
50+
singleFileDrive.onDelete(deleteCSV);
51+
singleFileDrive.onPlug(plug);
52+
singleFileDrive.onUnplug(unplug);
53+
singleFileDrive.begin("data.csv", "DATA", "CSV");
54+
55+
// Find the last written data
56+
File f = LittleFS.open("data.csv", "r");
57+
if (!f || !f.size()) {
58+
cnt = 1;
59+
headerCSV();
60+
} else {
61+
if (f.size() > 2048) {
62+
f.seek(f.size() - 1024);
63+
}
64+
do {
65+
String s = f.readStringUntil('\n');
66+
sscanf(s.c_str(), "%lu,", &cnt);
67+
} while (f.available());
68+
f.close();
69+
cnt++;
70+
}
71+
72+
Serial.printf("Starting acquisition at %d\n", cnt);
73+
}
74+
75+
void loop() {
76+
float temp = analogReadTemp();
77+
uint32_t hwrand = rp2040.hwrand32();
78+
// Make sure the USB connect doesn't happen while we're writing!
79+
noInterrupts();
80+
if (okayToWrite) {
81+
Serial.printf("Sampling...%lu\n", cnt);
82+
// Don't want the USB to connect during an update!
83+
File f = LittleFS.open("data.csv", "a");
84+
if (f) {
85+
f.printf("%lu,%lu,%f,%lu\n", cnt++, millis(), temp, hwrand);
86+
f.close();
87+
}
88+
}
89+
interrupts();
90+
91+
delay(10000);
92+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#######################################
2+
# Syntax Coloring Map
3+
#######################################
4+
5+
#######################################
6+
# Datatypes (KEYWORD1)
7+
#######################################
8+
9+
SingleFileDrive KEYWORD1
10+
11+
#######################################
12+
# Methods and Functions (KEYWORD2)
13+
#######################################
14+
15+
singleFileDrive KEYWORD1
16+
setFile KEYWORD1
17+
onDelete KEYWORD1
18+
onPlug KEYWORD1
19+
onUnplug KEYWORD1
20+
21+
#######################################
22+
# Constants (LITERAL1)
23+
#######################################
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=SingleFileDrive
2+
version=1.0.0
3+
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
4+
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
5+
sentence=Allows using USB MSC (USB stick) to transfer an onboard flash file to a PC
6+
paragraph=Emulates a USB stick and presents a single file for users to copy over/erase
7+
category=Device Control
8+
url=https://github.com/earlephilhower/arduino-pico
9+
architectures=rp2040
10+
dot_a_linkage=true

0 commit comments

Comments
 (0)