Parallax Propeller2 USB driver - the MashUp

Getting started: storage & files (thumb drives, SD readers, SSDs)
Login

Getting started: storage & files (thumb drives, SD readers, SSDs)

Status: validated live — 1 TB exFAT SSD (full performance profile), 64 GB exFAT card, 8 GB FAT32 card, hot-plug of a mounted volume.

What you need

pnut-ts -d -I <path-to>/src -D USB_MSC -D USB_EXFAT my_files.spin2

The smallest program

#IFDEF USB_MSC
#PRAGMA EXPORTDEF USB_MSC
#ENDIF
#IFDEF USB_EXFAT
#PRAGMA EXPORTDEF USB_EXFAT
#ENDIF

CON
  _clkfreq = 200_000_000

OBJ
  usb : "usb_app"

VAR
  byte  buf[64]

PUB main() | h, n
  usb.begin(16)
  ifnot usb.await_drive(15_000)             ' SSD bridges take 6-9 s to even
    debug("no drive")                       ' assert connect - 15 s is generous
    repeat

  h := usb.fs_create(string("/HELLO.TXT"))  ' filesystem mounts itself on
  if h >= 0                                 ' first use - no mount call needed
    usb.fs_write(h, @msg, 13)
    usb.fs_close(h)

  h := usb.fs_open_read(string("/HELLO.TXT"))
  if h >= 0
    n := usb.fs_read(h, @buf, 63)
    buf[n] := 0
    debug("read back: ", zstr_(@buf))
    usb.fs_close(h)

  repeat
    usb.poll()                              ' hot-plug: a yanked drive is
    waitms(50)                              ' abandoned safely, remount is
                                            ' automatic on next fs_* use

DAT
msg  BYTE  "hello, disk", 13, 0

What bring-up actually does

  1. usb.begin(16) enumerates; the first mass-storage device binds.
  2. await_drive() waits for SCSI readiness too — a cold card reader takes seconds to mount its card; the driver polls INQUIRY/TEST-UNIT- READY instead of guessing with delays.
  3. The first fs_* call probes the media: exFAT first (boot-region checksum verified), FAT32 fallback if both gates are compiled. fs_kind() tells you which one owns the mount.
  4. Paths are root-relative (/LOGS/RUN1.TXT) on exFAT; FAT32 uses 8.3 names. fs_open_write appends; fs_create fails if the file exists; negative returns are error codes (README table).
  5. Timestamps: bind an RTC before writing with fs_time_source(@rtc) — contract in the README — or files get a fixed build-time stamp.

Worth knowing

Troubleshooting

Symptom Likely cause
await_drive times out Media still booting (SSD bridges 6–9 s — raise the window); unpowered hub; drive on the other bus.
Mount error −81 Media isn't exFAT — add -D USB_FAT32, or reformat.
−87 after replug Stale handle from before the detach — reopen by path.
First op after attach is slow That's the card reader mounting its card (measured ~2.6 s) — it's the device, not the driver.
Mount fails, sector 0 reads all-zero, even a desktop format "succeeds" but changes nothing Dead flash: the card ACKs writes and silently discards them (bench-caught 2026-08-13: write CSW clean, read-back zeros). harness/top_lunprobe.spin2's write-persistence check proves it in seconds. Replace the card.
Reads/writes start failing and don't come back The drive has wedged. Branch on disk_wedged(), and see recovery.md — it covers telling a hub-disabled port from a wedged device, the recovery ladder, and how to prove a drive really came back.
Capacity/TUR fine but every read fails sense $B/47/01 The reader's SD data lines to the card are failing CRC (command line still works — capacity comes from it). Reseat/clean, or swap the reader.

Next