From 6fc557b9eafdcb04b7b21b32579bbb2484cb5c8c Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Thu, 3 Oct 2024 22:54:31 +0200 Subject: [PATCH 01/65] Try example from espressif/arduino-esp32 https://github.com/espressif/arduino-esp32/blob/733373a049d8a06fab00628bcae585550fac6d07/libraries/USB/examples/USBMSC/USBMSC.ino --- platformio.ini | 5 ++ src/main.cpp | 222 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) create mode 100644 platformio.ini create mode 100644 src/main.cpp diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 000000000..f797d69b2 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,5 @@ +[env:esp32-s3-devkitc-1] +platform = espressif32 +board = esp32-s3-devkitc-1 +framework = arduino +monitor_speed = 115200 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 000000000..3bccd3d6d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,222 @@ +#include +#ifndef ARDUINO_USB_MODE +#error This ESP32 SoC has no Native USB interface +#elif ARDUINO_USB_MODE == 1 +#warning This sketch should be used when USB is in OTG mode +void setup() {} +void loop() {} +#else +#include "USB.h" +#include "USBMSC.h" + +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) { + Serial.printf("MSC WRITE: lba: %lu, offset: %lu, bufsize: %lu\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) { + Serial.printf("MSC READ: lba: %lu, offset: %lu, bufsize: %lu\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) { + Serial.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: + Serial.println("USB PLUGGED"); + break; + case ARDUINO_USB_STOPPED_EVENT: + Serial.println("USB UNPLUGGED"); + break; + case ARDUINO_USB_SUSPEND_EVENT: + Serial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", + data->suspend.remote_wakeup_en); + break; + case ARDUINO_USB_RESUME_EVENT: + Serial.println("USB RESUMED"); + break; + + default: + break; + } + } +} + +void setup() { + Serial.begin(115200); + Serial.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.isWritable(true); // true if writable, false if read-only + + MSC.begin(DISK_SECTOR_COUNT, DISK_SECTOR_SIZE); + USB.begin(); +} + +void loop() { + // put your main code here, to run repeatedly: +} +#endif /* ARDUINO_USB_MODE */ From 9a0b34c71d758c3fb937be3ddba7cb215558c05c Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 5 Oct 2024 21:50:59 +0200 Subject: [PATCH 02/65] Try Adafruit USB device MSC example. https://github.com/adafruit/Adafruit_TinyUSB_Arduino/blob/a529a74e4f5018ad95b0524807b5e508871c7831/examples/MassStorage/msc_esp32_file_browser/msc_esp32_file_browser.ino --- platformio.ini | 8 + src/main.cpp | 447 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 455 insertions(+) create mode 100644 platformio.ini create mode 100644 src/main.cpp diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 000000000..21933067f --- /dev/null +++ b/platformio.ini @@ -0,0 +1,8 @@ +[env:esp32-s3-devkitc-1] +platform = espressif32 +board = esp32-s3-devkitc-1 +framework = arduino +monitor_speed = 115200 + +lib_deps = + adafruit/Adafruit TinyUSB Library@^3.3.4 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 000000000..e3392ceb1 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,447 @@ +/********************************************************************* + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + MIT license, check LICENSE for more information + Copyright (c) 2019 Ha Thach for Adafruit Industries + All text above, and the splash screen below must be included in + any redistribution +*********************************************************************/ + +/* This example is based on + * "arduino-esp32/libraries/WebServer/examples/FSBrowser" to expose on-board + * external Flash as USB Mass Storage and webserver. Both interfaces can make + * changes to file system + * + * How to run this example + * 1. Create secrets.h and define your "SECRET_SSID" and "SECRET_PASSWORD" + * 2. Compile and upload this sketch + * 3. Your ESP will be expose as MassStorage device. + * 4. If it is your first run (otherwise skip this step): + * - you may need to format the drive as FAT. Note: If your PC failed to + * format, you could format it using follow sketch + * "https://github.com/adafruit/Adafruit_SPIFlash/tree/master/examples/SdFat_format" + * - Copy all files in 'data/' folder of this example to the root directory of + * the MassStorage disk drive + * 5. When prompted, open http://esp32fs.local/edit to access the file browser + * 6. Modify/Update USB drive then refresh your browser to see if the change is + * updated + * 7. Upload/Edit a file using web browser then see if the USB Drive is updated. + * Note: the usb drive could briefly disappear and reappear to force PC to + * refresh its cache + * + * NOTE: Following library is required + * - Adafruit_SPIFlash https://github.com/adafruit/Adafruit_SPIFlash + * - SdFat https://github.com/adafruit/SdFat + */ + +#include "Adafruit_SPIFlash.h" +#include "Adafruit_TinyUSB.h" +#include "SPI.h" +#include "SdFat.h" + +#include +#include +#include +#include + +// check if secrets.h is includable, if not please +// create one with SSDI & PASSWORD macro as following example: +#if __has_include("secrets.h") +#include "secrets.h" +#else +#warning "Please create secrets.h with SSID & PASSWORD defined" +#define SECRET_SSID "your-ssid" +#define SECRET_PASSWORD "your-password" +#endif + +// Debug with FTDI (Serial0) or USBCDC (Serial) +#define DBG_SERIAL Serial + +// ESP32 use same flash device that store code. +// Therefore there is no need to specify the SPI and SS +Adafruit_FlashTransport_ESP32 flashTransport; +Adafruit_SPIFlash flash(&flashTransport); + +// file system object from SdFat +FatVolume fatfs; + +// USB Mass Storage object +Adafruit_USBD_MSC usb_msc; + +bool fs_formatted; // Check if flash is formatted +bool fs_changed; // Set to true when browser write to flash + +const char *host = "esp32fs"; +WebServer server(80); +// holds the current upload +File32 fsUploadFile; + +//--------------------------------------------------------------------+ +// Setup +//--------------------------------------------------------------------+ + +void setupMassStorage(void) { + flash.begin(); + + // Set disk vendor id, product id and revision with string up to 8, 16, 4 + // characters respectively + usb_msc.setID("Adafruit", "External Flash", "1.0"); + + // Set callback + usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb); + + // Set disk size, block size should be 512 regardless of spi flash page size + usb_msc.setCapacity(flash.size() / 512, 512); + + // MSC is ready for read/write + fs_changed = false; + usb_msc.setReadyCallback(0, msc_ready_callback); + usb_msc.begin(); + + // If already enumerated, additional class driverr begin() e.g msc, hid, midi + // won't take effect until re-enumeration + if (TinyUSBDevice.mounted()) { + TinyUSBDevice.detach(); + delay(10); + TinyUSBDevice.attach(); + } + + // Init file system on the flash + fs_formatted = fatfs.begin(&flash); + + if (!fs_formatted) { + DBG_SERIAL.println( + "Failed to init files system, flash may not be formatted"); + } +} + +void refreshMassStorage(void) { fs_changed = true; } + +void setupServer(void) { + // WIFI INIT + DBG_SERIAL.printf("Connecting to %s\n", SECRET_SSID); + if (String(WiFi.SSID()) != String(SECRET_SSID)) { + WiFi.mode(WIFI_STA); + WiFi.begin(SECRET_SSID, SECRET_PASSWORD); + } + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + DBG_SERIAL.print("."); + } + DBG_SERIAL.println(""); + DBG_SERIAL.print("Connected! IP address: "); + DBG_SERIAL.println(WiFi.localIP()); + + MDNS.begin(host); + DBG_SERIAL.print("Open http://"); + DBG_SERIAL.print(host); + DBG_SERIAL.println(".local/edit to access the file browser"); + + // SERVER INIT + + // list directory + server.on("/list", HTTP_GET, handleFileList); + + // load editor + server.on("/edit", HTTP_GET, []() { + if (!handleFileRead("/edit.htm")) { + server.send(404, "text/plain", "FileNotFound"); + } + }); + + // create file + server.on("/edit", HTTP_PUT, handleFileCreate); + + // delete file + server.on("/edit", HTTP_DELETE, handleFileDelete); + + // first callback is called after the request has ended with all parsed + // arguments second callback handles file uploads at that location + server.on( + "/edit", HTTP_POST, []() { server.send(200, "text/plain", ""); }, + handleFileUpload); + + // called when the url is not defined here + // use it to load content from fatfs + server.onNotFound([]() { + if (!handleFileRead(server.uri())) { + server.send(404, "text/plain", "FileNotFound"); + } + }); + + // get heap status, analog input value and all GPIO statuses in one json call + server.on("/all", HTTP_GET, []() { + String json = "{"; + json += "\"heap\":" + String(ESP.getFreeHeap()); + json += ", \"analog\":" + String(analogRead(A0)); + json += ", \"gpio\":" + String((uint32_t)(0)); + json += "}"; + server.send(200, "text/json", json); + json = String(); + }); + server.begin(); + DBG_SERIAL.println("HTTP server started"); +} + +void setup() { +#ifdef LED_BUILTIN + pinMode(LED_BUILTIN, OUTPUT); +#endif + + DBG_SERIAL.begin(115200); + + setupMassStorage(); + + // while ( !DBG_SERIAL ) delay(10); // wait for native usb + DBG_SERIAL.println("TinyUSB Mass Storage with ESP32 File Browser example"); + DBG_SERIAL.print("JEDEC ID: 0x"); + DBG_SERIAL.println(flash.getJEDECID(), HEX); + DBG_SERIAL.print("Flash size: "); + DBG_SERIAL.print(flash.size() / 1024); + DBG_SERIAL.println(" KB"); + + setupServer(); +} + +//--------------------------------------------------------------------+ +// Handle requests +//--------------------------------------------------------------------+ + +// format bytes +String formatBytes(size_t bytes) { + if (bytes < 1024) { + return String(bytes) + "B"; + } else if (bytes < (1024 * 1024)) { + return String(bytes / 1024.0) + "KB"; + } else if (bytes < (1024 * 1024 * 1024)) { + return String(bytes / 1024.0 / 1024.0) + "MB"; + } else { + return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB"; + } +} + +String getContentType(String filename) { + if (server.hasArg("download")) { + return "application/octet-stream"; + } else if (filename.endsWith(".htm")) { + return "text/html"; + } else if (filename.endsWith(".html")) { + return "text/html"; + } else if (filename.endsWith(".css")) { + return "text/css"; + } else if (filename.endsWith(".js")) { + return "application/javascript"; + } else if (filename.endsWith(".png")) { + return "image/png"; + } else if (filename.endsWith(".gif")) { + return "image/gif"; + } else if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } else if (filename.endsWith(".ico")) { + return "image/x-icon"; + } else if (filename.endsWith(".xml")) { + return "text/xml"; + } else if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } else if (filename.endsWith(".zip")) { + return "application/x-zip"; + } else if (filename.endsWith(".gz")) { + return "application/x-gzip"; + } + return "text/plain"; +} + +bool exists(String path) { + bool yes = false; + File32 file = fatfs.open(path, O_READ); + if (file && !file.isDirectory()) { + yes = true; + } + file.close(); + return yes; +} + +bool handleFileRead(String path) { + DBG_SERIAL.println("handleFileRead: " + path); + if (path.endsWith("/")) { + path += "index.htm"; + } + String contentType = getContentType(path); + // String pathWithGz = path + ".gz"; + if (/*exists(pathWithGz) ||*/ exists(path)) { + // if (exists(pathWithGz)) { + // path += ".gz"; + // } + File32 file = fatfs.open(path, O_READ); + server.streamFile(file, contentType); + file.close(); + return true; + } + return false; +} + +void handleFileUpload() { + if (server.uri() != "/edit") { + return; + } + HTTPUpload &upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + String filename = upload.filename; + if (!filename.startsWith("/")) { + filename = "/" + filename; + } + DBG_SERIAL.print("handleFileUpload Name: "); + DBG_SERIAL.println(filename); + fsUploadFile = fatfs.open(filename, O_WRITE | O_CREAT | O_TRUNC); + filename = String(); + } else if (upload.status == UPLOAD_FILE_WRITE) { + DBG_SERIAL.print("handleFileUpload Data: "); + DBG_SERIAL.println(upload.currentSize); + if (fsUploadFile) { + fsUploadFile.write(upload.buf, upload.currentSize); + } else { + DBG_SERIAL.print("handleFileUpload file is not opened !!!"); + } + } else if (upload.status == UPLOAD_FILE_END) { + if (fsUploadFile) { + fsUploadFile.close(); + refreshMassStorage(); + } + DBG_SERIAL.print("handleFileUpload Size: "); + DBG_SERIAL.println(upload.totalSize); + } +} + +void handleFileDelete() { + if (server.args() == 0) { + return server.send(500, "text/plain", "BAD ARGS"); + } + String path = server.arg(0); + DBG_SERIAL.println("handleFileDelete: " + path); + if (path == "/") { + return server.send(500, "text/plain", "BAD PATH"); + } + if (!exists(path)) { + return server.send(404, "text/plain", "FileNotFound"); + } + fatfs.remove(path.c_str()); + refreshMassStorage(); + server.send(200, "text/plain", ""); + path = String(); +} + +void handleFileCreate() { + if (server.args() == 0) { + return server.send(500, "text/plain", "BAD ARGS"); + } + String path = server.arg(0); + DBG_SERIAL.println("handleFileCreate: " + path); + if (path == "/") { + return server.send(500, "text/plain", "BAD PATH"); + } + if (exists(path)) { + return server.send(500, "text/plain", "FILE EXISTS"); + } + File32 file = fatfs.open(path, O_WRITE | O_CREAT); + if (file) { + file.close(); + } else { + return server.send(500, "text/plain", "CREATE FAILED"); + } + server.send(200, "text/plain", ""); + path = String(); +} + +void handleFileList() { + if (!server.hasArg("dir")) { + server.send(500, "text/plain", "BAD ARGS"); + return; + } + + String path = server.arg("dir"); + DBG_SERIAL.println("handleFileList: " + path); + + File32 root = fatfs.open(path); + path = String(); + + String output = "["; + if (root.isDirectory()) { + File32 file = root.openNextFile(); + char fname[256]; + while (file) { + if (output != "[") { + output += ','; + } + output += "{\"type\":\""; + output += (file.isDirectory()) ? "dir" : "file"; + output += "\",\"name\":\""; + // output += String(file.path()).substring(1); + file.getName(fname, sizeof(fname)); + output += fname; + output += "\"}"; + file = root.openNextFile(); + } + } + output += "]"; + server.send(200, "text/json", output); +} + +//--------------------------------------------------------------------+ +// Loop +//--------------------------------------------------------------------+ + +void loop() { + server.handleClient(); + delay(2); // allow the cpu to switch to other tasks +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and +// return number of copied bytes (must be multiple of block size) +int32_t msc_read_cb(uint32_t lba, void *buffer, uint32_t bufsize) { + // Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks + // already include 4K sector caching internally. We don't need to cache it, + // yahhhh!! + return flash.readBlocks(lba, (uint8_t *)buffer, bufsize / 512) ? bufsize : -1; +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and +// return number of written bytes (must be multiple of block size) +int32_t msc_write_cb(uint32_t lba, uint8_t *buffer, uint32_t bufsize) { +#ifdef LED_BUILTIN + digitalWrite(LED_BUILTIN, HIGH); +#endif + + // Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks + // already include 4K sector caching internally. We don't need to cache it, + // yahhhh!! + return flash.writeBlocks(lba, buffer, bufsize / 512) ? bufsize : -1; +} + +// Callback invoked when WRITE10 command is completed (status received and +// accepted by host). used to flush any pending cache. +void msc_flush_cb(void) { + // sync with flash + flash.syncBlocks(); + + // clear file system's cache to force refresh + fatfs.cacheClear(); + +#ifdef LED_BUILTIN + digitalWrite(LED_BUILTIN, LOW); +#endif +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool msc_ready_callback(void) { + // if fs has changed, mark unit as not ready temporarily to force PC to flush + // cache + bool ret = !fs_changed; + fs_changed = false; + return ret; +} From 998cf8e3561981c4f3eedf411836d47446d5da01 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 5 Oct 2024 23:38:58 +0200 Subject: [PATCH 03/65] Rearrange functions. --- src/main.cpp | 274 +++++++++++++++++++++++++-------------------------- 1 file changed, 137 insertions(+), 137 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index e3392ceb1..936924556 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -78,134 +78,6 @@ WebServer server(80); // holds the current upload File32 fsUploadFile; -//--------------------------------------------------------------------+ -// Setup -//--------------------------------------------------------------------+ - -void setupMassStorage(void) { - flash.begin(); - - // Set disk vendor id, product id and revision with string up to 8, 16, 4 - // characters respectively - usb_msc.setID("Adafruit", "External Flash", "1.0"); - - // Set callback - usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb); - - // Set disk size, block size should be 512 regardless of spi flash page size - usb_msc.setCapacity(flash.size() / 512, 512); - - // MSC is ready for read/write - fs_changed = false; - usb_msc.setReadyCallback(0, msc_ready_callback); - usb_msc.begin(); - - // If already enumerated, additional class driverr begin() e.g msc, hid, midi - // won't take effect until re-enumeration - if (TinyUSBDevice.mounted()) { - TinyUSBDevice.detach(); - delay(10); - TinyUSBDevice.attach(); - } - - // Init file system on the flash - fs_formatted = fatfs.begin(&flash); - - if (!fs_formatted) { - DBG_SERIAL.println( - "Failed to init files system, flash may not be formatted"); - } -} - -void refreshMassStorage(void) { fs_changed = true; } - -void setupServer(void) { - // WIFI INIT - DBG_SERIAL.printf("Connecting to %s\n", SECRET_SSID); - if (String(WiFi.SSID()) != String(SECRET_SSID)) { - WiFi.mode(WIFI_STA); - WiFi.begin(SECRET_SSID, SECRET_PASSWORD); - } - - while (WiFi.status() != WL_CONNECTED) { - delay(500); - DBG_SERIAL.print("."); - } - DBG_SERIAL.println(""); - DBG_SERIAL.print("Connected! IP address: "); - DBG_SERIAL.println(WiFi.localIP()); - - MDNS.begin(host); - DBG_SERIAL.print("Open http://"); - DBG_SERIAL.print(host); - DBG_SERIAL.println(".local/edit to access the file browser"); - - // SERVER INIT - - // list directory - server.on("/list", HTTP_GET, handleFileList); - - // load editor - server.on("/edit", HTTP_GET, []() { - if (!handleFileRead("/edit.htm")) { - server.send(404, "text/plain", "FileNotFound"); - } - }); - - // create file - server.on("/edit", HTTP_PUT, handleFileCreate); - - // delete file - server.on("/edit", HTTP_DELETE, handleFileDelete); - - // first callback is called after the request has ended with all parsed - // arguments second callback handles file uploads at that location - server.on( - "/edit", HTTP_POST, []() { server.send(200, "text/plain", ""); }, - handleFileUpload); - - // called when the url is not defined here - // use it to load content from fatfs - server.onNotFound([]() { - if (!handleFileRead(server.uri())) { - server.send(404, "text/plain", "FileNotFound"); - } - }); - - // get heap status, analog input value and all GPIO statuses in one json call - server.on("/all", HTTP_GET, []() { - String json = "{"; - json += "\"heap\":" + String(ESP.getFreeHeap()); - json += ", \"analog\":" + String(analogRead(A0)); - json += ", \"gpio\":" + String((uint32_t)(0)); - json += "}"; - server.send(200, "text/json", json); - json = String(); - }); - server.begin(); - DBG_SERIAL.println("HTTP server started"); -} - -void setup() { -#ifdef LED_BUILTIN - pinMode(LED_BUILTIN, OUTPUT); -#endif - - DBG_SERIAL.begin(115200); - - setupMassStorage(); - - // while ( !DBG_SERIAL ) delay(10); // wait for native usb - DBG_SERIAL.println("TinyUSB Mass Storage with ESP32 File Browser example"); - DBG_SERIAL.print("JEDEC ID: 0x"); - DBG_SERIAL.println(flash.getJEDECID(), HEX); - DBG_SERIAL.print("Flash size: "); - DBG_SERIAL.print(flash.size() / 1024); - DBG_SERIAL.println(" KB"); - - setupServer(); -} - //--------------------------------------------------------------------+ // Handle requests //--------------------------------------------------------------------+ @@ -254,6 +126,8 @@ String getContentType(String filename) { return "text/plain"; } +static void refreshMassStorage(void) { fs_changed = true; } + bool exists(String path) { bool yes = false; File32 file = fatfs.open(path, O_READ); @@ -389,15 +263,6 @@ void handleFileList() { server.send(200, "text/json", output); } -//--------------------------------------------------------------------+ -// Loop -//--------------------------------------------------------------------+ - -void loop() { - server.handleClient(); - delay(2); // allow the cpu to switch to other tasks -} - // Callback invoked when received READ10 command. // Copy disk's data to buffer (up to bufsize) and // return number of copied bytes (must be multiple of block size) @@ -445,3 +310,138 @@ bool msc_ready_callback(void) { fs_changed = false; return ret; } + +//--------------------------------------------------------------------+ +// Setup +//--------------------------------------------------------------------+ + +static void setupMassStorage(void) { + flash.begin(); + + // Set disk vendor id, product id and revision with string up to 8, 16, 4 + // characters respectively + usb_msc.setID("Adafruit", "External Flash", "1.0"); + + // Set callback + usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb); + + // Set disk size, block size should be 512 regardless of spi flash page size + usb_msc.setCapacity(flash.size() / 512, 512); + + // MSC is ready for read/write + fs_changed = false; + usb_msc.setReadyCallback(0, msc_ready_callback); + usb_msc.begin(); + + // If already enumerated, additional class driverr begin() e.g msc, hid, midi + // won't take effect until re-enumeration + if (TinyUSBDevice.mounted()) { + TinyUSBDevice.detach(); + delay(10); + TinyUSBDevice.attach(); + } + + // Init file system on the flash + fs_formatted = fatfs.begin(&flash); + + if (!fs_formatted) { + DBG_SERIAL.println( + "Failed to init files system, flash may not be formatted"); + } +} + +static void setupServer(void) { + // WIFI INIT + DBG_SERIAL.printf("Connecting to %s\n", SECRET_SSID); + if (String(WiFi.SSID()) != String(SECRET_SSID)) { + WiFi.mode(WIFI_STA); + WiFi.begin(SECRET_SSID, SECRET_PASSWORD); + } + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + DBG_SERIAL.print("."); + } + DBG_SERIAL.println(""); + DBG_SERIAL.print("Connected! IP address: "); + DBG_SERIAL.println(WiFi.localIP()); + + MDNS.begin(host); + DBG_SERIAL.print("Open http://"); + DBG_SERIAL.print(host); + DBG_SERIAL.println(".local/edit to access the file browser"); + + // SERVER INIT + + // list directory + server.on("/list", HTTP_GET, handleFileList); + + // load editor + server.on("/edit", HTTP_GET, []() { + if (!handleFileRead("/edit.htm")) { + server.send(404, "text/plain", "FileNotFound"); + } + }); + + // create file + server.on("/edit", HTTP_PUT, handleFileCreate); + + // delete file + server.on("/edit", HTTP_DELETE, handleFileDelete); + + // first callback is called after the request has ended with all parsed + // arguments second callback handles file uploads at that location + server.on( + "/edit", HTTP_POST, []() { server.send(200, "text/plain", ""); }, + handleFileUpload); + + // called when the url is not defined here + // use it to load content from fatfs + server.onNotFound([]() { + if (!handleFileRead(server.uri())) { + server.send(404, "text/plain", "FileNotFound"); + } + }); + + // get heap status, analog input value and all GPIO statuses in one json call + server.on("/all", HTTP_GET, []() { + String json = "{"; + json += "\"heap\":" + String(ESP.getFreeHeap()); + json += ", \"analog\":" + String(analogRead(A0)); + json += ", \"gpio\":" + String((uint32_t)(0)); + json += "}"; + server.send(200, "text/json", json); + json = String(); + }); + server.begin(); + DBG_SERIAL.println("HTTP server started"); +} + +void setup() { +#ifdef LED_BUILTIN + pinMode(LED_BUILTIN, OUTPUT); +#endif + + DBG_SERIAL.begin(115200); + + setupMassStorage(); + + // while ( !DBG_SERIAL ) delay(10); // wait for native usb + DBG_SERIAL.println("TinyUSB Mass Storage with ESP32 File Browser example"); + DBG_SERIAL.print("JEDEC ID: 0x"); + DBG_SERIAL.println(flash.getJEDECID(), HEX); + DBG_SERIAL.print("Flash size: "); + DBG_SERIAL.print(flash.size() / 1024); + DBG_SERIAL.println(" KB"); + + setupServer(); +} + +//--------------------------------------------------------------------+ +// Loop +//--------------------------------------------------------------------+ + +void loop() { + server.handleClient(); + delay(2); // allow the cpu to switch to other tasks +} From ec83e905c7b1b328fd92ee1322906067698a20e0 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 5 Oct 2024 23:42:47 +0200 Subject: [PATCH 04/65] Specify FatFS partition. https://github.com/espressif/arduino-esp32/blob/8ce5f775fe928dc4541b0fecaf710de58aa3d198/tools/partitions/default_ffat_8MB.csv https://docs.platformio.org/en/stable/platforms/espressif32.html#partition-tables --- platformio.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio.ini b/platformio.ini index 21933067f..975001f41 100644 --- a/platformio.ini +++ b/platformio.ini @@ -3,6 +3,8 @@ platform = espressif32 board = esp32-s3-devkitc-1 framework = arduino monitor_speed = 115200 +board_build.partitions = default_ffat_8MB.csv +board_build.filesystem = fatfs lib_deps = adafruit/Adafruit TinyUSB Library@^3.3.4 From 5baaa067db4ffaa2e51107d3c38cb5cf8037ae15 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 24 Nov 2024 17:34:39 +0100 Subject: [PATCH 05/65] Set CPP flags to enable USB. https://docs.espressif.com/projects/arduino-esp32/en/latest/troubleshooting.html#id5 --- platformio.ini | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/platformio.ini b/platformio.ini index f797d69b2..64f53a40b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -3,3 +3,11 @@ platform = espressif32 board = esp32-s3-devkitc-1 framework = arduino monitor_speed = 115200 + +build_unflags = + -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=0 + +build_flags = + -DARDUINO_USB_MODE=0 ; set USB Mode to “Hardware CDC and JTAG” + -DARDUINO_USB_CDC_ON_BOOT=1 ; enable USB-CDC on boot From f1c6c5e3e1b3c232744575048f7fce176b86cc02 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 25 Nov 2024 19:40:30 +0100 Subject: [PATCH 06/65] Use example code from installed framework version. Taken from framework-arduinoespressif32 version 3.20017.0. --- src/main.cpp | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3bccd3d6d..649357bd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,4 @@ -#include -#ifndef ARDUINO_USB_MODE -#error This ESP32 SoC has no Native USB interface -#elif ARDUINO_USB_MODE == 1 +#if ARDUINO_USB_MODE #warning This sketch should be used when USB is in OTG mode void setup() {} void loop() {} @@ -9,6 +6,14 @@ void loop() {} #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) @@ -57,8 +62,8 @@ static uint8_t msc_disk[DISK_SECTOR_COUNT][DISK_SECTOR_SIZE] = { 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 + 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) @@ -152,23 +157,23 @@ static uint8_t msc_disk[DISK_SECTOR_COUNT][DISK_SECTOR_SIZE] = { static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) { - Serial.printf("MSC WRITE: lba: %lu, offset: %lu, bufsize: %lu\n", lba, offset, - 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) { - Serial.printf("MSC READ: lba: %lu, offset: %lu, bufsize: %lu\n", lba, offset, - 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) { - Serial.printf("MSC START/STOP: power: %u, start: %u, eject: %u\n", - power_condition, start, load_eject); + HWSerial.printf("MSC START/STOP: power: %u, start: %u, eject: %u\n", + power_condition, start, load_eject); return true; } @@ -178,17 +183,17 @@ static void usbEventCallback(void *arg, esp_event_base_t event_base, arduino_usb_event_data_t *data = (arduino_usb_event_data_t *)event_data; switch (event_id) { case ARDUINO_USB_STARTED_EVENT: - Serial.println("USB PLUGGED"); + HWSerial.println("USB PLUGGED"); break; case ARDUINO_USB_STOPPED_EVENT: - Serial.println("USB UNPLUGGED"); + HWSerial.println("USB UNPLUGGED"); break; case ARDUINO_USB_SUSPEND_EVENT: - Serial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", - data->suspend.remote_wakeup_en); + HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", + data->suspend.remote_wakeup_en); break; case ARDUINO_USB_RESUME_EVENT: - Serial.println("USB RESUMED"); + HWSerial.println("USB RESUMED"); break; default: @@ -198,8 +203,8 @@ static void usbEventCallback(void *arg, esp_event_base_t event_base, } void setup() { - Serial.begin(115200); - Serial.setDebugOutput(true); + HWSerial.begin(115200); + HWSerial.setDebugOutput(true); USB.onEvent(usbEventCallback); MSC.vendorID("ESP32"); // max 8 chars @@ -208,11 +213,9 @@ void setup() { MSC.onStartStop(onStartStop); MSC.onRead(onRead); MSC.onWrite(onWrite); - MSC.mediaPresent(true); - MSC.isWritable(true); // true if writable, false if read-only - MSC.begin(DISK_SECTOR_COUNT, DISK_SECTOR_SIZE); + USBSerial.begin(); USB.begin(); } From f737f3e1d37daa783320199dc413f978854a7d40 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 25 Nov 2024 19:46:51 +0100 Subject: [PATCH 07/65] Misc --- platformio.ini | 2 +- src/main.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index 64f53a40b..a1aa9c640 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,4 +10,4 @@ build_unflags = build_flags = -DARDUINO_USB_MODE=0 ; set USB Mode to “Hardware CDC and JTAG” - -DARDUINO_USB_CDC_ON_BOOT=1 ; enable USB-CDC on boot + -DARDUINO_USB_CDC_ON_BOOT=0 ; disable USB-CDC on boot diff --git a/src/main.cpp b/src/main.cpp index 649357bd7..fdc09e40e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#include #if ARDUINO_USB_MODE #warning This sketch should be used when USB is in OTG mode void setup() {} @@ -62,8 +63,8 @@ static uint8_t msc_disk[DISK_SECTOR_COUNT][DISK_SECTOR_SIZE] = { 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 + 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) From 2cfbed2ebdfcd17108c5b2143dcbf133e046b0bc Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 15 Dec 2024 15:53:33 +0100 Subject: [PATCH 08/65] Adjust flag un-definition. This is probably without effect. But just to be clean. --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index a1aa9c640..02a12129d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,7 +6,7 @@ monitor_speed = 115200 build_unflags = -DARDUINO_USB_MODE=1 - -DARDUINO_USB_CDC_ON_BOOT=0 + -DARDUINO_USB_CDC_ON_BOOT=1 build_flags = -DARDUINO_USB_MODE=0 ; set USB Mode to “Hardware CDC and JTAG” From 3bff418e9c23fa3f8510fe0cb64aae38e4bd7990 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 23 Dec 2024 10:17:02 +0100 Subject: [PATCH 09/65] Resolve conflicting definitions of TinyUSB library. This should deactivate the use Arduino-ESP32 TinyUSB library. This way only the Adafruit version of the TinyUSB library should be used. --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 975001f41..ebd8194c9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,3 +8,4 @@ board_build.filesystem = fatfs lib_deps = adafruit/Adafruit TinyUSB Library@^3.3.4 +build_unflags = -DUSE_TINYUSB From c5b310c40d73cfd8df5543c41a665fb8940f82c3 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 23 Dec 2024 11:14:29 +0100 Subject: [PATCH 10/65] Use v3.3.4 of Adafruit library in order to avoid conflicting definitions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently there are linkage problems with v.3.4.0 🤷 https://github.com/adafruit/Adafruit_TinyUSB_Arduino/issues/473 https://community.platformio.org/t/new-tinyusb-doesnt-allow-compile/44281 Note: When using `@3.3.4` as version specification with platformio v3.4.0 is currently used. --- platformio.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index ebd8194c9..ce567cf45 100644 --- a/platformio.ini +++ b/platformio.ini @@ -7,5 +7,4 @@ board_build.partitions = default_ffat_8MB.csv board_build.filesystem = fatfs lib_deps = - adafruit/Adafruit TinyUSB Library@^3.3.4 -build_unflags = -DUSE_TINYUSB + adafruit/Adafruit TinyUSB Library@3.3.4 From 15175cd268d427d2469a1d39296cd72ef315b2eb Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Feb 2025 14:52:10 +0100 Subject: [PATCH 11/65] Specify exception decoder for monitor This helps debugging backtraces printed to UART --- platformio.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio.ini b/platformio.ini index ce567cf45..dd3ea2eeb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,3 +8,5 @@ board_build.filesystem = fatfs lib_deps = adafruit/Adafruit TinyUSB Library@3.3.4 + +monitor_filters = esp32_exception_decoder From 8956a150211f0dc5df465090b77eb13ff83af2ed Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Feb 2025 18:01:49 +0100 Subject: [PATCH 12/65] Add overview of USB options Based on: - https://raw.githubusercontent.com/espressif/arduino-esp32/refs/heads/idf-release/v5.3/platform.txt - https://raw.githubusercontent.com/espressif/arduino-esp32/refs/heads/idf-release/v5.3/boards.txt --- usb_options.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 usb_options.md diff --git a/usb_options.md b/usb_options.md new file mode 100644 index 000000000..3435a49ea --- /dev/null +++ b/usb_options.md @@ -0,0 +1,41 @@ +# Overview of Build Options for ESP32-S3 + +## 1. USB Mode (`ARDUINO_USB_MODE`) + +| Value | Label | +|------|----------------------------| +| `0` | USB-OTG (TinyUSB) (Default) | +| `1` | Hardware CDC and JTAG | + +**Default value:** `0` +**Macro:** `-DARDUINO_USB_MODE={build.usb_mode}` + +## 2. USB CDC On Boot (`ARDUINO_USB_CDC_ON_BOOT`) + +| Value | Label | +|------|------------| +| `0` | Disabled (Default) | +| `1` | Enabled | + +**Default value:** `0` +**Macro:** `-DARDUINO_USB_CDC_ON_BOOT={build.cdc_on_boot}` + +## 3. USB Firmware MSC On Boot (`ARDUINO_USB_MSC_ON_BOOT`) + +| Value | Label | +|------|--------------------------------| +| `0` | Disabled (Default) | +| `1` | Enabled (Requires USB-OTG Mode) | + +**Default value:** `0` +**Macro:** `-DARDUINO_USB_MSC_ON_BOOT={build.msc_on_boot}` + +## 4. USB DFU On Boot (`ARDUINO_USB_DFU_ON_BOOT`) + +| Value | Label | +|------|--------------------------------| +| `0` | Disabled (Default) | +| `1` | Enabled (Requires USB-OTG Mode) | + +**Default value:** `0` +**Macro:** `-DARDUINO_USB_DFU_ON_BOOT={build.dfu_on_boot}` From 12cd45323622a0b1099eca19337b59998d4ab145 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Feb 2025 18:04:23 +0100 Subject: [PATCH 13/65] Apply USB options from example See https://youtu.be/hJSBTFsOnoA?feature=shared&t=588 --- platformio.ini | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index 02a12129d..99a749b1e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,8 +6,13 @@ monitor_speed = 115200 build_unflags = -DARDUINO_USB_MODE=1 - -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_CDC_ON_BOOT=0 + -DARDUINO_USB_MSC_ON_BOOT=1 + -DARDUINO_USB_DFU_ON_BOOT=1 build_flags = - -DARDUINO_USB_MODE=0 ; set USB Mode to “Hardware CDC and JTAG” - -DARDUINO_USB_CDC_ON_BOOT=0 ; disable USB-CDC on boot + -DARDUINO_USB_MODE=0 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MSC_ON_BOOT=0 + -DARDUINO_USB_DFU_ON_BOOT=0 + From 9e011edf9d105ae605bf133da6a5f937addbd1d5 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 10 Feb 2025 20:52:24 +0100 Subject: [PATCH 14/65] Pin the platform version. As this is the version the example code has been used from. --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 99a749b1e..aa1f2a52b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,5 +1,5 @@ [env:esp32-s3-devkitc-1] -platform = espressif32 +platform = espressif32@6.9.0 board = esp32-s3-devkitc-1 framework = arduino monitor_speed = 115200 From 214160a88b366f7853d7d52489dbaf71cca0def9 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 16 Feb 2025 16:23:23 +0100 Subject: [PATCH 15/65] Generate empty Arduino project for ESP32-S3-DevKitC-1 Automatically generated by PlatformIO Home. --- .gitignore | 5 +++++ platformio.ini | 14 ++++++++++++++ src/main.cpp | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 .gitignore create mode 100644 platformio.ini create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..89cc49cbd --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 000000000..fba81db3b --- /dev/null +++ b/platformio.ini @@ -0,0 +1,14 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32-s3-devkitc-1] +platform = espressif32 +board = esp32-s3-devkitc-1 +framework = arduino diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 000000000..cb9fbba46 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,18 @@ +#include + +// put function declarations here: +int myFunction(int, int); + +void setup() { + // put your setup code here, to run once: + int result = myFunction(2, 3); +} + +void loop() { + // put your main code here, to run repeatedly: +} + +// put function definitions here: +int myFunction(int x, int y) { + return x + y; +} \ No newline at end of file From 3aa20b05c96b855824753d0e5fed0324769b06be Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 16 Feb 2025 16:30:54 +0100 Subject: [PATCH 16/65] Add test for FatFS --- platformio.ini | 4 ++++ src/main.cpp | 43 +++++++++++++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/platformio.ini b/platformio.ini index fba81db3b..ea8e5d83a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,3 +12,7 @@ platform = espressif32 board = esp32-s3-devkitc-1 framework = arduino +board_build.filesystem = fatfs +board_build.partitions = default_ffat_8MB.csv +monitor_filters = esp32_exception_decoder +monitor_speed = 115200 diff --git a/src/main.cpp b/src/main.cpp index cb9fbba46..c9b81c083 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,41 @@ #include +#include -// put function declarations here: -int myFunction(int, int); +static void testFatFS() { + File file = FFat.open("/test.txt", "w"); + if (!file) { + Serial.println("Fehler beim Öffnen der Datei!"); + return; + } + file.println("Hallo FatFS!"); + file.close(); + + file = FFat.open("/test.txt", "r"); + if (file) { + Serial.println("Inhalt der Datei:"); + while (file.available()) { + Serial.write(file.read()); + } + Serial.println(); + file.close(); + } else { + Serial.println("Konnte Datei nicht lesen."); + } +} void setup() { - // put your setup code here, to run once: - int result = myFunction(2, 3); + Serial.begin(115200); + + if (!FFat.begin( + true)) { // `true` = Formatieren falls kein Dateisystem vorhanden + Serial.println("FatFS konnte nicht gestartet werden!"); + return; + } + + Serial.println("FatFS erfolgreich gemountet."); } void loop() { - // put your main code here, to run repeatedly: + testFatFS(); + delay(10000); } - -// put function definitions here: -int myFunction(int x, int y) { - return x + y; -} \ No newline at end of file From 26b4dcd4578705e62e2ccf64e43bbf8bf2171c24 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 16 Feb 2025 22:23:39 +0100 Subject: [PATCH 17/65] WIP: Use partition instead of Array in RAM --- src/main.cpp | 135 ++++++++------------------------------------------- 1 file changed, 20 insertions(+), 115 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index fdc09e40e..68b41d082 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,6 @@ #include +#include +#include #if ARDUINO_USB_MODE #warning This sketch should be used when USB is in OTG mode void setup() {} @@ -27,7 +29,7 @@ USBMSC MSC; 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)) + 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) @@ -42,125 +44,13 @@ 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 const esp_partition_t *fatPartition = nullptr; 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); + esp_partition_write(fatPartition, offset, buffer, bufsize); return bufsize; } @@ -203,10 +93,25 @@ static void usbEventCallback(void *arg, esp_event_base_t event_base, } } +const esp_partition_t * +check_ffat_partition(const char *label); // defined in FFat.cpp + void setup() { HWSerial.begin(115200); HWSerial.setDebugOutput(true); + if (!FFat.begin( + true)) { // `true` = Formatieren falls kein Dateisystem vorhanden + HWSerial.println("FatFS konnte nicht gestartet werden!"); + return; + } + + fatPartition = check_ffat_partition(FFAT_PARTITION_LABEL); + if (!fatPartition) { + printf("Error with FAT partition"); + return; + } + USB.onEvent(usbEventCallback); MSC.vendorID("ESP32"); // max 8 chars MSC.productID("USB_MSC"); // max 16 chars From 2801511cd4f91b2aa1a2f8492b7e3391cabfd8d4 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Tue, 18 Feb 2025 09:18:05 +0100 Subject: [PATCH 18/65] WIP: Add flag for "filesystem ready" --- src/main.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 68b41d082..1bf7c7c8f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #if ARDUINO_USB_MODE #warning This sketch should be used when USB is in OTG mode @@ -19,25 +20,6 @@ USBCDC USBSerial; 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 @@ -58,7 +40,7 @@ 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); + esp_partition_read(fatPartition, offset, buffer, bufsize); return bufsize; } @@ -68,6 +50,16 @@ static bool onStartStop(uint8_t power_condition, bool start, bool load_eject) { return true; } +static std::atomic_bool fs_changed{false}; + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool msc_ready_callback(void) { + // if fs has changed, mark unit as not ready temporarily to force PC to flush + // cache + return !fs_changed.exchange(false); +} + static void usbEventCallback(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == ARDUINO_USB_EVENTS) { From 8b1f0ed81b1395c11647fbbf92847071b2967f07 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Tue, 18 Feb 2025 12:01:39 +0100 Subject: [PATCH 19/65] Substitute Adafruit_SPIFlash member functions with FFat member functions --- src/main.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6fe23d04d..c43d72a9c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,11 +72,16 @@ bool exists(String path) { return yes; } +static void rebootFs() { + FFat.end(); + FFat.begin(); +} + // Callback invoked when WRITE10 command is completed (status received and // accepted by host). used to flush any pending cache. void msc_flush_cb(void) { // sync with flash - flash.syncBlocks(); + rebootFs(); // clear file system's cache to force refresh fatfs.cacheClear(); @@ -138,9 +143,7 @@ void setup() { printf("Error with FAT partition"); return; } - DBG_SERIAL.print("Flash size: "); - DBG_SERIAL.print(flash.size() / 1024); - DBG_SERIAL.println(" KB"); + HWSerial.printf("Flash has a size of %u bytes\n", FFat.totalBytes()); USB.onEvent(usbEventCallback); MSC.vendorID("ESP32"); // max 8 chars @@ -157,8 +160,7 @@ void setup() { MSC.mediaPresent(true); // Set disk size, block size should be 512 regardless of spi flash page size - usb_msc.setCapacity(flash.size() / 512, 512); - MSC.begin(DISK_SECTOR_COUNT, DISK_SECTOR_SIZE); + MSC.begin(FFat.totalBytes() / DISK_SECTOR_SIZE, DISK_SECTOR_SIZE); USBSerial.begin(); USB.begin(); } From 8722fd9e109757b322b08e7d89e6167e60fba2d1 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Wed, 19 Feb 2025 07:32:21 +0100 Subject: [PATCH 20/65] WIP: replace fs_changed with mediaPresent() as we can not subscribe msc_ready_callback --- src/main.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c43d72a9c..11704d4fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #if ARDUINO_USB_MODE #warning This sketch should be used when USB is in OTG mode @@ -58,9 +57,7 @@ static bool onStartStop(uint8_t power_condition, bool start, bool load_eject) { return true; } -static std::atomic_bool fs_changed{false}; - -static void refreshMassStorage(void) { fs_changed = true; } +static void refreshMassStorage(void) { MSC.mediaPresent(false); } bool exists(String path) { bool yes = false; @@ -79,7 +76,8 @@ static void rebootFs() { // Callback invoked when WRITE10 command is completed (status received and // accepted by host). used to flush any pending cache. -void msc_flush_cb(void) { +void tud_msc_write10_complete_cb(void) { + MSC.mediaPresent(true); // sync with flash rebootFs(); @@ -91,14 +89,6 @@ void msc_flush_cb(void) { #endif } -// Invoked when received Test Unit Ready command. -// return true allowing host to read/write this LUN e.g SD card inserted -bool msc_ready_callback(void) { - // if fs has changed, mark unit as not ready temporarily to force PC to flush - // cache - return !fs_changed.exchange(false); -} - static void usbEventCallback(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == ARDUINO_USB_EVENTS) { @@ -155,8 +145,6 @@ void setup() { // Set callback usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb); // MSC is ready for read/write - fs_changed = false; - usb_msc.setReadyCallback(0, msc_ready_callback); MSC.mediaPresent(true); // Set disk size, block size should be 512 regardless of spi flash page size From a5fe1ee3771bf27aaad4a3784dee0bd07ac8340c Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 22 Feb 2025 20:07:58 +0100 Subject: [PATCH 21/65] Remove unavailable callbacks Some callbacks are simply not available using only the Arduino-ESP32 framework --- src/main.cpp | 73 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 11704d4fe..38d575dfb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,8 +6,13 @@ void setup() {} void loop() {} #else -#include "USB.h" -#include "USBMSC.h" +#include +#include +#include +#include + +static const std::filesystem::path ffat_base_path = + std::filesystem::path().append(FFAT_PARTITION_LABEL); #if ARDUINO_USB_CDC_ON_BOOT #define HWSerial Serial0 @@ -34,8 +39,9 @@ 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); - esp_partition_write(fatPartition, offset, buffer, - bufsize); // TODO handle error codes + // erase must be called before write + ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, offset, bufsize)); + ESP_ERROR_CHECK(esp_partition_write(fatPartition, offset, buffer, bufsize)); return bufsize; } @@ -46,8 +52,7 @@ 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); - esp_partition_read(fatPartition, offset, buffer, - bufsize); // TODO handle error codes + ESP_ERROR_CHECK(esp_partition_read(fatPartition, offset, buffer, bufsize)); return bufsize; } @@ -57,36 +62,40 @@ static bool onStartStop(uint8_t power_condition, bool start, bool load_eject) { return true; } -static void refreshMassStorage(void) { MSC.mediaPresent(false); } +/** + * Lists files and directories at path. + */ +static void listFiles(const char *const dirname) { + Serial.printf("Directory: '%s'\n", dirname); + File root = FFat.open(dirname); + if (!root || !root.isDirectory()) { + HWSerial.printf("Error: '%s' is not a directory!\n", dirname); + return; + } -bool exists(String path) { - bool yes = false; - File32 file = fatfs.open(path, O_READ); - if (file && !file.isDirectory()) { - yes = true; + File file = root.openNextFile(); + while (file) { + HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), + file.isDirectory() ? "d" : "f", file.size()); + file = root.openNextFile(); } - file.close(); - return yes; } -static void rebootFs() { - FFat.end(); - FFat.begin(); +/** + * Switch from USB MSC to application mode (file system). + */ +static void switchToApplicationMode() { + FFat.end(); // invalidate cache + MSC.mediaPresent(false); + FFat.begin(); // update data } -// Callback invoked when WRITE10 command is completed (status received and -// accepted by host). used to flush any pending cache. -void tud_msc_write10_complete_cb(void) { +/** + * Switch from application mode (file system) to USB MSC. + */ +static void switchToUSBMode() { + FFat.end(); // flush and unmount MSC.mediaPresent(true); - // sync with flash - rebootFs(); - - // clear file system's cache to force refresh - fatfs.cacheClear(); - -#ifdef LED_BUILTIN - digitalWrite(LED_BUILTIN, LOW); -#endif } static void usbEventCallback(void *arg, esp_event_base_t event_base, @@ -96,9 +105,12 @@ static void usbEventCallback(void *arg, esp_event_base_t event_base, switch (event_id) { case ARDUINO_USB_STARTED_EVENT: HWSerial.println("USB PLUGGED"); + switchToUSBMode(); break; case ARDUINO_USB_STOPPED_EVENT: HWSerial.println("USB UNPLUGGED"); + switchToApplicationMode(); + listFiles(ffat_base_path.c_str()); break; case ARDUINO_USB_SUSPEND_EVENT: HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", @@ -140,10 +152,9 @@ void setup() { MSC.productID("USB_MSC"); // max 16 chars MSC.productRevision("1.0"); // max 4 chars MSC.onStartStop(onStartStop); + // Set callback MSC.onRead(onRead); MSC.onWrite(onWrite); - // Set callback - usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb); // MSC is ready for read/write MSC.mediaPresent(true); From 9badb591971b3e84e0a53aa56a506ca5970e1e33 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 22 Feb 2025 20:23:55 +0100 Subject: [PATCH 22/65] Omit usage of std::filesystem Is apparently not included in the C++ Standard Library implementation --- src/main.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 38d575dfb..4ae704342 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,10 +9,8 @@ void loop() {} #include #include #include -#include -static const std::filesystem::path ffat_base_path = - std::filesystem::path().append(FFAT_PARTITION_LABEL); +static const char *const FFAT_BASE_PATH = "/" FFAT_PARTITION_LABEL; #if ARDUINO_USB_CDC_ON_BOOT #define HWSerial Serial0 @@ -110,7 +108,7 @@ static void usbEventCallback(void *arg, esp_event_base_t event_base, case ARDUINO_USB_STOPPED_EVENT: HWSerial.println("USB UNPLUGGED"); switchToApplicationMode(); - listFiles(ffat_base_path.c_str()); + listFiles(FFAT_BASE_PATH); break; case ARDUINO_USB_SUSPEND_EVENT: HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", From aa08b54544dc12616c75b71437dfe419404176d6 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 22 Feb 2025 20:27:18 +0100 Subject: [PATCH 23/65] Auxiliary commit to revert individual files from efdad5a4420dd61709eb9e8b8b9af260bc4c7121 This reverts commit bcca8ab37f4f06f32c8158948956261fb9c79f93, reversing changes made to 7602e1c783597339a87010bacbaa19ead1f29d33. --- platformio.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platformio.ini b/platformio.ini index 05d7aba31..66315f1fb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,6 +2,9 @@ platform = espressif32@6.9.0 board = esp32-s3-devkitc-1 framework = arduino +board_build.filesystem = fatfs +board_build.partitions = default_ffat_8MB.csv +monitor_filters = esp32_exception_decoder monitor_speed = 115200 build_unflags = From 38018732ef8da8f1650a0e432ef77529327c3d12 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 22 Feb 2025 21:31:54 +0100 Subject: [PATCH 24/65] Move switched between application and USB MSC to tasks --- src/main.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 4ae704342..970487ceb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,8 +10,6 @@ void loop() {} #include #include -static const char *const FFAT_BASE_PATH = "/" FFAT_PARTITION_LABEL; - #if ARDUINO_USB_CDC_ON_BOOT #define HWSerial Serial0 #define USBSerial Serial @@ -64,7 +62,7 @@ static bool onStartStop(uint8_t power_condition, bool start, bool load_eject) { * Lists files and directories at path. */ static void listFiles(const char *const dirname) { - Serial.printf("Directory: '%s'\n", dirname); + HWSerial.printf("Directory: '%s'\n", dirname); File root = FFat.open(dirname); if (!root || !root.isDirectory()) { HWSerial.printf("Error: '%s' is not a directory!\n", dirname); @@ -96,19 +94,32 @@ static void switchToUSBMode() { MSC.mediaPresent(true); } +static void usb_stopped_cb(void *const pvParameters) { + switchToApplicationMode(); + listFiles("/"); + vTaskDelete(nullptr); +} + +static void usb_started_cb(void *const pvParameters) { + switchToUSBMode(); + vTaskDelete(nullptr); +} + 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; + static int32_t old_event_id = ARDUINO_USB_ANY_EVENT; + if (old_event_id == event_id) + return; switch (event_id) { case ARDUINO_USB_STARTED_EVENT: HWSerial.println("USB PLUGGED"); - switchToUSBMode(); + xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); break; case ARDUINO_USB_STOPPED_EVENT: HWSerial.println("USB UNPLUGGED"); - switchToApplicationMode(); - listFiles(FFAT_BASE_PATH); + xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); break; case ARDUINO_USB_SUSPEND_EVENT: HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", @@ -121,6 +132,7 @@ static void usbEventCallback(void *arg, esp_event_base_t event_base, default: break; } + old_event_id = event_id; } } @@ -164,5 +176,10 @@ void setup() { void loop() { // put your main code here, to run repeatedly: + static uint32_t lastTrigger = 0; + if (millis() - lastTrigger > 1000) { + HWSerial.print("."); + lastTrigger = millis(); + } } #endif /* ARDUINO_USB_MODE */ From d58900794d9e4093236e3df1f3e3d630592167e3 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 22 Feb 2025 21:32:13 +0100 Subject: [PATCH 25/65] Add example file for file system --- data/usb_options.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 data/usb_options.md diff --git a/data/usb_options.md b/data/usb_options.md new file mode 100644 index 000000000..3435a49ea --- /dev/null +++ b/data/usb_options.md @@ -0,0 +1,41 @@ +# Overview of Build Options for ESP32-S3 + +## 1. USB Mode (`ARDUINO_USB_MODE`) + +| Value | Label | +|------|----------------------------| +| `0` | USB-OTG (TinyUSB) (Default) | +| `1` | Hardware CDC and JTAG | + +**Default value:** `0` +**Macro:** `-DARDUINO_USB_MODE={build.usb_mode}` + +## 2. USB CDC On Boot (`ARDUINO_USB_CDC_ON_BOOT`) + +| Value | Label | +|------|------------| +| `0` | Disabled (Default) | +| `1` | Enabled | + +**Default value:** `0` +**Macro:** `-DARDUINO_USB_CDC_ON_BOOT={build.cdc_on_boot}` + +## 3. USB Firmware MSC On Boot (`ARDUINO_USB_MSC_ON_BOOT`) + +| Value | Label | +|------|--------------------------------| +| `0` | Disabled (Default) | +| `1` | Enabled (Requires USB-OTG Mode) | + +**Default value:** `0` +**Macro:** `-DARDUINO_USB_MSC_ON_BOOT={build.msc_on_boot}` + +## 4. USB DFU On Boot (`ARDUINO_USB_DFU_ON_BOOT`) + +| Value | Label | +|------|--------------------------------| +| `0` | Disabled (Default) | +| `1` | Enabled (Requires USB-OTG Mode) | + +**Default value:** `0` +**Macro:** `-DARDUINO_USB_DFU_ON_BOOT={build.dfu_on_boot}` From e9036b57fd0d46eeab5793067b6f3163d65d90e8 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 22 Feb 2025 21:54:37 +0100 Subject: [PATCH 26/65] Fix incorrect offset calculation for USB MSC --- src/main.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 970487ceb..6f91efe51 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,9 +35,11 @@ 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); + uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; // erase must be called before write - ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, offset, bufsize)); - ESP_ERROR_CHECK(esp_partition_write(fatPartition, offset, buffer, bufsize)); + ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, byteOffset, bufsize)); + ESP_ERROR_CHECK( + esp_partition_write(fatPartition, byteOffset, buffer, bufsize)); return bufsize; } @@ -48,7 +50,9 @@ 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); - ESP_ERROR_CHECK(esp_partition_read(fatPartition, offset, buffer, bufsize)); + uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; + ESP_ERROR_CHECK( + esp_partition_read(fatPartition, byteOffset, buffer, bufsize)); return bufsize; } From b606d6f0387d51c172889de65e01a2a0354f39dc Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 23 Feb 2025 15:44:07 +0100 Subject: [PATCH 27/65] Document the decisions made regarding USB MSC integration with FFat on the ESP32-S3 --- doc/decisions/dr-005.md | 100 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 doc/decisions/dr-005.md diff --git a/doc/decisions/dr-005.md b/doc/decisions/dr-005.md new file mode 100644 index 000000000..88f9db76e --- /dev/null +++ b/doc/decisions/dr-005.md @@ -0,0 +1,100 @@ +# USB MSC Integration with FFat on ESP32-S3 + +## Context + +This ADR documents the analysis, findings, and decisions regarding the implementation of **USB Mass Storage Class (MSC)** support on an **ESP32-S3** device using **FFat (ESP32 FAT on Flash File System)**. The objective is to enable the ESP32 to act as a mass storage device, allowing: + +- **USB host (e.g., a computer) to access stored files** as if the ESP32 were a USB flash drive. +- **The ESP32 application to access and modify files via FFat.** +- **Synchronization between USB MSC access and internal application access.** + +The project is built with **PlatformIO** and uses the **Arduino-ESP32** framework. + +## Considered Approaches + +### Existing Examples & Research + +Several existing projects and examples were analyzed for feasibility: + +1. [Espressif's "TinyUSB Mass Storage Device Example" (ESP-IDF)](https://github.com/espressif/esp-idf/tree/v5.3.2/examples/peripherals/usb/device/tusb_msc#tinyusb-mass-storage-device-example) + - Uses `esp_tinyusb`, which integrates with ESP-IDF. + - Relies on `esp_partition_write()` via ESP-IDF’s Wear Leveling API. + - **Issue:** Requires ESP-IDF; not directly compatible with Arduino-ESP32. + +2. [chegewara/EspTinyUSB "flashdisk" Example](https://github.com/chegewara/EspTinyUSB/blob/f4d63153c0417922398de2fe43114b5224ff6def/examples/device/msc/flashdisk/flashdisk.ino) + - Implements USB MSC with `disk_read()` and `disk_write()` from ESP-IDF FatFS. + - **Issue:** Library is not well-maintained and might be unstable. + +3. [Espressif's "SD2USBMSC" Example](https://raw.githubusercontent.com/espressif/arduino-esp32/a7907cd07e605e4e9e37f0ec862e7da7a145fa38/libraries/SD_MMC/examples/SD2USBMSC/SD2USBMSC.ino) + - Uses SD card storage with raw block access. + - **Issue:** Designed for SD cards, not for internal flash. + +4. [Adafruit's "msc_esp32_file_browser" Example](https://raw.githubusercontent.com/adafruit/Adafruit_TinyUSB_Arduino/refs/tags/3.4.2/examples/MassStorage/msc_esp32_file_browser/msc_esp32_file_browser.ino) + - Exposes flash storage via USB MSC and a web server. + - Uses `Adafruit_SPIFlash`, which relies on TinyUSB. + - **Issue:** Introduces additional dependencies; Arduino-ESP32 already includes TinyUSB, causing conflicts. + +### Observations & Problems Identified + +- Arduino-ESP32 framework includes TinyUSB, but PlatformIO does not allow excluding components. +- FFat does not expose `disk_read()` or `disk_write()` for direct block-level access. +- Direct partition access (`esp_partition_read()` and `esp_partition_write()`) is needed for USB MSC. +- Concurrency between USB MSC and FFat must be managed to avoid filesystem corruption. +- ESP-IDF solutions are not directly compatible with Arduino-ESP32. + +## Decision + +**Chosen Approach:** Direct Partition Access with FFat Synchronization + +- USB MSC reads/writes directly to the flash partition using `esp_partition_write()` and `esp_partition_read()`. +- Application accesses files via FFat, using `FFat.begin()` and `FFat.end()` for synchronization. +- Synchronization via unmount/mount cycle (`FFat.end()` before USB access, `FFat.begin()` after). +- FreeRTOS Task for File System Operations + - A dedicated FreeRTOS task processes USB events. + - USB events do not perform filesystem operations directly. + +## Implementation Details + +### Core Components + +- USB MSC Read/Write Callbacks +- File System Synchronization Task + +## Alternative Considered but Rejected + +| Approach | Reason for Rejection | +|----------|----------------------| +| Using `esp_tinyusb` | Requires ESP-IDF, not compatible with Arduino-ESP32 | +| Adafruit TinyUSB | Introduces conflicts with built-in TinyUSB in Arduino-ESP32 | +| Direct FFat Access via `fread()` | FFat does not support block-level read/write | + +## Consequences + +### Positive Outcomes + +- ✅ Works within **Arduino-ESP32 and PlatformIO**. +- ✅ Uses **built-in ESP32 partition management**. +- ✅ Keeps **dependencies minimal**. +- ✅ Provides **clean USB MSC integration**. + +### Potential Issues + +- ⚠ **FFat remounting may cause latency** when switching access modes. +- ⚠ **USB MSC cannot modify FFat metadata dynamically**, requiring unmount/remount. +- ⚠ **Large writes via USB MSC could impact flash lifespan** as no wear leveling is used. + +## References + +- [Task-Tracker-Device Issue #6](https://github.com/Task-Tracker-Systems/Task-Tracker-Device/issues/6) +- [Espressif TinyUSB MSC Example](https://github.com/espressif/esp-idf/tree/v5.3.2/examples/peripherals/usb/device/tusb_msc) +- [Adafruit TinyUSB Arduino](https://github.com/adafruit/Adafruit_TinyUSB_Arduino) +- [ESP32 FatFS](https://github.com/espressif/esp-idf/tree/c71d74e2f853b1135c63f47e349f2a08f63f3e01/components/fatfs) +- [USB MSC example from Espressif for Arduino-ESP32](https://github.com/espressif/arduino-esp32/blob/988dbe29731e2a2d09db2ed642c06271afa93705/libraries/USB/examples/USBMSC/USBMSC.ino) +- [issue in Adafruit_TinyUSB_Arduino with macro redefinitions for ESP32S2 and ESP32S3 (#484)](https://github.com/adafruit/Adafruit_TinyUSB_Arduino/issues/484) +- [issue in Adafruit_TinyUSB_Arduino with Linker Error on esp32s3 with platformIO (#473)](https://github.com/adafruit/Adafruit_TinyUSB_Arduino/issues/473) +- [example for using USB MSC for firmware updates](https://github.com/espressif/arduino-esp32/blob/988dbe29731e2a2d09db2ed642c06271afa93705/libraries/USB/examples/FirmwareMSC/FirmwareMSC.ino) +- [example for using LittleFS with PlatformIO and Arduino-ESP32](https://github.com/espressif/arduino-esp32/tree/6ce43695d2591e600dc906eeb9e30a9ca3c71921/libraries/LittleFS/examples/LITTLEFS_PlatformIO) +- ["FSBrowser" example providing SPIFFS via Web](https://raw.githubusercontent.com/espressif/arduino-esp32/a7907cd07e605e4e9e37f0ec862e7da7a145fa38/libraries/WebServer/examples/FSBrowser/FSBrowser.ino) +- [Espressif's "TinyUSB Mass Storage Device Example" für ESP-IDF](https://github.com/espressif/esp-idf/tree/v5.3.2/examples/peripherals/usb/device/tusb_msc#tinyusb-mass-storage-device-example) +- [Espressif's additions to TinyUSB "esp_tinyusb"](https://components.espressif.com/components/espressif/esp_tinyusb) extends [espressif/tinyusb](https://github.com/espressif/tinyusb) +- [Wear Levelling API](https://github.com/espressif/esp-idf/tree/1160a86ba0b87b0ea68ad6b91eb10fe9c34ad692/components/wear_levelling) From 56a495e3a0dbc79295234f1740467ec76ce7bbfd Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 23 Feb 2025 17:17:49 +0100 Subject: [PATCH 28/65] Add minor details --- src/main.cpp | 4 ++-- usb_options.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6f91efe51..81984d3da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#if ARDUINO_USB_MODE +#if ARDUINO_USB_MODE == 1 #warning This sketch should be used when USB is in OTG mode void setup() {} void loop() {} @@ -10,7 +10,7 @@ void loop() {} #include #include -#if ARDUINO_USB_CDC_ON_BOOT +#if ARDUINO_USB_CDC_ON_BOOT == 1 #define HWSerial Serial0 #define USBSerial Serial #else diff --git a/usb_options.md b/usb_options.md index 3435a49ea..ccb62c9d7 100644 --- a/usb_options.md +++ b/usb_options.md @@ -1,5 +1,7 @@ # Overview of Build Options for ESP32-S3 +See also [documentation on USB options](https://docs.espressif.com/projects/arduino-esp32/en/latest/guides/tools_menu.html#usb-options). + ## 1. USB Mode (`ARDUINO_USB_MODE`) | Value | Label | From 1f9c5783a8f86d727b2b52676a62cc495f31e165 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 23 Feb 2025 17:41:48 +0100 Subject: [PATCH 29/65] Misc --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 81984d3da..5ea9556d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -85,8 +85,8 @@ static void listFiles(const char *const dirname) { * Switch from USB MSC to application mode (file system). */ static void switchToApplicationMode() { - FFat.end(); // invalidate cache MSC.mediaPresent(false); + FFat.end(); // invalidate cache FFat.begin(); // update data } From 50a091228a4fecc5e048dd28e1f12158d8796b59 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Tue, 25 Feb 2025 08:32:22 +0100 Subject: [PATCH 30/65] Begin to declare interface to storage --- lib/storage/storage.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 lib/storage/storage.hpp diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp new file mode 100644 index 000000000..165f560c4 --- /dev/null +++ b/lib/storage/storage.hpp @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include +#include + +class Storage +{ + public: + void begin(const char *partitionLabel = FFAT_PARTITION_LABEL); + void end(); + bool isFileSystemReady() const; + void setCallbackFsReady(); + void setCallbackFsBusy(); + + private: + const esp_partition_t *partition; + /** + * Interface to USB mass storage class. + */ + USBMSC usbMsc; + const std::size_t blockSize; +}; \ No newline at end of file From 50cfe3a64b9e58a89efe1628b0c872fb58b1033f Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 2 Mar 2025 17:16:24 +0100 Subject: [PATCH 31/65] WIP: Design non-static storage class --- lib/storage/storage.cpp | 35 +++++++++++++++++++++++++++++++++++ lib/storage/storage.hpp | 25 ++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 lib/storage/storage.cpp diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp new file mode 100644 index 000000000..dc9d8a840 --- /dev/null +++ b/lib/storage/storage.cpp @@ -0,0 +1,35 @@ +#include "storage.hpp" +#include + +const esp_partition_t *check_ffat_partition(const char *label); // defined in FFat.cpp + +Storage::Storage(const bool formatFsOnFail, const char *const partitionLabel, ESPUSB &usb, fs::F_Fat &fs, const std::size_t blockSize): +partition(check_ffat_partition(partitionLabel)), +blockSize(blockSize), +fs(fs), +usb(usb) +{ + if(!partition){ + std::cerr << "Error with partition!" << std::endl; + return; + } + + // initialize file system + const auto basePath = rootPath + partitionLabel; + constexpr auto maxOpenFiles = 10; + if(!fs.begin(formatFsOnFail, basePath.c_str(), maxOpenFiles, partitionLabel)){ + std::cerr << "File-system initialization failed!" << std::endl; + return; + } + std::cout << "Storage has a size of " << size() << " bytes." << std::endl; + + // subscribe to USB events + callbackSubscribers.push_back(this); + usb.onEvent(ARDUINO_USB_STARTED_EVENT, callbackUsbStarted_s); + usb.onEvent(ARDUINO_USB_STOPPED_EVENT, callbackUsbStopped_s); +} + +std::size_t Storage::size() const +{ + return fs.totalBytes(); +} diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp index 165f560c4..9c7ce5291 100644 --- a/lib/storage/storage.hpp +++ b/lib/storage/storage.hpp @@ -1,17 +1,29 @@ #pragma once #include +#include #include #include #include +#include +#include +#include class Storage { public: - void begin(const char *partitionLabel = FFAT_PARTITION_LABEL); - void end(); + Storage(bool formatFsOnFail = false, const char *partitionLabel = FFAT_PARTITION_LABEL, ESPUSB &usb = USB, + fs::F_Fat &fs = FFat, std::size_t blockSize = 512); bool isFileSystemReady() const; void setCallbackFsReady(); void setCallbackFsBusy(); + std::size_t size() const; + std::string getFileContents(const char *path) const; + ~Storage(); + + static constexpr std::string rootPath = "/"; + + protected: + fs::FS &getFileSystem(); private: const esp_partition_t *partition; @@ -20,4 +32,11 @@ class Storage */ USBMSC usbMsc; const std::size_t blockSize; -}; \ No newline at end of file + fs::F_Fat &fs; + ESPUSB &usb; + static std::vector callbackSubscribers; + static esp_event_handler_t callbackUsbStarted_s; + void callbackUsbStarted(); + static esp_event_handler_t callbackUsbStopped_s; + void callbackUsbStopped(); +}; From 7b1882dfc494103deb65ff28cbc24641f5daa6b0 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 2 Mar 2025 18:42:19 +0100 Subject: [PATCH 32/65] Move storage functions to class --- lib/storage/storage.cpp | 170 ++++++++++++++++++++++++++---- lib/storage/storage.hpp | 44 +++----- src/main.cpp | 226 +++++++++------------------------------- 3 files changed, 212 insertions(+), 228 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index dc9d8a840..d5fde09be 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -1,35 +1,159 @@ #include "storage.hpp" +#include +#include +#include +#include #include +#include + +#if defined(ARDUINO_USB_MODE) +static_assert(ARDUINO_USB_MODE == 0, "USB must be in OTG mode"); +#endif const esp_partition_t *check_ffat_partition(const char *label); // defined in FFat.cpp -Storage::Storage(const bool formatFsOnFail, const char *const partitionLabel, ESPUSB &usb, fs::F_Fat &fs, const std::size_t blockSize): -partition(check_ffat_partition(partitionLabel)), -blockSize(blockSize), -fs(fs), -usb(usb) +static const std::string rootPath = "/"; +static constexpr std::size_t blockSize = 512; // bytes + +static const esp_partition_t *partition; +static USBMSC usbMsc; +static bool usbIsRunning = false; +static bool fileSystemIsReady = false; + +static void callbackUsbStarted(void *, esp_event_base_t, int32_t event_id, void *) +{ + usbIsRunning = true; + Storage::switchToUsbMode(); +} + +static void callbackUsbStopped(void *, esp_event_base_t, int32_t event_id, void *) +{ + if (!usbIsRunning) /* https://github.com/espressif/arduino-esp32/issues/7228 */ + { + return; + } + usbIsRunning = false; + Storage::switchToApplicationMode(); +} + +/** + * Callback invoked when received WRITE10 command. + * + * Process data in buffer to disk's storage. + * + * @param lba logical block address + * @returns the number of written bytes (must be multiple of block size) + */ +static std::int32_t usbMsc_onWrite(const std::uint32_t lba, const std::uint32_t offset, std::uint8_t *const buffer, + const uint32_t bufsize) +{ + std::cout << "MSC WRITE: lba: " << lba << ", offset: " << offset << ", bufsize: " << bufsize << std::endl; + const std::uint32_t byteOffset = lba * blockSize + offset; + ESP_ERROR_CHECK(esp_partition_erase_range(partition, byteOffset, bufsize)); // erase must be called before write + ESP_ERROR_CHECK(esp_partition_write(partition, byteOffset, buffer, bufsize)); + return bufsize; +} + +// and +// return + +/** + * Callback invoked when received READ10 command. + * + * Copy disk's data to buffer (up to bufsize). + * + * @param lba logical block address + * @returns the number of copied bytes (must be multiple of block size) + */ +static std::int32_t usbMsc_onRead(const std::uint32_t lba, const std::uint32_t offset, void *const buffer, + const std::uint32_t bufsize) +{ + std::cout << "MSC READ: lba: " << lba << ", offset: " << offset << ", bufsize: " << bufsize << std::endl; + const std::uint32_t byteOffset = lba * blockSize + offset; + ESP_ERROR_CHECK(esp_partition_read(partition, byteOffset, buffer, bufsize)); + return bufsize; +} + +static bool usbMsc_onStartStop(const std::uint8_t power_condition, const bool start, const bool load_eject) +{ + std::cout << "MSC START/STOP: power: " << power_condition << ", start: " << start << ", eject: " << load_eject + << std::endl; + return true; +} + +std::size_t Storage::size() +{ + return FFat.totalBytes(); +} + +void Storage::begin(const bool formatFsOnFail, const char *const partitionLabel) +{ + partition = check_ffat_partition(partitionLabel); + + if (!partition) + { + std::cerr << "Error with partition!" << std::endl; + return; + } + + // initialize file system + const auto basePath = rootPath + partitionLabel; + constexpr auto maxOpenFiles = 10U; + if (!FFat.begin(formatFsOnFail, basePath.c_str(), maxOpenFiles, partitionLabel)) + { + std::cerr << "File-system initialization failed!" << std::endl; + return; + } + std::cout << "Storage has a size of " << size() << " bytes." << std::endl; + + // setup USB Mass Storage Class + usbMsc.vendorID("TTS"); // max 8 chars + usbMsc.productID("TTD"); // max 16 chars + usbMsc.productRevision("1.0"); // max 4 chars + usbMsc.onStartStop(usbMsc_onStartStop); + // Set callback + usbMsc.onRead(usbMsc_onRead); + usbMsc.onWrite(usbMsc_onWrite); + // usbMsc is ready for read/write + usbMsc.mediaPresent(true); + usbMsc.begin(FFat.totalBytes() / blockSize, blockSize); + fileSystemIsReady = true; + + // subscribe to USB events + USB.onEvent(ARDUINO_USB_STARTED_EVENT, callbackUsbStarted); + USB.onEvent(ARDUINO_USB_STOPPED_EVENT, callbackUsbStopped); + USB.begin(); +} + +void Storage::end() { - if(!partition){ - std::cerr << "Error with partition!" << std::endl; - return; - } + usbMsc.end(); + usbIsRunning = false; + FFat.end(); + fileSystemIsReady = false; +} - // initialize file system - const auto basePath = rootPath + partitionLabel; - constexpr auto maxOpenFiles = 10; - if(!fs.begin(formatFsOnFail, basePath.c_str(), maxOpenFiles, partitionLabel)){ - std::cerr << "File-system initialization failed!" << std::endl; - return; - } - std::cout << "Storage has a size of " << size() << " bytes." << std::endl; +void Storage::switchToUsbMode() +{ + FFat.end(); // flush and unmount + fileSystemIsReady = false; + usbMsc.mediaPresent(true); +} - // subscribe to USB events - callbackSubscribers.push_back(this); - usb.onEvent(ARDUINO_USB_STARTED_EVENT, callbackUsbStarted_s); - usb.onEvent(ARDUINO_USB_STOPPED_EVENT, callbackUsbStopped_s); +bool Storage::isFileSystemReady() +{ + return fileSystemIsReady; +} + +void Storage::switchToApplicationMode() +{ + usbMsc.mediaPresent(false); + FFat.end(); // invalidate cache + FFat.begin(); // update data + fileSystemIsReady = true; } -std::size_t Storage::size() const +fs::FS &Storage::getFileSystem() { - return fs.totalBytes(); + return FFat; } diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp index 9c7ce5291..b5e3eb3f9 100644 --- a/lib/storage/storage.hpp +++ b/lib/storage/storage.hpp @@ -1,42 +1,30 @@ #pragma once #include -#include +#include #include #include +#include #include -#include #include -#include class Storage { public: - Storage(bool formatFsOnFail = false, const char *partitionLabel = FFAT_PARTITION_LABEL, ESPUSB &usb = USB, - fs::F_Fat &fs = FFat, std::size_t blockSize = 512); - bool isFileSystemReady() const; - void setCallbackFsReady(); - void setCallbackFsBusy(); - std::size_t size() const; - std::string getFileContents(const char *path) const; - ~Storage(); + static void begin(bool formatFsOnFail = false, const char *partitionLabel = FFAT_PARTITION_LABEL); + static void end(); + static bool isFileSystemReady(); + static std::size_t size(); + static std::string getFileContent(const char *path); - static constexpr std::string rootPath = "/"; - - protected: - fs::FS &getFileSystem(); - - private: - const esp_partition_t *partition; /** - * Interface to USB mass storage class. + * Switch from application mode (file system) to USB MSC. */ - USBMSC usbMsc; - const std::size_t blockSize; - fs::F_Fat &fs; - ESPUSB &usb; - static std::vector callbackSubscribers; - static esp_event_handler_t callbackUsbStarted_s; - void callbackUsbStarted(); - static esp_event_handler_t callbackUsbStopped_s; - void callbackUsbStopped(); + static void switchToUsbMode(); + /** + * Switch from USB MSC to application mode (file system). + */ + static void switchToApplicationMode(); + + protected: + static fs::FS &getFileSystem(); }; diff --git a/src/main.cpp b/src/main.cpp index 5ea9556d7..12c91cb70 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,189 +1,61 @@ #include -#include -#include -#if ARDUINO_USB_MODE == 1 -#warning This sketch should be used when USB is in OTG mode -void setup() {} -void loop() {} -#else -#include -#include -#include +#include #if ARDUINO_USB_CDC_ON_BOOT == 1 #define HWSerial Serial0 #define USBSerial Serial #else #define HWSerial Serial +#include USBCDC USBSerial; #endif -USBMSC MSC; - -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 const esp_partition_t *fatPartition = nullptr; - -// Callback invoked when received WRITE10 command. -// Process data in buffer to disk's storage and -// return number of written bytes (must be multiple of block size) -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); - uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; - // erase must be called before write - ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, byteOffset, bufsize)); - ESP_ERROR_CHECK( - esp_partition_write(fatPartition, byteOffset, buffer, bufsize)); - return bufsize; -} - -// Callback invoked when received READ10 command. -// Copy disk's data to buffer (up to bufsize) and -// return number of copied bytes (must be multiple of block size) -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); - uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; - ESP_ERROR_CHECK( - esp_partition_read(fatPartition, byteOffset, buffer, 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; -} - -/** - * Lists files and directories at path. - */ -static void listFiles(const char *const dirname) { - HWSerial.printf("Directory: '%s'\n", dirname); - File root = FFat.open(dirname); - if (!root || !root.isDirectory()) { - HWSerial.printf("Error: '%s' is not a directory!\n", dirname); - return; - } - - File file = root.openNextFile(); - while (file) { - HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), - file.isDirectory() ? "d" : "f", file.size()); - file = root.openNextFile(); - } -} - -/** - * Switch from USB MSC to application mode (file system). - */ -static void switchToApplicationMode() { - MSC.mediaPresent(false); - FFat.end(); // invalidate cache - FFat.begin(); // update data -} - -/** - * Switch from application mode (file system) to USB MSC. - */ -static void switchToUSBMode() { - FFat.end(); // flush and unmount - MSC.mediaPresent(true); -} - -static void usb_stopped_cb(void *const pvParameters) { - switchToApplicationMode(); - listFiles("/"); - vTaskDelete(nullptr); -} - -static void usb_started_cb(void *const pvParameters) { - switchToUSBMode(); - vTaskDelete(nullptr); -} - -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; - static int32_t old_event_id = ARDUINO_USB_ANY_EVENT; - if (old_event_id == event_id) - return; - switch (event_id) { - case ARDUINO_USB_STARTED_EVENT: - HWSerial.println("USB PLUGGED"); - xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); - break; - case ARDUINO_USB_STOPPED_EVENT: - HWSerial.println("USB UNPLUGGED"); - xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); - 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; +struct TestStorage : Storage +{ + + /** + * Lists files and directories at path. + */ + static void listFiles(const char *const dirname) + { + auto FFat = getFileSystem(); + HWSerial.printf("Directory: '%s'\n", dirname); + File root = FFat.open(dirname); + if (!root || !root.isDirectory()) + { + HWSerial.printf("Error: '%s' is not a directory!\n", dirname); + return; + } + + File file = root.openNextFile(); + while (file) + { + HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), file.isDirectory() ? "d" : "f", file.size()); + file = root.openNextFile(); + } + } +}; + +void setup() +{ + ESP_LOGE("tag", "hello"), HWSerial.begin(115200); + HWSerial.setDebugOutput(true); + Storage::begin(); + + USBSerial.begin(); +} + +void loop() +{ + // put your main code here, to run repeatedly: + static uint32_t lastTrigger = 0; + if (millis() - lastTrigger > 5000) + { + HWSerial.print("."); + if (Storage::isFileSystemReady()) + { + TestStorage::listFiles("/"); + } + lastTrigger = millis(); } - old_event_id = event_id; - } -} - -const esp_partition_t * -check_ffat_partition(const char *label); // defined in FFat.cpp - -void setup() { - HWSerial.begin(115200); - HWSerial.setDebugOutput(true); - - if (!FFat.begin( - true)) { // `true` = Formatieren falls kein Dateisystem vorhanden - HWSerial.println("Failed to init files system, flash may not be formatted"); - return; - } - HWSerial.println("FatFS erfolgreich gemountet."); - - fatPartition = check_ffat_partition(FFAT_PARTITION_LABEL); - if (!fatPartition) { - printf("Error with FAT partition"); - return; - } - HWSerial.printf("Flash has a size of %u bytes\n", FFat.totalBytes()); - - 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); - // Set callback - MSC.onRead(onRead); - MSC.onWrite(onWrite); - // MSC is ready for read/write - MSC.mediaPresent(true); - - // Set disk size, block size should be 512 regardless of spi flash page size - MSC.begin(FFat.totalBytes() / DISK_SECTOR_SIZE, DISK_SECTOR_SIZE); - USBSerial.begin(); - USB.begin(); -} - -void loop() { - // put your main code here, to run repeatedly: - static uint32_t lastTrigger = 0; - if (millis() - lastTrigger > 1000) { - HWSerial.print("."); - lastTrigger = millis(); - } } -#endif /* ARDUINO_USB_MODE */ From ab0a115d679f8a8a197f22ec0751b99114b9af70 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 2 Mar 2025 21:19:51 +0100 Subject: [PATCH 33/65] Add logging --- lib/storage/storage.cpp | 66 +++++++++++++++++++++++++++++++---------- lib/storage/storage.hpp | 3 +- platformio.ini | 3 ++ src/main.cpp | 24 +++++++++++---- 4 files changed, 74 insertions(+), 22 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index d5fde09be..97358a01b 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -3,9 +3,19 @@ #include #include #include -#include +#include +#include #include +#if ARDUINO_USB_CDC_ON_BOOT == 1 +#define HWSerial Serial0 +#define USBSerial Serial +#else +#define HWSerial Serial +#include +USBCDC USBSerial; +#endif + #if defined(ARDUINO_USB_MODE) static_assert(ARDUINO_USB_MODE == 0, "USB must be in OTG mode"); #endif @@ -14,16 +24,29 @@ const esp_partition_t *check_ffat_partition(const char *label); // defined in FF static const std::string rootPath = "/"; static constexpr std::size_t blockSize = 512; // bytes +static const char *const TAG = "STORAGE"; static const esp_partition_t *partition; static USBMSC usbMsc; static bool usbIsRunning = false; static bool fileSystemIsReady = false; +static void usb_stopped_cb(void *const pvParameters) +{ + Storage::switchToApplicationMode(); + vTaskDelete(nullptr); +} + +static void usb_started_cb(void *const pvParameters) +{ + Storage::switchToUsbMode(); + vTaskDelete(nullptr); +} + static void callbackUsbStarted(void *, esp_event_base_t, int32_t event_id, void *) { usbIsRunning = true; - Storage::switchToUsbMode(); + xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); } static void callbackUsbStopped(void *, esp_event_base_t, int32_t event_id, void *) @@ -33,7 +56,7 @@ static void callbackUsbStopped(void *, esp_event_base_t, int32_t event_id, void return; } usbIsRunning = false; - Storage::switchToApplicationMode(); + xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); } /** @@ -47,7 +70,7 @@ static void callbackUsbStopped(void *, esp_event_base_t, int32_t event_id, void static std::int32_t usbMsc_onWrite(const std::uint32_t lba, const std::uint32_t offset, std::uint8_t *const buffer, const uint32_t bufsize) { - std::cout << "MSC WRITE: lba: " << lba << ", offset: " << offset << ", bufsize: " << bufsize << std::endl; + ESP_LOGV(TAG, "MSC WRITE: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize); const std::uint32_t byteOffset = lba * blockSize + offset; ESP_ERROR_CHECK(esp_partition_erase_range(partition, byteOffset, bufsize)); // erase must be called before write ESP_ERROR_CHECK(esp_partition_write(partition, byteOffset, buffer, bufsize)); @@ -68,7 +91,7 @@ static std::int32_t usbMsc_onWrite(const std::uint32_t lba, const std::uint32_t static std::int32_t usbMsc_onRead(const std::uint32_t lba, const std::uint32_t offset, void *const buffer, const std::uint32_t bufsize) { - std::cout << "MSC READ: lba: " << lba << ", offset: " << offset << ", bufsize: " << bufsize << std::endl; + ESP_LOGV(TAG, "MSC READ: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize); const std::uint32_t byteOffset = lba * blockSize + offset; ESP_ERROR_CHECK(esp_partition_read(partition, byteOffset, buffer, bufsize)); return bufsize; @@ -76,8 +99,7 @@ static std::int32_t usbMsc_onRead(const std::uint32_t lba, const std::uint32_t o static bool usbMsc_onStartStop(const std::uint8_t power_condition, const bool start, const bool load_eject) { - std::cout << "MSC START/STOP: power: " << power_condition << ", start: " << start << ", eject: " << load_eject - << std::endl; + ESP_LOGV(TAG, "MSC START/STOP: power: %u, start: %u, eject: %u\n", power_condition, start, load_eject); return true; } @@ -86,14 +108,15 @@ std::size_t Storage::size() return FFat.totalBytes(); } -void Storage::begin(const bool formatFsOnFail, const char *const partitionLabel) +bool Storage::begin(const bool formatFsOnFail, const char *const partitionLabel) { + ESP_LOGI(TAG, "Starting storage..."); partition = check_ffat_partition(partitionLabel); if (!partition) { - std::cerr << "Error with partition!" << std::endl; - return; + ESP_LOGE(TAG, "Error with partition!"); + return false; } // initialize file system @@ -101,10 +124,11 @@ void Storage::begin(const bool formatFsOnFail, const char *const partitionLabel) constexpr auto maxOpenFiles = 10U; if (!FFat.begin(formatFsOnFail, basePath.c_str(), maxOpenFiles, partitionLabel)) { - std::cerr << "File-system initialization failed!" << std::endl; - return; + ESP_LOGE(TAG, "File-system initialization failed!"); + return false; } - std::cout << "Storage has a size of " << size() << " bytes." << std::endl; + ESP_LOGI(TAG, "Storage has a size of %u bytes.", size()); + ESP_LOGI(TAG, "Storage mounted at '%s'.", basePath.c_str()); // setup USB Mass Storage Class usbMsc.vendorID("TTS"); // max 8 chars @@ -116,13 +140,23 @@ void Storage::begin(const bool formatFsOnFail, const char *const partitionLabel) usbMsc.onWrite(usbMsc_onWrite); // usbMsc is ready for read/write usbMsc.mediaPresent(true); - usbMsc.begin(FFat.totalBytes() / blockSize, blockSize); + if (!usbMsc.begin(FFat.totalBytes() / blockSize, blockSize)) + { + ESP_LOGE(TAG, "USB MSC initialization failed!"); + return false; + } fileSystemIsReady = true; // subscribe to USB events USB.onEvent(ARDUINO_USB_STARTED_EVENT, callbackUsbStarted); USB.onEvent(ARDUINO_USB_STOPPED_EVENT, callbackUsbStopped); - USB.begin(); + if (!USB.begin()) + { + ESP_LOGE(TAG, "USB initialization failed!"); + return false; + } + ESP_LOGI(TAG, "Storage started."); + return true; } void Storage::end() @@ -138,6 +172,7 @@ void Storage::switchToUsbMode() FFat.end(); // flush and unmount fileSystemIsReady = false; usbMsc.mediaPresent(true); + ESP_LOGD(TAG, "Switched to USB mode"); } bool Storage::isFileSystemReady() @@ -151,6 +186,7 @@ void Storage::switchToApplicationMode() FFat.end(); // invalidate cache FFat.begin(); // update data fileSystemIsReady = true; + ESP_LOGD(TAG, "Switched to application mode"); } fs::FS &Storage::getFileSystem() diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp index b5e3eb3f9..f2bc7b3ec 100644 --- a/lib/storage/storage.hpp +++ b/lib/storage/storage.hpp @@ -4,13 +4,12 @@ #include #include #include -#include #include class Storage { public: - static void begin(bool formatFsOnFail = false, const char *partitionLabel = FFAT_PARTITION_LABEL); + static bool begin(bool formatFsOnFail = false, const char *partitionLabel = FFAT_PARTITION_LABEL); static void end(); static bool isFileSystemReady(); static std::size_t size(); diff --git a/platformio.ini b/platformio.ini index 66315f1fb..2213636e7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,6 +6,7 @@ board_build.filesystem = fatfs board_build.partitions = default_ffat_8MB.csv monitor_filters = esp32_exception_decoder monitor_speed = 115200 +build_type = debug build_unflags = -DARDUINO_USB_MODE=1 @@ -18,3 +19,5 @@ build_flags = -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 + -DCONFIG_ARDUHAL_LOG_COLORS=1 + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE diff --git a/src/main.cpp b/src/main.cpp index 12c91cb70..3b8fd7f00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #if ARDUINO_USB_CDC_ON_BOOT == 1 @@ -10,6 +12,8 @@ USBCDC USBSerial; #endif +static const char *const TAG = "MAIN"; + struct TestStorage : Storage { @@ -18,12 +22,13 @@ struct TestStorage : Storage */ static void listFiles(const char *const dirname) { - auto FFat = getFileSystem(); + // auto FFat = getFileSystem(); HWSerial.printf("Directory: '%s'\n", dirname); File root = FFat.open(dirname); if (!root || !root.isDirectory()) { - HWSerial.printf("Error: '%s' is not a directory!\n", dirname); + ESP_LOGE(TAG, "'%s' is not a directory!\n", dirname); + root.close(); return; } @@ -33,15 +38,24 @@ struct TestStorage : Storage HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), file.isDirectory() ? "d" : "f", file.size()); file = root.openNextFile(); } + file.close(); + root.close(); } }; void setup() { - ESP_LOGE("tag", "hello"), HWSerial.begin(115200); + HWSerial.begin(115200); HWSerial.setDebugOutput(true); - Storage::begin(); - + delay(300); // in order to give the serial monitor time to start + HWSerial.println("START HWSerial"); + ESP_LOGE(TAG, "Example error"); + ESP_LOGW(TAG, "Example warning"); + ESP_LOGI(TAG, "Example info"); + ESP_LOGD(TAG, "Example debug"); + ESP_LOGV(TAG, "Example verbose"); + std::cout << "Hello" << std::endl; + Storage::begin(true); USBSerial.begin(); } From 351da67fa8fc7754b70bb482010ea17687fd2170 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 2 Mar 2025 21:50:44 +0100 Subject: [PATCH 34/65] WIP --- lib/storage/storage.cpp | 19 +++++-------------- src/main.cpp | 6 ++++-- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 97358a01b..664a9d0a6 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -1,3 +1,5 @@ +#include + #include "storage.hpp" #include #include @@ -7,15 +9,6 @@ #include #include -#if ARDUINO_USB_CDC_ON_BOOT == 1 -#define HWSerial Serial0 -#define USBSerial Serial -#else -#define HWSerial Serial -#include -USBCDC USBSerial; -#endif - #if defined(ARDUINO_USB_MODE) static_assert(ARDUINO_USB_MODE == 0, "USB must be in OTG mode"); #endif @@ -110,7 +103,7 @@ std::size_t Storage::size() bool Storage::begin(const bool formatFsOnFail, const char *const partitionLabel) { - ESP_LOGI(TAG, "Starting storage..."); + ESP_LOGD(TAG, "Starting storage..."); partition = check_ffat_partition(partitionLabel); if (!partition) @@ -129,6 +122,7 @@ bool Storage::begin(const bool formatFsOnFail, const char *const partitionLabel) } ESP_LOGI(TAG, "Storage has a size of %u bytes.", size()); ESP_LOGI(TAG, "Storage mounted at '%s'.", basePath.c_str()); + fileSystemIsReady = true; // setup USB Mass Storage Class usbMsc.vendorID("TTS"); // max 8 chars @@ -138,14 +132,11 @@ bool Storage::begin(const bool formatFsOnFail, const char *const partitionLabel) // Set callback usbMsc.onRead(usbMsc_onRead); usbMsc.onWrite(usbMsc_onWrite); - // usbMsc is ready for read/write - usbMsc.mediaPresent(true); - if (!usbMsc.begin(FFat.totalBytes() / blockSize, blockSize)) + if (!usbMsc.begin(size() / blockSize, blockSize)) { ESP_LOGE(TAG, "USB MSC initialization failed!"); return false; } - fileSystemIsReady = true; // subscribe to USB events USB.onEvent(ARDUINO_USB_STARTED_EVENT, callbackUsbStarted); diff --git a/src/main.cpp b/src/main.cpp index 3b8fd7f00..d120caea8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,12 +22,12 @@ struct TestStorage : Storage */ static void listFiles(const char *const dirname) { - // auto FFat = getFileSystem(); + auto FFat = getFileSystem(); HWSerial.printf("Directory: '%s'\n", dirname); File root = FFat.open(dirname); if (!root || !root.isDirectory()) { - ESP_LOGE(TAG, "'%s' is not a directory!\n", dirname); + ESP_LOGE(TAG, "'%s' is not a directory!", dirname); root.close(); return; } @@ -36,10 +36,12 @@ struct TestStorage : Storage while (file) { HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), file.isDirectory() ? "d" : "f", file.size()); + file.close(); file = root.openNextFile(); } file.close(); root.close(); + HWSerial.println("end of directory"); } }; From 3570ea1f4bef00286d58f060c117748d31d64ae5 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 18:25:32 +0100 Subject: [PATCH 35/65] Begin to design Storage class --- lib/storage/storage.cpp | 2 ++ lib/storage/storage.hpp | 5 +++++ 2 files changed, 7 insertions(+) create mode 100644 lib/storage/storage.cpp create mode 100644 lib/storage/storage.hpp diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp new file mode 100644 index 000000000..489f33d32 --- /dev/null +++ b/lib/storage/storage.cpp @@ -0,0 +1,2 @@ +#include "storage.hpp" + diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp new file mode 100644 index 000000000..a1154e387 --- /dev/null +++ b/lib/storage/storage.hpp @@ -0,0 +1,5 @@ +#pragma once + +struct Storage +{ +}; From 8bdef19295f5189073c9184fe78535777b980207 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 18:32:30 +0100 Subject: [PATCH 36/65] Move "everything" into Storage class --- lib/storage/storage.cpp | 182 ++++++++++++++++++++++++++++++++++++++ lib/storage/storage.hpp | 1 + src/main.cpp | 189 ++++------------------------------------ 3 files changed, 198 insertions(+), 174 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 489f33d32..8da05195b 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -1,2 +1,184 @@ #include "storage.hpp" +#include +#include +#include +#if ARDUINO_USB_MODE == 1 +#warning This sketch should be used when USB is in OTG mode +void setup() {} +void loop() {} +#else +#include +#include +#include + +#if ARDUINO_USB_CDC_ON_BOOT == 1 +#define HWSerial Serial0 +#define USBSerial Serial +#else +#define HWSerial Serial +USBCDC USBSerial; +#endif + +USBMSC MSC; + +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 const esp_partition_t *fatPartition = nullptr; + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and +// return number of written bytes (must be multiple of block size) +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); + uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; + // erase must be called before write + ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, byteOffset, bufsize)); + ESP_ERROR_CHECK( + esp_partition_write(fatPartition, byteOffset, buffer, bufsize)); + return bufsize; +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and +// return number of copied bytes (must be multiple of block size) +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); + uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; + ESP_ERROR_CHECK( + esp_partition_read(fatPartition, byteOffset, buffer, 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; +} + +/** + * Lists files and directories at path. + */ +static void listFiles(const char *const dirname) { + HWSerial.printf("Directory: '%s'\n", dirname); + File root = FFat.open(dirname); + if (!root || !root.isDirectory()) { + HWSerial.printf("Error: '%s' is not a directory!\n", dirname); + return; + } + + File file = root.openNextFile(); + while (file) { + HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), + file.isDirectory() ? "d" : "f", file.size()); + file = root.openNextFile(); + } +} + +/** + * Switch from USB MSC to application mode (file system). + */ +static void switchToApplicationMode() { + MSC.mediaPresent(false); + FFat.end(); // invalidate cache + FFat.begin(); // update data +} + +/** + * Switch from application mode (file system) to USB MSC. + */ +static void switchToUSBMode() { + FFat.end(); // flush and unmount + MSC.mediaPresent(true); +} + +static void usb_stopped_cb(void *const pvParameters) { + switchToApplicationMode(); + listFiles("/"); + vTaskDelete(nullptr); +} + +static void usb_started_cb(void *const pvParameters) { + switchToUSBMode(); + vTaskDelete(nullptr); +} + +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; + static int32_t old_event_id = ARDUINO_USB_ANY_EVENT; + if (old_event_id == event_id) + return; + switch (event_id) { + case ARDUINO_USB_STARTED_EVENT: + HWSerial.println("USB PLUGGED"); + xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); + break; + case ARDUINO_USB_STOPPED_EVENT: + HWSerial.println("USB UNPLUGGED"); + xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); + 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; + } + old_event_id = event_id; + } +} + +const esp_partition_t * +check_ffat_partition(const char *label); // defined in FFat.cpp + +void Storage::begin() { + HWSerial.begin(115200); + HWSerial.setDebugOutput(true); + + if (!FFat.begin( + true)) { // `true` = Formatieren falls kein Dateisystem vorhanden + HWSerial.println("Failed to init files system, flash may not be formatted"); + return; + } + HWSerial.println("FatFS erfolgreich gemountet."); + + fatPartition = check_ffat_partition(FFAT_PARTITION_LABEL); + if (!fatPartition) { + printf("Error with FAT partition"); + return; + } + HWSerial.printf("Flash has a size of %u bytes\n", FFat.totalBytes()); + + 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); + // Set callback + MSC.onRead(onRead); + MSC.onWrite(onWrite); + // MSC is ready for read/write + MSC.mediaPresent(true); + + // Set disk size, block size should be 512 regardless of spi flash page size + MSC.begin(FFat.totalBytes() / DISK_SECTOR_SIZE, DISK_SECTOR_SIZE); + USBSerial.begin(); + USB.begin(); +} + + +#endif /* ARDUINO_USB_MODE */ diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp index a1154e387..a95c5e2b6 100644 --- a/lib/storage/storage.hpp +++ b/lib/storage/storage.hpp @@ -2,4 +2,5 @@ struct Storage { + static void begin(); }; diff --git a/src/main.cpp b/src/main.cpp index 5ea9556d7..caee127aa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,189 +1,30 @@ #include -#include -#include -#if ARDUINO_USB_MODE == 1 -#warning This sketch should be used when USB is in OTG mode -void setup() {} -void loop() {} -#else +#if defined(ARDUINO_USB_MODE) +static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); +#endif +#include #include #include #include #if ARDUINO_USB_CDC_ON_BOOT == 1 #define HWSerial Serial0 -#define USBSerial Serial #else #define HWSerial Serial -USBCDC USBSerial; #endif -USBMSC MSC; - -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 const esp_partition_t *fatPartition = nullptr; - -// Callback invoked when received WRITE10 command. -// Process data in buffer to disk's storage and -// return number of written bytes (must be multiple of block size) -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); - uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; - // erase must be called before write - ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, byteOffset, bufsize)); - ESP_ERROR_CHECK( - esp_partition_write(fatPartition, byteOffset, buffer, bufsize)); - return bufsize; -} - -// Callback invoked when received READ10 command. -// Copy disk's data to buffer (up to bufsize) and -// return number of copied bytes (must be multiple of block size) -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); - uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; - ESP_ERROR_CHECK( - esp_partition_read(fatPartition, byteOffset, buffer, 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; -} - -/** - * Lists files and directories at path. - */ -static void listFiles(const char *const dirname) { - HWSerial.printf("Directory: '%s'\n", dirname); - File root = FFat.open(dirname); - if (!root || !root.isDirectory()) { - HWSerial.printf("Error: '%s' is not a directory!\n", dirname); - return; - } - - File file = root.openNextFile(); - while (file) { - HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), - file.isDirectory() ? "d" : "f", file.size()); - file = root.openNextFile(); - } -} - -/** - * Switch from USB MSC to application mode (file system). - */ -static void switchToApplicationMode() { - MSC.mediaPresent(false); - FFat.end(); // invalidate cache - FFat.begin(); // update data -} - -/** - * Switch from application mode (file system) to USB MSC. - */ -static void switchToUSBMode() { - FFat.end(); // flush and unmount - MSC.mediaPresent(true); -} - -static void usb_stopped_cb(void *const pvParameters) { - switchToApplicationMode(); - listFiles("/"); - vTaskDelete(nullptr); -} - -static void usb_started_cb(void *const pvParameters) { - switchToUSBMode(); - vTaskDelete(nullptr); +void setup() +{ + Storage::begin(); } -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; - static int32_t old_event_id = ARDUINO_USB_ANY_EVENT; - if (old_event_id == event_id) - return; - switch (event_id) { - case ARDUINO_USB_STARTED_EVENT: - HWSerial.println("USB PLUGGED"); - xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); - break; - case ARDUINO_USB_STOPPED_EVENT: - HWSerial.println("USB UNPLUGGED"); - xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); - 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 loop() +{ + // put your main code here, to run repeatedly: + static uint32_t lastTrigger = 0; + if (millis() - lastTrigger > 1000) + { + HWSerial.print("."); + lastTrigger = millis(); } - old_event_id = event_id; - } -} - -const esp_partition_t * -check_ffat_partition(const char *label); // defined in FFat.cpp - -void setup() { - HWSerial.begin(115200); - HWSerial.setDebugOutput(true); - - if (!FFat.begin( - true)) { // `true` = Formatieren falls kein Dateisystem vorhanden - HWSerial.println("Failed to init files system, flash may not be formatted"); - return; - } - HWSerial.println("FatFS erfolgreich gemountet."); - - fatPartition = check_ffat_partition(FFAT_PARTITION_LABEL); - if (!fatPartition) { - printf("Error with FAT partition"); - return; - } - HWSerial.printf("Flash has a size of %u bytes\n", FFat.totalBytes()); - - 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); - // Set callback - MSC.onRead(onRead); - MSC.onWrite(onWrite); - // MSC is ready for read/write - MSC.mediaPresent(true); - - // Set disk size, block size should be 512 regardless of spi flash page size - MSC.begin(FFat.totalBytes() / DISK_SECTOR_SIZE, DISK_SECTOR_SIZE); - USBSerial.begin(); - USB.begin(); -} - -void loop() { - // put your main code here, to run repeatedly: - static uint32_t lastTrigger = 0; - if (millis() - lastTrigger > 1000) { - HWSerial.print("."); - lastTrigger = millis(); - } } -#endif /* ARDUINO_USB_MODE */ From 2c73542ecf920e4263371d80d3c9d8d4c6257b08 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 18:37:35 +0100 Subject: [PATCH 37/65] Static assert for USB mode (2/2) --- lib/storage/storage.cpp | 251 ++++++++++++++++++++-------------------- 1 file changed, 124 insertions(+), 127 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 8da05195b..6a3823870 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -1,16 +1,14 @@ #include "storage.hpp" #include +#if defined(ARDUINO_USB_MODE) +static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); +#endif #include -#include -#if ARDUINO_USB_MODE == 1 -#warning This sketch should be used when USB is in OTG mode -void setup() {} -void loop() {} -#else #include #include #include +#include #if ARDUINO_USB_CDC_ON_BOOT == 1 #define HWSerial Serial0 @@ -22,163 +20,162 @@ USBCDC USBSerial; USBMSC MSC; -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 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 const esp_partition_t *fatPartition = nullptr; // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and // return number of written bytes (must be multiple of block size) -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); - uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; - // erase must be called before write - ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, byteOffset, bufsize)); - ESP_ERROR_CHECK( - esp_partition_write(fatPartition, byteOffset, buffer, bufsize)); - return bufsize; +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); + uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; + // erase must be called before write + ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, byteOffset, bufsize)); + ESP_ERROR_CHECK(esp_partition_write(fatPartition, byteOffset, buffer, bufsize)); + return bufsize; } // Callback invoked when received READ10 command. // Copy disk's data to buffer (up to bufsize) and // return number of copied bytes (must be multiple of block size) -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); - uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; - ESP_ERROR_CHECK( - esp_partition_read(fatPartition, byteOffset, 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); + uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; + ESP_ERROR_CHECK(esp_partition_read(fatPartition, byteOffset, buffer, 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 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; } /** * Lists files and directories at path. */ -static void listFiles(const char *const dirname) { - HWSerial.printf("Directory: '%s'\n", dirname); - File root = FFat.open(dirname); - if (!root || !root.isDirectory()) { - HWSerial.printf("Error: '%s' is not a directory!\n", dirname); - return; - } - - File file = root.openNextFile(); - while (file) { - HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), - file.isDirectory() ? "d" : "f", file.size()); - file = root.openNextFile(); - } +static void listFiles(const char *const dirname) +{ + HWSerial.printf("Directory: '%s'\n", dirname); + File root = FFat.open(dirname); + if (!root || !root.isDirectory()) + { + HWSerial.printf("Error: '%s' is not a directory!\n", dirname); + return; + } + + File file = root.openNextFile(); + while (file) + { + HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), file.isDirectory() ? "d" : "f", file.size()); + file = root.openNextFile(); + } } /** * Switch from USB MSC to application mode (file system). */ -static void switchToApplicationMode() { - MSC.mediaPresent(false); - FFat.end(); // invalidate cache - FFat.begin(); // update data +static void switchToApplicationMode() +{ + MSC.mediaPresent(false); + FFat.end(); // invalidate cache + FFat.begin(); // update data } /** * Switch from application mode (file system) to USB MSC. */ -static void switchToUSBMode() { - FFat.end(); // flush and unmount - MSC.mediaPresent(true); +static void switchToUSBMode() +{ + FFat.end(); // flush and unmount + MSC.mediaPresent(true); } -static void usb_stopped_cb(void *const pvParameters) { - switchToApplicationMode(); - listFiles("/"); - vTaskDelete(nullptr); +static void usb_stopped_cb(void *const pvParameters) +{ + switchToApplicationMode(); + listFiles("/"); + vTaskDelete(nullptr); } -static void usb_started_cb(void *const pvParameters) { - switchToUSBMode(); - vTaskDelete(nullptr); +static void usb_started_cb(void *const pvParameters) +{ + switchToUSBMode(); + vTaskDelete(nullptr); } -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; - static int32_t old_event_id = ARDUINO_USB_ANY_EVENT; - if (old_event_id == event_id) - return; - switch (event_id) { - case ARDUINO_USB_STARTED_EVENT: - HWSerial.println("USB PLUGGED"); - xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); - break; - case ARDUINO_USB_STOPPED_EVENT: - HWSerial.println("USB UNPLUGGED"); - xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); - 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; +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; + static int32_t old_event_id = ARDUINO_USB_ANY_EVENT; + if (old_event_id == event_id) + return; + switch (event_id) + { + case ARDUINO_USB_STARTED_EVENT: + HWSerial.println("USB PLUGGED"); + xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); + break; + case ARDUINO_USB_STOPPED_EVENT: + HWSerial.println("USB UNPLUGGED"); + xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); + 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; + } + old_event_id = event_id; } - old_event_id = event_id; - } } -const esp_partition_t * -check_ffat_partition(const char *label); // defined in FFat.cpp - -void Storage::begin() { - HWSerial.begin(115200); - HWSerial.setDebugOutput(true); - - if (!FFat.begin( - true)) { // `true` = Formatieren falls kein Dateisystem vorhanden - HWSerial.println("Failed to init files system, flash may not be formatted"); - return; - } - HWSerial.println("FatFS erfolgreich gemountet."); - - fatPartition = check_ffat_partition(FFAT_PARTITION_LABEL); - if (!fatPartition) { - printf("Error with FAT partition"); - return; - } - HWSerial.printf("Flash has a size of %u bytes\n", FFat.totalBytes()); - - 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); - // Set callback - MSC.onRead(onRead); - MSC.onWrite(onWrite); - // MSC is ready for read/write - MSC.mediaPresent(true); - - // Set disk size, block size should be 512 regardless of spi flash page size - MSC.begin(FFat.totalBytes() / DISK_SECTOR_SIZE, DISK_SECTOR_SIZE); - USBSerial.begin(); - USB.begin(); -} +const esp_partition_t *check_ffat_partition(const char *label); // defined in FFat.cpp + +void Storage::begin() +{ + HWSerial.begin(115200); + HWSerial.setDebugOutput(true); + if (!FFat.begin(true)) + { // `true` = Formatieren falls kein Dateisystem vorhanden + HWSerial.println("Failed to init files system, flash may not be formatted"); + return; + } + HWSerial.println("FatFS erfolgreich gemountet."); -#endif /* ARDUINO_USB_MODE */ + fatPartition = check_ffat_partition(FFAT_PARTITION_LABEL); + if (!fatPartition) + { + printf("Error with FAT partition"); + return; + } + HWSerial.printf("Flash has a size of %u bytes\n", FFat.totalBytes()); + + 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); + // Set callback + MSC.onRead(onRead); + MSC.onWrite(onWrite); + // MSC is ready for read/write + MSC.mediaPresent(true); + + // Set disk size, block size should be 512 regardless of spi flash page size + MSC.begin(FFat.totalBytes() / DISK_SECTOR_SIZE, DISK_SECTOR_SIZE); + USBSerial.begin(); + USB.begin(); +} From 74dcb21acd3a2fce39ba1574e93acef4b051dd95 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 18:51:16 +0100 Subject: [PATCH 38/65] Move serial initialization to main --- lib/storage/storage.cpp | 3 --- src/main.cpp | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 6a3823870..ea202a2e5 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -145,8 +145,6 @@ const esp_partition_t *check_ffat_partition(const char *label); // defined in FF void Storage::begin() { - HWSerial.begin(115200); - HWSerial.setDebugOutput(true); if (!FFat.begin(true)) { // `true` = Formatieren falls kein Dateisystem vorhanden @@ -176,6 +174,5 @@ void Storage::begin() // Set disk size, block size should be 512 regardless of spi flash page size MSC.begin(FFat.totalBytes() / DISK_SECTOR_SIZE, DISK_SECTOR_SIZE); - USBSerial.begin(); USB.begin(); } diff --git a/src/main.cpp b/src/main.cpp index caee127aa..7a5ee1b0b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,10 +2,10 @@ #if defined(ARDUINO_USB_MODE) static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); #endif -#include #include #include #include +#include #if ARDUINO_USB_CDC_ON_BOOT == 1 #define HWSerial Serial0 @@ -15,6 +15,8 @@ static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); void setup() { + HWSerial.begin(115200); + HWSerial.setDebugOutput(true); Storage::begin(); } From 6464a8baff0d6e1cb9a54a2d59d80601b514e1c4 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 18:52:27 +0100 Subject: [PATCH 39/65] Remove USBSerial --- lib/storage/storage.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index ea202a2e5..470c41e2f 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -12,10 +12,8 @@ static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); #if ARDUINO_USB_CDC_ON_BOOT == 1 #define HWSerial Serial0 -#define USBSerial Serial #else #define HWSerial Serial -USBCDC USBSerial; #endif USBMSC MSC; From c07f405449711ef4beee9a19adaff6d38dccbfd3 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 18:53:34 +0100 Subject: [PATCH 40/65] Make MSC static --- lib/storage/storage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 470c41e2f..ebe1b3cf8 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -16,7 +16,7 @@ static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); #define HWSerial Serial #endif -USBMSC MSC; +static USBMSC MSC; 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 From 7b507b88896869e22f2646f9ddf3375d29ba78e0 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 21:22:32 +0100 Subject: [PATCH 41/65] blockSize --- lib/storage/storage.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index ebe1b3cf8..a706c52f3 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -7,6 +7,7 @@ static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); #include #include #include +#include #include #include @@ -18,9 +19,7 @@ static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); static USBMSC MSC; -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 constexpr std::uint16_t blockSize = 512; // Should be 512 static const esp_partition_t *fatPartition = nullptr; @@ -30,7 +29,7 @@ static const esp_partition_t *fatPartition = nullptr; 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); - uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; + uint32_t byteOffset = lba * blockSize + offset; // erase must be called before write ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, byteOffset, bufsize)); ESP_ERROR_CHECK(esp_partition_write(fatPartition, byteOffset, buffer, bufsize)); @@ -43,7 +42,7 @@ static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t 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); - uint32_t byteOffset = lba * DISK_SECTOR_SIZE + offset; + uint32_t byteOffset = lba * blockSize + offset; ESP_ERROR_CHECK(esp_partition_read(fatPartition, byteOffset, buffer, bufsize)); return bufsize; } @@ -171,6 +170,6 @@ void Storage::begin() MSC.mediaPresent(true); // Set disk size, block size should be 512 regardless of spi flash page size - MSC.begin(FFat.totalBytes() / DISK_SECTOR_SIZE, DISK_SECTOR_SIZE); + MSC.begin(FFat.totalBytes() / blockSize, blockSize); USB.begin(); } From c530dbdc5b3d6dd08aee9f3a01c632bf77f062e5 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 21:31:43 +0100 Subject: [PATCH 42/65] Replace commulated USB callback with individual callbacks --- lib/storage/storage.cpp | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index a706c52f3..5ac825e0e 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -106,36 +106,20 @@ static void usb_started_cb(void *const pvParameters) vTaskDelete(nullptr); } -static void usbEventCallback(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +static bool usbIsRunning = false; +static void usbStoppedCallback(void *, esp_event_base_t, int32_t, void *) { - if (event_base == ARDUINO_USB_EVENTS) + if (!usbIsRunning) { - arduino_usb_event_data_t *data = (arduino_usb_event_data_t *)event_data; - static int32_t old_event_id = ARDUINO_USB_ANY_EVENT; - if (old_event_id == event_id) - return; - switch (event_id) - { - case ARDUINO_USB_STARTED_EVENT: - HWSerial.println("USB PLUGGED"); - xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); - break; - case ARDUINO_USB_STOPPED_EVENT: - HWSerial.println("USB UNPLUGGED"); - xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); - 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; - } - old_event_id = event_id; + return; } + usbIsRunning = false; + xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); +} +static void usbStartedCallback(void *, esp_event_base_t, int32_t, void *) +{ + usbIsRunning = true; + xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); } const esp_partition_t *check_ffat_partition(const char *label); // defined in FFat.cpp @@ -158,7 +142,8 @@ void Storage::begin() } HWSerial.printf("Flash has a size of %u bytes\n", FFat.totalBytes()); - USB.onEvent(usbEventCallback); + USB.onEvent(ARDUINO_USB_STARTED_EVENT, usbStartedCallback); + USB.onEvent(ARDUINO_USB_STOPPED_EVENT, usbStoppedCallback); MSC.vendorID("ESP32"); // max 8 chars MSC.productID("USB_MSC"); // max 16 chars MSC.productRevision("1.0"); // max 4 chars From 148ef20051e41d85df6614ac716ea2d400baf0df Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 21:32:14 +0100 Subject: [PATCH 43/65] Rename partition --- lib/storage/storage.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 5ac825e0e..a999a628e 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -21,7 +21,7 @@ static USBMSC MSC; static constexpr std::uint16_t blockSize = 512; // Should be 512 -static const esp_partition_t *fatPartition = nullptr; +static const esp_partition_t *partition = nullptr; // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and @@ -31,8 +31,8 @@ static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t HWSerial.printf("MSC WRITE: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize); uint32_t byteOffset = lba * blockSize + offset; // erase must be called before write - ESP_ERROR_CHECK(esp_partition_erase_range(fatPartition, byteOffset, bufsize)); - ESP_ERROR_CHECK(esp_partition_write(fatPartition, byteOffset, buffer, bufsize)); + ESP_ERROR_CHECK(esp_partition_erase_range(partition, byteOffset, bufsize)); + ESP_ERROR_CHECK(esp_partition_write(partition, byteOffset, buffer, bufsize)); return bufsize; } @@ -43,7 +43,7 @@ static int32_t onRead(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufs { HWSerial.printf("MSC READ: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize); uint32_t byteOffset = lba * blockSize + offset; - ESP_ERROR_CHECK(esp_partition_read(fatPartition, byteOffset, buffer, bufsize)); + ESP_ERROR_CHECK(esp_partition_read(partition, byteOffset, buffer, bufsize)); return bufsize; } @@ -134,8 +134,8 @@ void Storage::begin() } HWSerial.println("FatFS erfolgreich gemountet."); - fatPartition = check_ffat_partition(FFAT_PARTITION_LABEL); - if (!fatPartition) + partition = check_ffat_partition(FFAT_PARTITION_LABEL); + if (!partition) { printf("Error with FAT partition"); return; From bf336e0757d0f8d7fb8df23bd2014e2b9fda840e Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 21:48:18 +0100 Subject: [PATCH 44/65] Use logging macros and iostream --- lib/storage/storage.cpp | 23 +++++++++++++---------- platformio.ini | 3 +++ src/main.cpp | 13 ++++++++++++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index a999a628e..b5d83375f 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -10,6 +10,7 @@ static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); #include #include #include +#include #if ARDUINO_USB_CDC_ON_BOOT == 1 #define HWSerial Serial0 @@ -22,13 +23,14 @@ static USBMSC MSC; static constexpr std::uint16_t blockSize = 512; // Should be 512 static const esp_partition_t *partition = nullptr; +static const char *const TAG = "STORAGE"; // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and // return number of written bytes (must be multiple of block size) 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); + ESP_LOGV(TAG, "MSC WRITE: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize); uint32_t byteOffset = lba * blockSize + offset; // erase must be called before write ESP_ERROR_CHECK(esp_partition_erase_range(partition, byteOffset, bufsize)); @@ -41,7 +43,7 @@ static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t // return number of copied bytes (must be multiple of block size) 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); + ESP_LOGV(TAG, "MSC READ: lba: %u, offset: %u, bufsize: %u\n", lba, offset, bufsize); uint32_t byteOffset = lba * blockSize + offset; ESP_ERROR_CHECK(esp_partition_read(partition, byteOffset, buffer, bufsize)); return bufsize; @@ -49,7 +51,7 @@ static int32_t onRead(uint32_t lba, uint32_t offset, void *buffer, uint32_t bufs 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); + ESP_LOGV(TAG, "MSC START/STOP: power: %u, start: %u, eject: %u\n", power_condition, start, load_eject); return true; } @@ -58,18 +60,19 @@ static bool onStartStop(uint8_t power_condition, bool start, bool load_eject) */ static void listFiles(const char *const dirname) { - HWSerial.printf("Directory: '%s'\n", dirname); + std::cout << "Directory: '" << dirname << "'" << std::endl; File root = FFat.open(dirname); if (!root || !root.isDirectory()) { - HWSerial.printf("Error: '%s' is not a directory!\n", dirname); + ESP_LOGE(TAG, "Error: '%s' is not a directory!\n", dirname); return; } File file = root.openNextFile(); while (file) { - HWSerial.printf(" %s (%s, %d Bytes)\n", file.name(), file.isDirectory() ? "d" : "f", file.size()); + std::cout << "\t" << file.name() << " (" << (file.isDirectory() ? "d" : "f") << ", " << file.size() << " Bytes)" + << std::endl; file = root.openNextFile(); } } @@ -129,18 +132,18 @@ void Storage::begin() if (!FFat.begin(true)) { // `true` = Formatieren falls kein Dateisystem vorhanden - HWSerial.println("Failed to init files system, flash may not be formatted"); + ESP_LOGE(TAG, "Failed to init files system, flash may not be formatted"); return; } - HWSerial.println("FatFS erfolgreich gemountet."); + ESP_LOGI(TAG, "FatFS erfolgreich gemountet."); partition = check_ffat_partition(FFAT_PARTITION_LABEL); if (!partition) { - printf("Error with FAT partition"); + ESP_LOGE(TAG, "Error with FAT partition"); return; } - HWSerial.printf("Flash has a size of %u bytes\n", FFat.totalBytes()); + ESP_LOGI(TAG, "Flash has a size of %u bytes\n", FFat.totalBytes()); USB.onEvent(ARDUINO_USB_STARTED_EVENT, usbStartedCallback); USB.onEvent(ARDUINO_USB_STOPPED_EVENT, usbStoppedCallback); diff --git a/platformio.ini b/platformio.ini index 66315f1fb..2213636e7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,6 +6,7 @@ board_build.filesystem = fatfs board_build.partitions = default_ffat_8MB.csv monitor_filters = esp32_exception_decoder monitor_speed = 115200 +build_type = debug build_unflags = -DARDUINO_USB_MODE=1 @@ -18,3 +19,5 @@ build_flags = -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 + -DCONFIG_ARDUHAL_LOG_COLORS=1 + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE diff --git a/src/main.cpp b/src/main.cpp index 7a5ee1b0b..644eb97d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,7 +4,9 @@ static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); #endif #include #include +#include #include +#include #include #if ARDUINO_USB_CDC_ON_BOOT == 1 @@ -13,10 +15,19 @@ static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); #define HWSerial Serial #endif +static const char *const TAG = "MAIN"; + void setup() { HWSerial.begin(115200); HWSerial.setDebugOutput(true); + ESP_LOGV(TAG, "verbose"); + ESP_LOGD(TAG, "debug"); + ESP_LOGI(TAG, "info"); + ESP_LOGW(TAG, "warning"); + ESP_LOGE(TAG, "error"); + delay(200); // wait for the serial monitor to be ready + std::cout << "Started program" << std::endl; Storage::begin(); } @@ -26,7 +37,7 @@ void loop() static uint32_t lastTrigger = 0; if (millis() - lastTrigger > 1000) { - HWSerial.print("."); + std::cout << "." << std::flush; lastTrigger = millis(); } } From db09b41a3634e29a9b5d5e77db1892dfda408050 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 21:50:17 +0100 Subject: [PATCH 45/65] added more error checks --- lib/storage/storage.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index b5d83375f..80b2f4b5e 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -158,6 +158,12 @@ void Storage::begin() MSC.mediaPresent(true); // Set disk size, block size should be 512 regardless of spi flash page size - MSC.begin(FFat.totalBytes() / blockSize, blockSize); - USB.begin(); + if (!MSC.begin(FFat.totalBytes() / blockSize, blockSize)) + { + ESP_LOGE(TAG, "starting USB MSC failed"); + } + if (!USB.begin()) + { + ESP_LOGE(TAG, "starting USB failed"); + } } From 23c66d36909482834380756aca78a1320307e4fc Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Mon, 3 Mar 2025 21:51:31 +0100 Subject: [PATCH 46/65] Close files after usage --- lib/storage/storage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 80b2f4b5e..b3eb57222 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -73,8 +73,11 @@ static void listFiles(const char *const dirname) { std::cout << "\t" << file.name() << " (" << (file.isDirectory() ? "d" : "f") << ", " << file.size() << " Bytes)" << std::endl; + file.close(); file = root.openNextFile(); } + file.close(); + root.close(); } /** From d8603775ca430016ab4f21ffec00a2ecdecc0454 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Tue, 4 Mar 2025 21:56:30 +0100 Subject: [PATCH 47/65] Adjust logging --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 644eb97d6..1a267b312 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,13 +20,13 @@ static const char *const TAG = "MAIN"; void setup() { HWSerial.begin(115200); + delay(200); // wait for the serial monitor to be ready HWSerial.setDebugOutput(true); ESP_LOGV(TAG, "verbose"); ESP_LOGD(TAG, "debug"); ESP_LOGI(TAG, "info"); ESP_LOGW(TAG, "warning"); ESP_LOGE(TAG, "error"); - delay(200); // wait for the serial monitor to be ready std::cout << "Started program" << std::endl; Storage::begin(); } From 01ed8932c5ecb72d024c99f0bc1bea418a244fd8 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Tue, 4 Mar 2025 21:57:17 +0100 Subject: [PATCH 48/65] Inline USB start/stop callback tasks --- lib/storage/storage.cpp | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index b3eb57222..f7861e622 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -99,19 +99,6 @@ static void switchToUSBMode() MSC.mediaPresent(true); } -static void usb_stopped_cb(void *const pvParameters) -{ - switchToApplicationMode(); - listFiles("/"); - vTaskDelete(nullptr); -} - -static void usb_started_cb(void *const pvParameters) -{ - switchToUSBMode(); - vTaskDelete(nullptr); -} - static bool usbIsRunning = false; static void usbStoppedCallback(void *, esp_event_base_t, int32_t, void *) { @@ -120,12 +107,14 @@ static void usbStoppedCallback(void *, esp_event_base_t, int32_t, void *) return; } usbIsRunning = false; - xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); + switchToApplicationMode(); + listFiles("/"); } + static void usbStartedCallback(void *, esp_event_base_t, int32_t, void *) { usbIsRunning = true; - xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); + switchToUSBMode(); } const esp_partition_t *check_ffat_partition(const char *label); // defined in FFat.cpp From 175e4646b91f6cb261779194c65c292cd911e830 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Wed, 5 Mar 2025 22:11:28 +0100 Subject: [PATCH 49/65] Add wait for file system --- lib/storage/storage.cpp | 40 +++++++++++++++++++++++++++++++++++++--- lib/storage/storage.hpp | 3 ++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index f7861e622..c7fa16a05 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -7,10 +7,13 @@ static_assert(ARDUINO_USB_MODE == 0, "must be used when USB is in OTG mode"); #include #include #include +#include +#include #include #include #include #include +#include #if ARDUINO_USB_CDC_ON_BOOT == 1 #define HWSerial Serial0 @@ -25,6 +28,30 @@ static constexpr std::uint16_t blockSize = 512; // Should be 512 static const esp_partition_t *partition = nullptr; static const char *const TAG = "STORAGE"; +static class ReadyCondition +{ + public: + void setReady(const bool new_state) + { + ready = new_state; + conditionVariable.notify_all(); + } + bool isReady() const + { + return ready; + } + void wait_unitl_ready() const + { + std::mutex cv_m; + std::unique_lock lock(cv_m); + conditionVariable.wait(lock, [this] { return isReady(); }); + } + + private: + mutable std::condition_variable conditionVariable; + std::atomic ready; +} fileSystemState; + // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and // return number of written bytes (must be multiple of block size) @@ -86,8 +113,9 @@ static void listFiles(const char *const dirname) static void switchToApplicationMode() { MSC.mediaPresent(false); - FFat.end(); // invalidate cache - FFat.begin(); // update data + FFat.end(); // invalidate cache + ESP_ERROR_CHECK(FFat.begin()); // update data + fileSystemState.setReady(true); } /** @@ -96,6 +124,7 @@ static void switchToApplicationMode() static void switchToUSBMode() { FFat.end(); // flush and unmount + fileSystemState.setReady(false); MSC.mediaPresent(true); } @@ -108,7 +137,6 @@ static void usbStoppedCallback(void *, esp_event_base_t, int32_t, void *) } usbIsRunning = false; switchToApplicationMode(); - listFiles("/"); } static void usbStartedCallback(void *, esp_event_base_t, int32_t, void *) @@ -159,3 +187,9 @@ void Storage::begin() ESP_LOGE(TAG, "starting USB failed"); } } + +void Storage::waitForFileSystem() +{ + fileSystemState.wait_unitl_ready(); + listFiles("/"); +} diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp index a95c5e2b6..06ca9ebed 100644 --- a/lib/storage/storage.hpp +++ b/lib/storage/storage.hpp @@ -2,5 +2,6 @@ struct Storage { - static void begin(); + static void begin(); + static void waitForFileSystem(); }; From e1b6c201be765102684546fc9754a559400fbcb3 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Wed, 5 Mar 2025 22:13:41 +0100 Subject: [PATCH 50/65] Revert "Inline USB start/stop callback tasks" This reverts commit 01ed8932c5ecb72d024c99f0bc1bea418a244fd8. Because starting file system within event loop failed: ``` ESP_ERROR_CHECK failed: esp_err_t 0x1 (ERROR) at 0x4037ab88 #0 0x4037ab88 in _esp_error_check_failed at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/esp_err.c:42 file: "lib/storage/storage.cpp" line 117 func: void switchToApplicationMode() expression: FFat.begin() abort() was called at PC 0x4037ab8b on core 1 Backtrace: 0x403777ba:0x3fcf2100 0x4037ab95:0x3fcf2120 0x4038058d:0x3fcf2140 0x4037ab8b:0x3fcf21c0 0x42002afb:0x3fcf21e0 0x42002b15:0x3fcf2200 0x4205e6d5:0x3fcf2220 0x4205e754:0x3fcf2270 #0 0x403777ba in panic_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/panic.c:408 #1 0x4037ab95 in esp_system_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/esp_system.c:137 #2 0x4038058d in abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/newlib/abort.c:46 #3 0x4037ab8b in _esp_error_check_failed at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/esp_err.c:43 #4 0x42002afb in switchToApplicationMode() at lib/storage/storage.cpp:117 (discriminator 1) #5 0x42002b15 in usbStoppedCallback(void*, char const*, int, void*) at lib/storage/storage.cpp:139 #6 0x4205e6d5 in handler_execute at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_event/esp_event.c:145 (inlined by) esp_event_loop_run at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_event/esp_event.c:598 #7 0x4205e754 in esp_event_loop_run_task at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_event/esp_event.c:115 (discriminator 15) ``` --- lib/storage/storage.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index c7fa16a05..788eabb6f 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -128,6 +128,19 @@ static void switchToUSBMode() MSC.mediaPresent(true); } +static void usb_stopped_cb(void *const pvParameters) +{ + switchToApplicationMode(); + listFiles("/"); + vTaskDelete(nullptr); +} + +static void usb_started_cb(void *const pvParameters) +{ + switchToUSBMode(); + vTaskDelete(nullptr); +} + static bool usbIsRunning = false; static void usbStoppedCallback(void *, esp_event_base_t, int32_t, void *) { @@ -136,13 +149,12 @@ static void usbStoppedCallback(void *, esp_event_base_t, int32_t, void *) return; } usbIsRunning = false; - switchToApplicationMode(); + xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); } - static void usbStartedCallback(void *, esp_event_base_t, int32_t, void *) { usbIsRunning = true; - switchToUSBMode(); + xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); } const esp_partition_t *check_ffat_partition(const char *label); // defined in FFat.cpp From 44b02fba83f5852c4c757aa511775fb39bd18b23 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 8 Mar 2025 14:03:08 +0100 Subject: [PATCH 51/65] Add error checking when re-mounting FFat --- lib/storage/storage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index b3eb57222..71f050199 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -86,8 +86,8 @@ static void listFiles(const char *const dirname) static void switchToApplicationMode() { MSC.mediaPresent(false); - FFat.end(); // invalidate cache - FFat.begin(); // update data + FFat.end(); // invalidate cache + ESP_ERROR_CHECK(FFat.begin()); // update data } /** From 964007243d383ae158c6eb6517415c4788cfd71d Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sat, 8 Mar 2025 14:08:27 +0100 Subject: [PATCH 52/65] Corrected the error handling for FFat.begin() That function returns `true` on success. --- lib/storage/storage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 71f050199..1f7c38a0a 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -86,8 +86,8 @@ static void listFiles(const char *const dirname) static void switchToApplicationMode() { MSC.mediaPresent(false); - FFat.end(); // invalidate cache - ESP_ERROR_CHECK(FFat.begin()); // update data + FFat.end(); // invalidate cache + assert(FFat.begin()); // update data } /** From 64cc314a539eff373c1ab43f1f6fa13879f2e23f Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Mar 2025 09:44:56 +0100 Subject: [PATCH 53/65] Define `fileSystemState` separate for more clarity. --- lib/storage/storage.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index aa408678c..c1d20aee8 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -26,7 +26,7 @@ static constexpr std::uint16_t blockSize = 512; // Should be 512 static const esp_partition_t *partition = nullptr; static const char *const TAG = "STORAGE"; -static class ReadyCondition +class ReadyCondition { public: void setReady(const bool new_state) @@ -48,7 +48,9 @@ static class ReadyCondition private: mutable std::condition_variable conditionVariable; std::atomic ready; -} fileSystemState; +}; + +static ReadyCondition fileSystemState; /** * Callback invoked when received WRITE10 command. @@ -127,7 +129,7 @@ static void listFiles(const char *const dirname) static void switchToApplicationMode() { usbMsc.mediaPresent(false); - FFat.end(); // invalidate cache + FFat.end(); // invalidate cache assert(FFat.begin()); // update data fileSystemState.setReady(true); } From 432cffe817c3a4d6bc70c532b963afd3dfac3d42 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Mar 2025 14:55:48 +0100 Subject: [PATCH 54/65] Enable C++17 --- platformio.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio.ini b/platformio.ini index 2213636e7..97555c191 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,6 +13,7 @@ build_unflags = -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=1 -DARDUINO_USB_DFU_ON_BOOT=1 + -std=gnu++11 build_flags = -DARDUINO_USB_MODE=0 @@ -21,3 +22,4 @@ build_flags = -DARDUINO_USB_DFU_ON_BOOT=0 -DCONFIG_ARDUHAL_LOG_COLORS=1 -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE + -std=gnu++17 From cea6d8b3e1a6a3bb709bd2ce26725af196a637cd Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Mar 2025 14:56:26 +0100 Subject: [PATCH 55/65] WIP: Define manager for file system state --- lib/storage/storage.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index c1d20aee8..3bac3ae3a 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #if defined(ARDUINO_USB_MODE) static_assert(ARDUINO_USB_MODE == 0, "USB must be in OTG mode"); @@ -26,6 +27,42 @@ static constexpr std::uint16_t blockSize = 512; // Should be 512 static const esp_partition_t *partition = nullptr; static const char *const TAG = "STORAGE"; +class FileSystemSwitcher +{ + public: + typedef decltype(FFat) FileSystem; + std::shared_ptr getFileSystem_locking() + { + std::unique_lock fs_state_lock{fileSystemState_mutex}; + if (!fileSystemIsActive) + { + stateChanged.wait(fs_state_lock, [this]() { return fileSystemIsActive; }); + } + fs_state_lock.release(); + return {&FFat, [this](FileSystem *) { fileSystemState_mutex.unlock(); }}; + } + void requestState(const bool fileSystemActive) + { + requestFileSystemActive = fileSystemActive; + stateChangeRequested.notify_all(); + } + + protected: + bool isFileSystemActive() const + { + return fileSystemIsActive; + } + void refreshState(); + + private: + std::condition_variable stateChangeRequested; + std::condition_variable stateChanged; + std::atomic requestFileSystemActive; + std::thread stateMachine; + std::mutex fileSystemState_mutex; //!< used to lock the actual state + bool fileSystemIsActive; //!< describes the current state +}; + class ReadyCondition { public: From f22611a042bc45caa030d8ed624161cfedbed500 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Mar 2025 16:39:49 +0100 Subject: [PATCH 56/65] WIP: Define manager for file system state --- lib/storage/storage.cpp | 164 +++++++++++++++------------------------- lib/storage/storage.hpp | 1 - 2 files changed, 63 insertions(+), 102 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 3bac3ae3a..55d1932cd 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -27,11 +27,41 @@ static constexpr std::uint16_t blockSize = 512; // Should be 512 static const esp_partition_t *partition = nullptr; static const char *const TAG = "STORAGE"; +/** + * Lists files and directories at path. + */ +static void listFiles(const char *const dirname) +{ + std::cout << "Directory: '" << dirname << "'" << std::endl; + File root = FFat.open(dirname); + if (!root || !root.isDirectory()) + { + ESP_LOGE(TAG, "Error: '%s' is not a directory!\n", dirname); + return; + } + + File file = root.openNextFile(); + while (file) + { + std::cout << "\t" << file.name() << " (" << (file.isDirectory() ? "d" : "f") << ", " << file.size() << " Bytes)" + << std::endl; + file.close(); + file = root.openNextFile(); + } + file.close(); + root.close(); +} + class FileSystemSwitcher { public: - typedef decltype(FFat) FileSystem; - std::shared_ptr getFileSystem_locking() + void begin(const bool fsIsActive) + { + fileSystemIsActive = fsIsActive; + stateMachine = std::thread(&FileSystemSwitcher::processStateRequests, this); + } + + std::shared_ptr getFileSystem_locking() { std::unique_lock fs_state_lock{fileSystemState_mutex}; if (!fileSystemIsActive) @@ -39,7 +69,7 @@ class FileSystemSwitcher stateChanged.wait(fs_state_lock, [this]() { return fileSystemIsActive; }); } fs_state_lock.release(); - return {&FFat, [this](FileSystem *) { fileSystemState_mutex.unlock(); }}; + return {&FFat, [this](fs::FS *) { fileSystemState_mutex.unlock(); }}; } void requestState(const bool fileSystemActive) { @@ -52,7 +82,27 @@ class FileSystemSwitcher { return fileSystemIsActive; } - void refreshState(); + void processStateRequests() + { + while (true) + { + std::unique_lock fs_state_lock{fileSystemState_mutex}; + stateChangeRequested.wait(fs_state_lock, + [this]() { return requestFileSystemActive != fileSystemIsActive; }); + if (fileSystemIsActive = requestFileSystemActive) + { + usbMsc.mediaPresent(false); + FFat.end(); // invalidate cache + assert(FFat.begin()); // update data + listFiles("/"); + } + else + { + FFat.end(); // flush and unmount + usbMsc.mediaPresent(true); + } + } + } private: std::condition_variable stateChangeRequested; @@ -63,31 +113,7 @@ class FileSystemSwitcher bool fileSystemIsActive; //!< describes the current state }; -class ReadyCondition -{ - public: - void setReady(const bool new_state) - { - ready = new_state; - conditionVariable.notify_all(); - } - bool isReady() const - { - return ready; - } - void wait_unitl_ready() const - { - std::mutex cv_m; - std::unique_lock lock(cv_m); - conditionVariable.wait(lock, [this] { return isReady(); }); - } - - private: - mutable std::condition_variable conditionVariable; - std::atomic ready; -}; - -static ReadyCondition fileSystemState; +static FileSystemSwitcher fileSystemSwitcher; /** * Callback invoked when received WRITE10 command. @@ -135,66 +161,7 @@ std::size_t Storage::size() return FFat.totalBytes(); } -/** - * Lists files and directories at path. - */ -static void listFiles(const char *const dirname) -{ - std::cout << "Directory: '" << dirname << "'" << std::endl; - File root = FFat.open(dirname); - if (!root || !root.isDirectory()) - { - ESP_LOGE(TAG, "Error: '%s' is not a directory!\n", dirname); - return; - } - - File file = root.openNextFile(); - while (file) - { - std::cout << "\t" << file.name() << " (" << (file.isDirectory() ? "d" : "f") << ", " << file.size() << " Bytes)" - << std::endl; - file.close(); - file = root.openNextFile(); - } - file.close(); - root.close(); -} - -/** - * Switch from USB MSC to application mode (file system). - */ -static void switchToApplicationMode() -{ - usbMsc.mediaPresent(false); - FFat.end(); // invalidate cache - assert(FFat.begin()); // update data - fileSystemState.setReady(true); -} - -/** - * Switch from application mode (file system) to USB MSC. - */ -static void switchToUSBMode() -{ - FFat.end(); // flush and unmount - fileSystemState.setReady(false); - usbMsc.mediaPresent(true); -} - -static void usb_stopped_cb(void *const pvParameters) -{ - switchToApplicationMode(); - listFiles("/"); - vTaskDelete(nullptr); -} - -static void usb_started_cb(void *const pvParameters) -{ - switchToUSBMode(); - vTaskDelete(nullptr); -} - -static bool usbIsRunning = false; +static std::atomic usbIsRunning = false; static void usbStoppedCallback(void *, esp_event_base_t, int32_t, void *) { if (!usbIsRunning) @@ -202,12 +169,12 @@ static void usbStoppedCallback(void *, esp_event_base_t, int32_t, void *) return; } usbIsRunning = false; - xTaskCreate(usb_stopped_cb, "USB_Stopped_CB", 4096, nullptr, 5, nullptr); + fileSystemSwitcher.requestState(true); } static void usbStartedCallback(void *, esp_event_base_t, int32_t, void *) { usbIsRunning = true; - xTaskCreate(usb_started_cb, "USB_Started_CB", 4096, nullptr, 5, nullptr); + fileSystemSwitcher.requestState(false); } void Storage::begin() @@ -228,17 +195,18 @@ void Storage::begin() } ESP_LOGI(TAG, "Flash has a size of %u bytes\n", FFat.totalBytes()); - USB.onEvent(ARDUINO_USB_STARTED_EVENT, usbStartedCallback); - USB.onEvent(ARDUINO_USB_STOPPED_EVENT, usbStoppedCallback); + fileSystemSwitcher.begin(true); // define state before callbacks are activated + usbMsc.vendorID("ESP32"); // max 8 chars usbMsc.productID("USB_MSC"); // max 16 chars usbMsc.productRevision("1.0"); // max 4 chars usbMsc.onStartStop(usbMsc_onStartStop); + usbMsc.mediaPresent(false); // Set callback usbMsc.onRead(usbMsc_onRead); usbMsc.onWrite(usbMsc_onWrite); - // MSC is ready for read/write - usbMsc.mediaPresent(true); + USB.onEvent(ARDUINO_USB_STARTED_EVENT, usbStartedCallback); + USB.onEvent(ARDUINO_USB_STOPPED_EVENT, usbStoppedCallback); // Set disk size, block size should be 512 regardless of spi flash page size if (!usbMsc.begin(FFat.totalBytes() / blockSize, blockSize)) @@ -257,9 +225,3 @@ void Storage::end() usbIsRunning = false; FFat.end(); } - -void Storage::waitForFileSystem() -{ - fileSystemState.wait_unitl_ready(); - listFiles("/"); -} diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp index 313d509e0..d57f32b52 100644 --- a/lib/storage/storage.hpp +++ b/lib/storage/storage.hpp @@ -4,7 +4,6 @@ struct Storage { static void begin(); - static void waitForFileSystem(); static void end(); static std::size_t size(); }; From b39d476a01ca62abe96582653b7bfd66971ac2c3 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Mar 2025 16:59:15 +0100 Subject: [PATCH 57/65] WIP: Define manager for file system state --- lib/storage/storage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 55d1932cd..e2630138b 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -58,6 +58,7 @@ class FileSystemSwitcher void begin(const bool fsIsActive) { fileSystemIsActive = fsIsActive; + requestFileSystemActive = fsIsActive; stateMachine = std::thread(&FileSystemSwitcher::processStateRequests, this); } @@ -91,6 +92,7 @@ class FileSystemSwitcher [this]() { return requestFileSystemActive != fileSystemIsActive; }); if (fileSystemIsActive = requestFileSystemActive) { + ESP_LOGD(TAG, "mount FS"); usbMsc.mediaPresent(false); FFat.end(); // invalidate cache assert(FFat.begin()); // update data @@ -98,6 +100,7 @@ class FileSystemSwitcher } else { + ESP_LOGD(TAG, "unmount FS"); FFat.end(); // flush and unmount usbMsc.mediaPresent(true); } From 3a2b088c99569accdbaf2ec1291b98afd2c3fe5f Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Mar 2025 17:30:19 +0100 Subject: [PATCH 58/65] Testing Storage: OK --- lib/storage/storage.cpp | 14 +++++++++++--- lib/storage/storage.hpp | 1 + src/main.cpp | 15 +++++++-------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index e2630138b..1e2cc753a 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -30,10 +30,10 @@ static const char *const TAG = "STORAGE"; /** * Lists files and directories at path. */ -static void listFiles(const char *const dirname) +static void listFiles(const char *const dirname, const std::shared_ptr fs) { std::cout << "Directory: '" << dirname << "'" << std::endl; - File root = FFat.open(dirname); + File root = fs->open(dirname); if (!root || !root.isDirectory()) { ESP_LOGE(TAG, "Error: '%s' is not a directory!\n", dirname); @@ -75,6 +75,7 @@ class FileSystemSwitcher void requestState(const bool fileSystemActive) { requestFileSystemActive = fileSystemActive; + ESP_LOGD(TAG, "request new state: %s", fileSystemActive ? "true" : "false"); stateChangeRequested.notify_all(); } @@ -88,6 +89,7 @@ class FileSystemSwitcher while (true) { std::unique_lock fs_state_lock{fileSystemState_mutex}; + ESP_LOGD(TAG, "waiting for state change request"); stateChangeRequested.wait(fs_state_lock, [this]() { return requestFileSystemActive != fileSystemIsActive; }); if (fileSystemIsActive = requestFileSystemActive) @@ -96,7 +98,6 @@ class FileSystemSwitcher usbMsc.mediaPresent(false); FFat.end(); // invalidate cache assert(FFat.begin()); // update data - listFiles("/"); } else { @@ -104,6 +105,7 @@ class FileSystemSwitcher FFat.end(); // flush and unmount usbMsc.mediaPresent(true); } + stateChanged.notify_all(); } } @@ -228,3 +230,9 @@ void Storage::end() usbIsRunning = false; FFat.end(); } + +void Storage::test() +{ + auto fs_p = fileSystemSwitcher.getFileSystem_locking(); + listFiles("/", fs_p); +} diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp index d57f32b52..920e3879b 100644 --- a/lib/storage/storage.hpp +++ b/lib/storage/storage.hpp @@ -5,5 +5,6 @@ struct Storage { static void begin(); static void end(); + static void test(); static std::size_t size(); }; diff --git a/src/main.cpp b/src/main.cpp index e63cc9521..cdeb007b9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,12 @@ #include +#include #include #include #include #include +#include + +using namespace std::chrono_literals; #if ARDUINO_USB_CDC_ON_BOOT == 1 #define HWSerial Serial0 @@ -16,7 +20,7 @@ void setup() { HWSerial.begin(115200); HWSerial.setDebugOutput(true); - delay(300); // in order to give the serial monitor time to start + delay(3000); // in order to give the serial monitor time to start std::cout << "Started program" << std::endl; ESP_LOGE(TAG, "Example error"); ESP_LOGW(TAG, "Example warning"); @@ -28,11 +32,6 @@ void setup() void loop() { - // put your main code here, to run repeatedly: - static uint32_t lastTrigger = 0; - if (millis() - lastTrigger > 1000) - { - std::cout << "." << std::flush; - lastTrigger = millis(); - } + Storage::test(); + std::this_thread::sleep_for(3s); } From b7a0efd66dc8c25dcd784467dc35f3ec7843e2c7 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Sun, 9 Mar 2025 18:15:05 +0100 Subject: [PATCH 59/65] Simplify interface; inline FileSystemSwitcher --- lib/storage/storage.cpp | 129 +++++++++++++--------------------------- lib/storage/storage.hpp | 8 ++- platformio.ini | 2 +- src/main.cpp | 28 ++++++++- 4 files changed, 76 insertions(+), 91 deletions(-) diff --git a/lib/storage/storage.cpp b/lib/storage/storage.cpp index 1e2cc753a..0b46361e0 100644 --- a/lib/storage/storage.cpp +++ b/lib/storage/storage.cpp @@ -27,98 +27,54 @@ static constexpr std::uint16_t blockSize = 512; // Should be 512 static const esp_partition_t *partition = nullptr; static const char *const TAG = "STORAGE"; -/** - * Lists files and directories at path. - */ -static void listFiles(const char *const dirname, const std::shared_ptr fs) +static std::condition_variable stateChangeRequested; +static std::condition_variable stateChanged; +static std::atomic requestFileSystemActive; +static std::thread stateMachine; +static std::mutex fileSystemState_mutex; //!< used to lock the actual state +static bool fileSystemIsActive; //!< describes the current state + +std::shared_ptr Storage::getFileSystem_locking() { - std::cout << "Directory: '" << dirname << "'" << std::endl; - File root = fs->open(dirname); - if (!root || !root.isDirectory()) - { - ESP_LOGE(TAG, "Error: '%s' is not a directory!\n", dirname); - return; - } - - File file = root.openNextFile(); - while (file) + std::unique_lock fs_state_lock{fileSystemState_mutex}; + if (!fileSystemIsActive) { - std::cout << "\t" << file.name() << " (" << (file.isDirectory() ? "d" : "f") << ", " << file.size() << " Bytes)" - << std::endl; - file.close(); - file = root.openNextFile(); + stateChanged.wait(fs_state_lock, []() { return fileSystemIsActive; }); } - file.close(); - root.close(); + fs_state_lock.release(); + return {&FFat, [](fs::FS *) { fileSystemState_mutex.unlock(); }}; } -class FileSystemSwitcher +static void requestState(const bool fileSystemActive) { - public: - void begin(const bool fsIsActive) - { - fileSystemIsActive = fsIsActive; - requestFileSystemActive = fsIsActive; - stateMachine = std::thread(&FileSystemSwitcher::processStateRequests, this); - } + requestFileSystemActive = fileSystemActive; + ESP_LOGD(TAG, "request new state: %s", fileSystemActive ? "true" : "false"); + stateChangeRequested.notify_all(); +} - std::shared_ptr getFileSystem_locking() +static void processStateRequests() +{ + while (true) { std::unique_lock fs_state_lock{fileSystemState_mutex}; - if (!fileSystemIsActive) + ESP_LOGD(TAG, "waiting for state change request"); + stateChangeRequested.wait(fs_state_lock, []() { return requestFileSystemActive != fileSystemIsActive; }); + if (fileSystemIsActive = requestFileSystemActive) { - stateChanged.wait(fs_state_lock, [this]() { return fileSystemIsActive; }); + ESP_LOGI(TAG, "mount FS"); + usbMsc.mediaPresent(false); + FFat.end(); // invalidate cache + assert(FFat.begin()); // update data } - fs_state_lock.release(); - return {&FFat, [this](fs::FS *) { fileSystemState_mutex.unlock(); }}; - } - void requestState(const bool fileSystemActive) - { - requestFileSystemActive = fileSystemActive; - ESP_LOGD(TAG, "request new state: %s", fileSystemActive ? "true" : "false"); - stateChangeRequested.notify_all(); - } - - protected: - bool isFileSystemActive() const - { - return fileSystemIsActive; - } - void processStateRequests() - { - while (true) + else { - std::unique_lock fs_state_lock{fileSystemState_mutex}; - ESP_LOGD(TAG, "waiting for state change request"); - stateChangeRequested.wait(fs_state_lock, - [this]() { return requestFileSystemActive != fileSystemIsActive; }); - if (fileSystemIsActive = requestFileSystemActive) - { - ESP_LOGD(TAG, "mount FS"); - usbMsc.mediaPresent(false); - FFat.end(); // invalidate cache - assert(FFat.begin()); // update data - } - else - { - ESP_LOGD(TAG, "unmount FS"); - FFat.end(); // flush and unmount - usbMsc.mediaPresent(true); - } - stateChanged.notify_all(); + ESP_LOGI(TAG, "unmount FS"); + FFat.end(); // flush and unmount + usbMsc.mediaPresent(true); } + stateChanged.notify_all(); } - - private: - std::condition_variable stateChangeRequested; - std::condition_variable stateChanged; - std::atomic requestFileSystemActive; - std::thread stateMachine; - std::mutex fileSystemState_mutex; //!< used to lock the actual state - bool fileSystemIsActive; //!< describes the current state -}; - -static FileSystemSwitcher fileSystemSwitcher; +} /** * Callback invoked when received WRITE10 command. @@ -174,12 +130,12 @@ static void usbStoppedCallback(void *, esp_event_base_t, int32_t, void *) return; } usbIsRunning = false; - fileSystemSwitcher.requestState(true); + requestState(true); } static void usbStartedCallback(void *, esp_event_base_t, int32_t, void *) { usbIsRunning = true; - fileSystemSwitcher.requestState(false); + requestState(false); } void Storage::begin() @@ -190,7 +146,7 @@ void Storage::begin() ESP_LOGE(TAG, "Failed to init files system, flash may not be formatted"); return; } - ESP_LOGI(TAG, "FatFS erfolgreich gemountet."); + ESP_LOGI(TAG, "file system initialized"); partition = check_ffat_partition(FFAT_PARTITION_LABEL); if (!partition) @@ -200,7 +156,10 @@ void Storage::begin() } ESP_LOGI(TAG, "Flash has a size of %u bytes\n", FFat.totalBytes()); - fileSystemSwitcher.begin(true); // define state before callbacks are activated + // define state before callbacks are activated + fileSystemIsActive = true; + requestFileSystemActive = true; + stateMachine = std::thread(processStateRequests); usbMsc.vendorID("ESP32"); // max 8 chars usbMsc.productID("USB_MSC"); // max 16 chars @@ -230,9 +189,3 @@ void Storage::end() usbIsRunning = false; FFat.end(); } - -void Storage::test() -{ - auto fs_p = fileSystemSwitcher.getFileSystem_locking(); - listFiles("/", fs_p); -} diff --git a/lib/storage/storage.hpp b/lib/storage/storage.hpp index 920e3879b..9c0f17da5 100644 --- a/lib/storage/storage.hpp +++ b/lib/storage/storage.hpp @@ -1,10 +1,16 @@ #pragma once #include +#include + +namespace fs +{ +class FS; +} struct Storage { static void begin(); static void end(); - static void test(); + static std::shared_ptr getFileSystem_locking(); static std::size_t size(); }; diff --git a/platformio.ini b/platformio.ini index 97555c191..e1ca729d4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,5 +21,5 @@ build_flags = -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -DCONFIG_ARDUHAL_LOG_COLORS=1 - -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO -std=gnu++17 diff --git a/src/main.cpp b/src/main.cpp index cdeb007b9..928629114 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -16,6 +17,31 @@ using namespace std::chrono_literals; static const char *const TAG = "MAIN"; +/** + * Lists files and directories at path. + */ +static void listFiles(const char *const dirname, const std::shared_ptr fs) +{ + std::cout << "Directory: '" << dirname << "'" << std::endl; + File root = fs->open(dirname); + if (!root || !root.isDirectory()) + { + ESP_LOGE(TAG, "Error: '%s' is not a directory!\n", dirname); + return; + } + + File file = root.openNextFile(); + while (file) + { + std::cout << "\t" << file.name() << " (" << (file.isDirectory() ? "d" : "f") << ", " << file.size() << " Bytes)" + << std::endl; + file.close(); + file = root.openNextFile(); + } + file.close(); + root.close(); +} + void setup() { HWSerial.begin(115200); @@ -32,6 +58,6 @@ void setup() void loop() { - Storage::test(); + listFiles("/", Storage::getFileSystem_locking()); std::this_thread::sleep_for(3s); } From ceaddb1c4a7ec3b3aea8258beb701a613ef9cc94 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Fri, 14 Mar 2025 08:45:22 +0100 Subject: [PATCH 60/65] Remove unnecessary dependency to header --- lib/board_adapters/storage/storage.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/board_adapters/storage/storage.cpp b/lib/board_adapters/storage/storage.cpp index 0b46361e0..a75f9866a 100644 --- a/lib/board_adapters/storage/storage.cpp +++ b/lib/board_adapters/storage/storage.cpp @@ -1,5 +1,3 @@ -#include - #include "storage.hpp" #include #include From fe06ec898edf6014feb73333ae2fbed4fc455bed Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Fri, 14 Mar 2025 11:35:57 +0100 Subject: [PATCH 61/65] Create stub headers for tests --- platformio.ini | 1 + test/test_doubles/FFat.h | 6 ++++++ test/test_doubles/USB.h | 6 ++++++ test/test_doubles/USBMSC.h | 6 ++++++ test/test_doubles/esp32-hal-log.h | 6 ++++++ test/test_doubles/esp_err.h | 6 ++++++ test/test_doubles/esp_partition.h | 6 ++++++ 7 files changed, 37 insertions(+) create mode 100644 test/test_doubles/FFat.h create mode 100644 test/test_doubles/USB.h create mode 100644 test/test_doubles/USBMSC.h create mode 100644 test/test_doubles/esp32-hal-log.h create mode 100644 test/test_doubles/esp_err.h create mode 100644 test/test_doubles/esp_partition.h diff --git a/platformio.ini b/platformio.ini index d22f4c7db..858a49006 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,6 +39,7 @@ build_flags = -fprofile-abs-path -O0 -ggdb3 + -Itest/test_doubles [env:esp32-s3-devkitc-1] platform = espressif32@6.9.0 diff --git a/test/test_doubles/FFat.h b/test/test_doubles/FFat.h new file mode 100644 index 000000000..a07899f07 --- /dev/null +++ b/test/test_doubles/FFat.h @@ -0,0 +1,6 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once diff --git a/test/test_doubles/USB.h b/test/test_doubles/USB.h new file mode 100644 index 000000000..a07899f07 --- /dev/null +++ b/test/test_doubles/USB.h @@ -0,0 +1,6 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once diff --git a/test/test_doubles/USBMSC.h b/test/test_doubles/USBMSC.h new file mode 100644 index 000000000..a07899f07 --- /dev/null +++ b/test/test_doubles/USBMSC.h @@ -0,0 +1,6 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once diff --git a/test/test_doubles/esp32-hal-log.h b/test/test_doubles/esp32-hal-log.h new file mode 100644 index 000000000..a07899f07 --- /dev/null +++ b/test/test_doubles/esp32-hal-log.h @@ -0,0 +1,6 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once diff --git a/test/test_doubles/esp_err.h b/test/test_doubles/esp_err.h new file mode 100644 index 000000000..a07899f07 --- /dev/null +++ b/test/test_doubles/esp_err.h @@ -0,0 +1,6 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once diff --git a/test/test_doubles/esp_partition.h b/test/test_doubles/esp_partition.h new file mode 100644 index 000000000..a07899f07 --- /dev/null +++ b/test/test_doubles/esp_partition.h @@ -0,0 +1,6 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once From 62d6f19164f4355cd7615670f6dda0092dc25fe3 Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Tue, 25 Mar 2025 07:26:35 +0100 Subject: [PATCH 62/65] Add missing header inclusion --- lib/board_adapters/storage/storage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/board_adapters/storage/storage.cpp b/lib/board_adapters/storage/storage.cpp index a75f9866a..622f656a2 100644 --- a/lib/board_adapters/storage/storage.cpp +++ b/lib/board_adapters/storage/storage.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include From f55b602b4a3b14fbd9d2b0509f4c19f8c9705e6f Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Tue, 25 Mar 2025 07:26:58 +0100 Subject: [PATCH 63/65] Defined some stubs as test doubles --- platformio.ini | 2 +- test/doubles/FFat.h | 45 ++++++++++++++++++++++++ test/doubles/README.md | 5 +++ test/doubles/USB.h | 29 +++++++++++++++ test/doubles/USBMSC.h | 37 +++++++++++++++++++ test/doubles/esp32-hal-log.h | 31 ++++++++++++++++ test/{test_doubles => doubles}/esp_err.h | 8 +++++ test/doubles/esp_partition.h | 27 ++++++++++++++ test/test_doubles/FFat.h | 6 ---- test/test_doubles/USB.h | 6 ---- test/test_doubles/USBMSC.h | 6 ---- test/test_doubles/esp32-hal-log.h | 6 ---- test/test_doubles/esp_partition.h | 6 ---- 13 files changed, 183 insertions(+), 31 deletions(-) create mode 100644 test/doubles/FFat.h create mode 100644 test/doubles/README.md create mode 100644 test/doubles/USB.h create mode 100644 test/doubles/USBMSC.h create mode 100644 test/doubles/esp32-hal-log.h rename test/{test_doubles => doubles}/esp_err.h (50%) create mode 100644 test/doubles/esp_partition.h delete mode 100644 test/test_doubles/FFat.h delete mode 100644 test/test_doubles/USB.h delete mode 100644 test/test_doubles/USBMSC.h delete mode 100644 test/test_doubles/esp32-hal-log.h delete mode 100644 test/test_doubles/esp_partition.h diff --git a/platformio.ini b/platformio.ini index 858a49006..a8b2f4013 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,7 +39,7 @@ build_flags = -fprofile-abs-path -O0 -ggdb3 - -Itest/test_doubles + -Itest/doubles [env:esp32-s3-devkitc-1] platform = espressif32@6.9.0 diff --git a/test/doubles/FFat.h b/test/doubles/FFat.h new file mode 100644 index 000000000..74387a2f4 --- /dev/null +++ b/test/doubles/FFat.h @@ -0,0 +1,45 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once +#include "esp_partition.h" +#include +#include + +namespace fs +{ +struct FS +{ + bool begin(bool formatOnFail = false, const char *basePath = "/ffat", uint8_t maxOpenFiles = 10, const char *partitionLabel = (char *)FFAT_PARTITION_LABEL) + { + return true; + } + bool format(bool full_wipe = true, char *partitionLabel = (char *)FFAT_PARTITION_LABEL) + { + return true; + } + size_t totalBytes() + { + return 42; + } + size_t usedBytes() + { + return 42 / 2; + } + size_t freeBytes() + { + return totalBytes() - usedBytes(); + } + void end() + { + } + bool exists(const char *) + { + return true; + } +}; +} // namespace fs + +extern fs::FS FFat; diff --git a/test/doubles/README.md b/test/doubles/README.md new file mode 100644 index 000000000..daa70921f --- /dev/null +++ b/test/doubles/README.md @@ -0,0 +1,5 @@ +\dir . +\brief Package \ref test_doubles + +\page test_doubles Test Doubles +\brief Stubs, fakes, mocks, ... supporting unit testing. diff --git a/test/doubles/USB.h b/test/doubles/USB.h new file mode 100644 index 000000000..4bff93841 --- /dev/null +++ b/test/doubles/USB.h @@ -0,0 +1,29 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once +#include + +typedef const char *esp_event_base_t; +typedef void (*esp_event_handler_t)(void *, esp_event_base_t, int32_t, void *); + +enum arduino_usb_event_t +{ + ARDUINO_USB_STARTED_EVENT, + ARDUINO_USB_STOPPED_EVENT, +}; + +struct ESPUSB +{ + bool begin() + { + return true; + } + + void onEvent(arduino_usb_event_t, esp_event_handler_t) + { + } +}; +extern ESPUSB USB; diff --git a/test/doubles/USBMSC.h b/test/doubles/USBMSC.h new file mode 100644 index 000000000..de2218736 --- /dev/null +++ b/test/doubles/USBMSC.h @@ -0,0 +1,37 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once +#include + +typedef bool (*msc_start_stop_cb)(uint8_t, bool, bool); +typedef int32_t (*msc_read_cb)(uint32_t, uint32_t, void *, uint32_t); +typedef int32_t (*msc_write_cb)(uint32_t, uint32_t, uint8_t *, uint32_t); + +struct USBMSC +{ + bool begin(uint32_t, uint16_t) + { + return true; + } + void end() + { + } + void vendorID(const char *) + { + } + void productID(const char *) + { + } + void productRevision(const char *) + { + } + void mediaPresent(bool) + { + } + void onStartStop(msc_start_stop_cb); + void onRead(msc_read_cb); + void onWrite(msc_write_cb); +}; diff --git a/test/doubles/esp32-hal-log.h b/test/doubles/esp32-hal-log.h new file mode 100644 index 000000000..1c29d2120 --- /dev/null +++ b/test/doubles/esp32-hal-log.h @@ -0,0 +1,31 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once + +template +inline void ESP_LOGD(const char *, const char *, T...) +{ +} + +template +inline void ESP_LOGV(const char *, const char *, T...) +{ +} + +template +inline void ESP_LOGI(const char *, const char *, T...) +{ +} + +template +inline void ESP_LOGW(const char *, const char *, T...) +{ +} + +template +void ESP_LOGE(const char *, const char *, T...) +{ +} \ No newline at end of file diff --git a/test/test_doubles/esp_err.h b/test/doubles/esp_err.h similarity index 50% rename from test/test_doubles/esp_err.h rename to test/doubles/esp_err.h index a07899f07..8f1da0055 100644 --- a/test/test_doubles/esp_err.h +++ b/test/doubles/esp_err.h @@ -4,3 +4,11 @@ */ #pragma once + +struct esp_err_t +{ +}; + +inline void ESP_ERROR_CHECK(esp_err_t) +{ +} diff --git a/test/doubles/esp_partition.h b/test/doubles/esp_partition.h new file mode 100644 index 000000000..3f0b8b050 --- /dev/null +++ b/test/doubles/esp_partition.h @@ -0,0 +1,27 @@ +/** + * @file + * @brief Fake for testing on native + */ + +#pragma once +#include "esp_err.h" +#include + +struct esp_partition_t; + +inline esp_err_t esp_partition_erase_range(const esp_partition_t *, size_t, size_t) +{ + return {}; +} + +inline esp_err_t esp_partition_read(const esp_partition_t *, size_t, void *, size_t) +{ + return {}; +} + +inline esp_err_t esp_partition_write(const esp_partition_t *, size_t, const void *, size_t) +{ + return {}; +} + +constexpr char FFAT_PARTITION_LABEL[] = "FFAT_PARTITION_LABEL"; \ No newline at end of file diff --git a/test/test_doubles/FFat.h b/test/test_doubles/FFat.h deleted file mode 100644 index a07899f07..000000000 --- a/test/test_doubles/FFat.h +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @file - * @brief Fake for testing on native - */ - -#pragma once diff --git a/test/test_doubles/USB.h b/test/test_doubles/USB.h deleted file mode 100644 index a07899f07..000000000 --- a/test/test_doubles/USB.h +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @file - * @brief Fake for testing on native - */ - -#pragma once diff --git a/test/test_doubles/USBMSC.h b/test/test_doubles/USBMSC.h deleted file mode 100644 index a07899f07..000000000 --- a/test/test_doubles/USBMSC.h +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @file - * @brief Fake for testing on native - */ - -#pragma once diff --git a/test/test_doubles/esp32-hal-log.h b/test/test_doubles/esp32-hal-log.h deleted file mode 100644 index a07899f07..000000000 --- a/test/test_doubles/esp32-hal-log.h +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @file - * @brief Fake for testing on native - */ - -#pragma once diff --git a/test/test_doubles/esp_partition.h b/test/test_doubles/esp_partition.h deleted file mode 100644 index a07899f07..000000000 --- a/test/test_doubles/esp_partition.h +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @file - * @brief Fake for testing on native - */ - -#pragma once From 5b150476a94687e003a5b250f7ec7268701ba95a Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Wed, 26 Mar 2025 08:31:43 +0100 Subject: [PATCH 64/65] Fix header --- doc/decisions/dr-005.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/decisions/dr-005.md b/doc/decisions/dr-005.md index 88f9db76e..4da6f1d4d 100644 --- a/doc/decisions/dr-005.md +++ b/doc/decisions/dr-005.md @@ -1,4 +1,4 @@ -# USB MSC Integration with FFat on ESP32-S3 +# DR005 USB MSC Integration with FFat on ESP32-S3 ## Context From 25f4c14743450741e6d0cab6fc945e85a1d6aa2c Mon Sep 17 00:00:00 2001 From: David Hebbeker Date: Wed, 26 Mar 2025 08:31:52 +0100 Subject: [PATCH 65/65] Exclude test code from documentation This cleans up the dependency graph --- Doxyfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Doxyfile b/Doxyfile index 6d70b7b54..a30364219 100644 --- a/Doxyfile +++ b/Doxyfile @@ -9,6 +9,7 @@ EXTRACT_STATIC = YES CITE_BIB_FILES = doc/bibliography INPUT = . RECURSIVE = YES +EXCLUDE = test/ EXAMPLE_PATH = doc/debouncing/ IMAGE_PATH = doc/ USE_MDFILE_AS_MAINPAGE = ./README.md