From 10ad74f50c9b3a40308381fa7b55f8e8c639a0f7 Mon Sep 17 00:00:00 2001 From: Stephen Barriball Date: Fri, 12 Sep 2025 13:37:52 +0100 Subject: [PATCH] Initial files --- Documentation/20250905 - NameCheap.pdf | Bin 0 -> 13694 bytes Frontend/Demo.ino/Demo.ino.ino | 192 ++++++++++ Frontend/Demo.ino/ci.json | 5 + .../{ => ESP32_Firmware}/ESP32_Firmware.ino | 47 ++- Frontend/ESP32_Firmware/test1.ino/ci.json | 5 + .../ESP32_Firmware/test1.ino/test1.ino.ino | 192 ++++++++++ .../test2_ino/.theia/launch.json | 8 + Frontend/ESP32_Firmware/test2_ino/ci.json | 5 + .../ESP32_Firmware/test2_ino/test2_ino.ino | 362 ++++++++++++++++++ Frontend/test.ino | 0 10 files changed, 795 insertions(+), 21 deletions(-) create mode 100644 Documentation/20250905 - NameCheap.pdf create mode 100644 Frontend/Demo.ino/Demo.ino.ino create mode 100644 Frontend/Demo.ino/ci.json rename Frontend/{ => ESP32_Firmware}/ESP32_Firmware.ino (92%) create mode 100644 Frontend/ESP32_Firmware/test1.ino/ci.json create mode 100644 Frontend/ESP32_Firmware/test1.ino/test1.ino.ino create mode 100644 Frontend/ESP32_Firmware/test2_ino/.theia/launch.json create mode 100644 Frontend/ESP32_Firmware/test2_ino/ci.json create mode 100644 Frontend/ESP32_Firmware/test2_ino/test2_ino.ino create mode 100644 Frontend/test.ino diff --git a/Documentation/20250905 - NameCheap.pdf b/Documentation/20250905 - NameCheap.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d2f78e2f169fe97de108ed4cb2077bd721c38211 GIT binary patch literal 13694 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 m9pkHGe07Ykj`7tozB> 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) + +const char 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 = 500 * 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]; + +//static uint8_t msc_disk[DISK_SECTOR_COUNT][DISK_SECTOR_SIZE] = { + const uint8_t sector0[512] = + //------------- 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 + 'N', 'e', 't', 'F', 'l', 'o', 'p', 'p', 'y', ' ', ' ', //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 + }; + + const uint8_t sector1[] = //------------- 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 + }; + + const uint8_t sector2[512] = //------------- Block2: Root Directory -------------// + { + // first entry is volume label + 'N', 'e', 't', 'F', 'l', 'o', 'p', 'p', 'y', ' ', ' ', + 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(2025, 8, 26), //last_modified_ymd + FAT_U16(0), FAT_U32(0), + + // second entry is readme file + 'F', 'i', 'l', 'e', ' ', '1', ' ', ' ', //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 + }; +//}; + +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); + + // Allocate each disk sector in PSRAM + for (uint x=0; x < DISK_SECTOR_COUNT; x++) + { + msc_disk[x] = (uint8_t*)ps_malloc(DISK_SECTOR_SIZE * sizeof(uint8_t)); + + if (!msc_disk[x]) + { + Serial.println("Unable to allocate memory"); + return; + } + } + + memcpy(msc_disk[0], sector0, 512); + memcpy(msc_disk[1], sector1, 6); + memcpy(msc_disk[2], sector2, 512); + memcpy(msc_disk[3], README_CONTENTS, 512); + + USB.onEvent(usbEventCallback); + MSC.vendorID("ACIT"); //max 8 chars + MSC.productID("NetFloppy"); //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(); + + Serial.print("Total PSRAM:"); + Serial.println(ESP.getPsramSize()); + Serial.print("Free PSRAM: "); + Serial.println(ESP.getFreePsram()); +} + +void loop() +{ + // +} +#endif /* ARDUINO_USB_MODE */ diff --git a/Frontend/Demo.ino/ci.json b/Frontend/Demo.ino/ci.json new file mode 100644 index 0000000..f9ac7d0 --- /dev/null +++ b/Frontend/Demo.ino/ci.json @@ -0,0 +1,5 @@ +{ + "requires": [ + "CONFIG_SOC_USB_OTG_SUPPORTED=y" + ] +} diff --git a/Frontend/ESP32_Firmware.ino b/Frontend/ESP32_Firmware/ESP32_Firmware.ino similarity index 92% rename from Frontend/ESP32_Firmware.ino rename to Frontend/ESP32_Firmware/ESP32_Firmware.ino index 0f62153..6940862 100644 --- a/Frontend/ESP32_Firmware.ino +++ b/Frontend/ESP32_Firmware/ESP32_Firmware.ino @@ -28,7 +28,6 @@ #include #include #include -#include "esp_tinyusb.h" #include "tusb.h" // ---------------- GPIO Configuration ---------------- @@ -94,23 +93,29 @@ static const char* wifi_state() { } // ---------------- USB MSC (TinyUSB) ---------------- -extern "C" { - int32_t tud_msc_read10_cb (uint8_t, uint32_t lba, uint32_t off, void* buf, uint32_t len){ - if(!mscFile) return -1; uint64_t a=(uint64_t)lba*BYTES_PER_SECTOR + off; - if(!mscFile.seek(a)) return -1; return (int32_t)mscFile.read((uint8_t*)buf, len); - } - int32_t tud_msc_write10_cb(uint8_t, uint32_t lba, uint32_t off, uint8_t const* buf, uint32_t len){ - if(!mscFile) return -1; uint64_t a=(uint64_t)lba*BYTES_PER_SECTOR + off; - if(!mscFile.seek(a)) return -1; return (int32_t)mscFile.write((const uint8_t*)buf, len); - } - bool tud_msc_is_writable_cb(uint8_t){ return true; } - bool tud_msc_test_unit_ready_cb(uint8_t){ return mscReady && mscFile; } - void tud_msc_capacity_cb(uint8_t, uint32_t* bc, uint16_t* bs){ *bc=mscBlocks; *bs=BYTES_PER_SECTOR; } - bool tud_msc_start_stop_cb(uint8_t, uint8_t, bool start, bool load_eject){ - mscReady = (start && !load_eject && mscFile); return true; - } +//extern "C" { + //int32_t tud_msc_read10_cb (uint8_t, uint32_t lba, uint32_t off, void* buf, uint32_t len){ + // if(!mscFile) return -1; uint64_t a=(uint64_t)lba*BYTES_PER_SECTOR + off; + // if(!mscFile.seek(a)) return -1; return (int32_t)mscFile.read((uint8_t*)buf, len); + // } + //int32_t tud_msc_write10_cb(uint8_t, uint32_t lba, uint32_t off, uint8_t const* buf, uint32_t len){ + // if(!mscFile) return -1; uint64_t a=(uint64_t)lba*BYTES_PER_SECTOR + off; + // if(!mscFile.seek(a)) return -1; return (int32_t)mscFile.write((const uint8_t*)buf, len); + // } + //bool tud_msc_is_writable_cb(uint8_t){ return true; } + //bool tud_msc_test_unit_ready_cb(uint8_t){ return mscReady && mscFile; } + //void tud_msc_capacity_cb(uint8_t, uint32_t* bc, uint16_t* bs){ *bc=mscBlocks; *bs=BYTES_PER_SECTOR; } + //bool tud_msc_start_stop_cb(uint8_t, uint8_t, bool start, bool load_eject) + //{ + // mscReady = (start && !load_eject && mscFile); return true; + //} +//} +static void usb_init() +{ + tinyusb_config_t cfg={}; + tusb_init(&cfg); + mscReady=false; } -static void usb_init(){ tinyusb_config_t cfg={}; tusb_init(&cfg); mscReady=false; } static bool vol_open(){ if(mscFile) mscFile.close(); @@ -234,9 +239,9 @@ static bool https_post_volume(const String& id){ url += "/api/volume?id="; url += id; WiFiClientSecure client; - client.setCACert(SERVER_CA_PEM); - client.setCertificate(CLIENT_CRT_PEM); - client.setPrivateKey(CLIENT_KEY_PEM); + //client.setCACert(SERVER_CA_PEM); + //client.setCertificate(CLIENT_CRT_PEM); + //client.setPrivateKey(CLIENT_KEY_PEM); HTTPClient https; if(!https.begin(client, url)){ lastNote="begin fail"; return false; } https.addHeader("Content-Type","application/octet-stream"); @@ -322,7 +327,7 @@ static void h_remount(){ static void enter_loaded(){ // Read tag uint64_t tag=0; - if(!rfid_read_tag(tag, 3000)){ lastNote="no tag"; return; } + //if(!rfid_read_tag(tag, 3000)){ lastNote="no tag"; return; } String id = hex64(tag); if(!https_fetch_volume(id)) return; if(!vol_open()) return; diff --git a/Frontend/ESP32_Firmware/test1.ino/ci.json b/Frontend/ESP32_Firmware/test1.ino/ci.json new file mode 100644 index 0000000..f9ac7d0 --- /dev/null +++ b/Frontend/ESP32_Firmware/test1.ino/ci.json @@ -0,0 +1,5 @@ +{ + "requires": [ + "CONFIG_SOC_USB_OTG_SUPPORTED=y" + ] +} diff --git a/Frontend/ESP32_Firmware/test1.ino/test1.ino.ino b/Frontend/ESP32_Firmware/test1.ino/test1.ino.ino new file mode 100644 index 0000000..6cfa721 --- /dev/null +++ b/Frontend/ESP32_Firmware/test1.ino/test1.ino.ino @@ -0,0 +1,192 @@ +#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) + +const char 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 = 500 * 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]; + +//static uint8_t msc_disk[DISK_SECTOR_COUNT][DISK_SECTOR_SIZE] = { + const uint8_t sector0[512] = + //------------- 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 + 'N', 'e', 't', 'F', 'l', 'o', 'p', 'p', 'y', ' ', ' ', //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 + }; + + const uint8_t sector1[] = //------------- 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 + }; + + const uint8_t sector2[512] = //------------- Block2: Root Directory -------------// + { + // first entry is volume label + 'N', 'e', 't', 'F', 'l', 'o', 'p', 'p', 'y', ' ', ' ', + 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(2025, 8, 26), //last_modified_ymd + FAT_U16(0), FAT_U32(0), + + // second entry is readme file + 'F', 'i', 'l', 'e', ' ', '1', ' ', ' ', //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 + }; +//}; + +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); + + // Allocate each disk sector in PSRAM + for (uint x=0; x < DISK_SECTOR_COUNT; x++) + { + msc_disk[x] = (uint8_t*)ps_malloc(DISK_SECTOR_SIZE * sizeof(uint8_t)); + + if (!msc_disk[x]) + { + Serial.println("Unable to allocate memory"); + return; + } + } + + memcpy(msc_disk[0], sector0, 512); + memcpy(msc_disk[1], sector1, 6); + memcpy(msc_disk[2], sector2, 512); + memcpy(msc_disk[3], README_CONTENTS, 512); + + USB.onEvent(usbEventCallback); + MSC.vendorID("ACIT"); //max 8 chars + MSC.productID("NetFloppy"); //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(); + + Serial.print("Total PSRAM:"); + Serial.println(ESP.getPsramSize()); + Serial.print("Free PSRAM: "); + Serial.println(ESP.getFreePsram()); +} + +void loop() +{ + // +} +#endif /* ARDUINO_USB_MODE */ diff --git a/Frontend/ESP32_Firmware/test2_ino/.theia/launch.json b/Frontend/ESP32_Firmware/test2_ino/.theia/launch.json new file mode 100644 index 0000000..7e4253b --- /dev/null +++ b/Frontend/ESP32_Firmware/test2_ino/.theia/launch.json @@ -0,0 +1,8 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + "version": "0.2.0", + "configurations": [ + + ] +} diff --git a/Frontend/ESP32_Firmware/test2_ino/ci.json b/Frontend/ESP32_Firmware/test2_ino/ci.json new file mode 100644 index 0000000..f9ac7d0 --- /dev/null +++ b/Frontend/ESP32_Firmware/test2_ino/ci.json @@ -0,0 +1,5 @@ +{ + "requires": [ + "CONFIG_SOC_USB_OTG_SUPPORTED=y" + ] +} diff --git a/Frontend/ESP32_Firmware/test2_ino/test2_ino.ino b/Frontend/ESP32_Firmware/test2_ino/test2_ino.ino new file mode 100644 index 0000000..2f48440 --- /dev/null +++ b/Frontend/ESP32_Firmware/test2_ino/test2_ino.ino @@ -0,0 +1,362 @@ +// ESP32-S2/S3 native USB MSC RAM disk for Arduino-ESP32 3.3.0 +// Uses USB.h / USBMSC.h (no Adafruit_TinyUSB) + +#include +#include +#include "USB.h" +#include "USBMSC.h" +#include +#include + +#define LED_ON 2 +#define LED_TX 43 +#define LED_RX 44 +#define BUTTON 0 +#define WS2812 48 + +// ---- Disk parameters ---- +static const uint32_t DISK_SECTOR_SIZE = 512; // 512 bytes/sector +static const uint32_t DISK_SECTOR_COUNT = 1000 * 8; // ~2 MB (4000 * 512) +static const uint32_t DISK_BYTE_SIZE = DISK_SECTOR_COUNT * DISK_SECTOR_SIZE; + +static uint8_t* msc_disk = nullptr; +static volatile bool media_ready = false; + +USBMSC msc; +Adafruit_NeoPixel pixels(1, WS2812, NEO_GRB + NEO_KHZ800); + +// Utility: bounds check for (lba, offset, len) +static inline bool in_range(uint32_t lba, uint32_t offset, uint32_t len) +{ + uint64_t start = (uint64_t)lba * DISK_SECTOR_SIZE + offset; + uint64_t end = start + len; + return end <= (uint64_t)DISK_BYTE_SIZE; +} + +// ---------- Settings ---------- +static const uint32_t SERIAL_BAUD = 115200; +static const uint32_t CONNECT_TIMEOUT_MS = 20000; // 20s + + +Preferences prefs; +String ssid = ""; +String pass = ""; +bool autoConnect = false; + +void loadPrefs() +{ + prefs.begin("wifi", true); // read-only + ssid = prefs.getString("ssid", "Barriball - Automation"); + pass = prefs.getString("pass", "password123abc"); + autoConnect = prefs.getBool("auto", true); + prefs.end(); +} + + +void savePrefs() +{ + prefs.begin("wifi", false); + prefs.putString("ssid", ssid); + prefs.putString("pass", pass); + prefs.putBool("auto", autoConnect); + prefs.end(); +} + + +void forgetPrefs() +{ + prefs.begin("wifi", false); + prefs.remove("ssid"); + prefs.remove("pass"); + prefs.remove("auto"); + prefs.end(); + ssid = ""; + pass = ""; + autoConnect = false; +} + + +void showStatus() +{ + wl_status_t st = WiFi.status(); + + Serial.println("\n=== WiFi Status ==="); + Serial.printf("Mode: %s\n", WiFi.getMode() == WIFI_MODE_STA ? "STA" : "Other"); + Serial.printf("Saved SSID: %s\n", ssid.c_str()); + Serial.printf("Auto-connect: %s\n", autoConnect ? "ON" : "OFF"); + Serial.printf("Runtime status: %d\n", (int)st); + + if (st == WL_CONNECTED) + { + Serial.printf("Connected SSID: %s\n", WiFi.SSID().c_str()); + Serial.printf("IP: %s\n", WiFi.localIP().toString().c_str()); + Serial.printf("RSSI: %d dBm\n", WiFi.RSSI()); + } + + Serial.println("===================\n"); +} + + + + + +void changeMediaStatus(bool mediaPresent) +{ + + if (mediaPresent != media_ready) + { + media_ready = mediaPresent; + + msc.mediaPresent(mediaPresent); + + if (mediaPresent) + { + Serial.printf("Mount disk\n"); + // Set the size of the disk + // initialiseDisk(); + + showGreenLed(); + } + else + { + Serial.printf("Unmount Disk\n"); + // msc.end(); + + showOrangeLed(); + } + } +} + +void showGreenLed() +{ + pixels.setPixelColor(0, pixels.Color(0, 150, 0)); + pixels.show(); +} + +void showOrangeLed() +{ + pixels.setPixelColor(0, pixels.Color(150, 150, 0)); + pixels.show(); +} + +void showRedLed() +{ + pixels.setPixelColor(0, pixels.Color(150, 0, 0)); + pixels.show(); +} + +void showBlueLed() +{ + pixels.setPixelColor(0, pixels.Color(0, 0, 150)); + pixels.show(); +} + +void usbEvent(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + +} + +bool onStartStop(uint8_t power_condition, bool start, bool load_eject) +{ + (void)power_condition; + + // start: true = mount/open; false = stop + // load_eject: true when host is (eject) requesting unload + + Serial.printf("StartStop: pwr=%u start=%u load_eject=%u\n", power_condition, start, load_eject); + + + if (load_eject && !start) + { + Serial.printf("Eject"); + + // Zero out storage area + //memset(msc_disk, 0x00, DISK_BYTE_SIZE); + + // Host requested eject + changeMediaStatus(false); + + return true; + } + else if (load_eject && start) + { + Serial.printf("Insert"); + + // Re-insert + changeMediaStatus(true); + + return true; + } + else if (!load_eject && !start) + { + Serial.printf("Stop"); + + //showRedLed(); + // Stop (could flush if backing store were non-volatile) + return true; + } + else + { + Serial.printf("Start"); + + // Start + changeMediaStatus(true); + return true; + } +} + + +void initialiseDisk() +{ + // Zero out storage area + memset(msc_disk, 0x00, DISK_BYTE_SIZE); + + // Bring up native USB (CDC + MSC share the device) + //USB.onEvent(usbEvent) + USB.begin(); + + // int32_t (*msc_read_cb)(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) + msc.onRead([](uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) -> int32_t + { + if (!media_ready) + { + //Serial.println("READ: media not ready"); + + return 0; + } + + if (!in_range(lba, offset, bufsize)) + { + Serial.printf("READ OOB: lba=%lu off=%lu len=%lu\n", lba, offset, bufsize); + return -1; + } + + memcpy(buffer, msc_disk + (lba * DISK_SECTOR_SIZE) + offset, bufsize); + //Serial.printf("READ lba=%lu off=%lu len=%lu\n", lba, offset, bufsize); + return (int32_t)bufsize; + }); + + // int32_t (*msc_write_cb)(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) + msc.onWrite([](uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) -> int32_t + { + if (!media_ready) + { + //Serial.println("WRITE: media not ready"); + return -1; + } + + if (!in_range(lba, offset, bufsize)) + { + Serial.printf("WRITE OOB: lba=%lu off=%lu len=%lu\n", lba, offset, bufsize); + return -1; + } + + memcpy(msc_disk + (lba * DISK_SECTOR_SIZE) + offset, buffer, bufsize); + //Serial.printf("WRITE lba=%lu off=%lu len=%lu\n", lba, offset, bufsize); + return (int32_t)bufsize; + }); + + msc.onStartStop(onStartStop); + + // Set disks properties + msc.vendorID("ACIT"); + msc.productID("NetFloppy"); + msc.productRevision("1.0"); + msc.isWritable(true); + msc.mediaPresent(true); + + // Set the size of the disk + if (!msc.begin(DISK_SECTOR_COUNT, DISK_SECTOR_SIZE)) + { + Serial.println("Error: Could not initialise USB disk"); + } + + changeMediaStatus(true); + // No setUnitReady() / onReady() in this API; readiness handled inside callbacks. +} + +void setup() +{ + + Serial.begin(SERIAL_BAUD); + + while (!Serial) + { + delay(10); + } + + delay(200); + + Serial.flush(); + + pinMode(LED_ON, OUTPUT); + + digitalWrite(LED_ON, LOW); + + pixels.begin(); + showBlueLed(); + + Serial.printf("\n\n+=====================================+\n"); + Serial.printf("| NetFloppy |\n"); + Serial.printf("| Version 1.0 |\n"); + Serial.printf("| Copyright Aqua Cube IT Limited 2025 |\n"); + Serial.printf("+=====================================+\n\n"); + + uint32_t chipId = 0; + + for (int i = 0; i < 17; i = i + 8) + { + chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; + } + + Serial.printf("ESP32 Chip model = %s Rev %d\n", ESP.getChipModel(), ESP.getChipRevision()); + Serial.printf("This chip has %d cores\n", ESP.getChipCores()); + Serial.print("Chip ID: "); + Serial.println(chipId); + + + Serial.printf("Initialising...\n"); + Serial.printf("Allocating memory...\n"); + + // Allocate ramdisk in PSRAM + msc_disk = (uint8_t*) ps_malloc(DISK_BYTE_SIZE); + + if (!msc_disk) + { + Serial.println("PSRAM alloc FAILED\n"); + } + + Serial.printf("Allocated %u bytes of %u - %u Free\n", (unsigned)DISK_BYTE_SIZE, ESP.getPsramSize(), ESP.getFreePsram()); + + + loadPrefs(); + + if (ssid == "") + { + ssid = "Barriball - Automation"; + pass = "password123abc"; + autoConnect = true; + +Serial.printf("SSID: '%s', Pass: '%s', Auto: %u\n", ssid.c_str(), pass.c_str(), autoConnect); + + savePrefs(); + } + + if (autoConnect && ssid.length()) + { + Serial.printf("Connecting to WIFI '%s'...\n", ssid.c_str()); + //connectWiFi(ssid, pass); + WiFi.begin(ssid.c_str(), pass.c_str()); + + showStatus(); + } + + initialiseDisk(); +} + +void loop() +{ + // Simple heartbeat + //digitalWrite(LED_ON, !digitalRead(LED_ON)); + delay(10); +} diff --git a/Frontend/test.ino b/Frontend/test.ino new file mode 100644 index 0000000..e69de29