ESP32 S2 上实现假U盘(ESP32 2.0.1 )

这次介绍的是在 Arduino 1.8.16 + ESP32 2.0.1 下实现假U盘的例子。特别强调:ESP32 Arduino 环境必须是 2.0.1, 更高的版本反倒有兼容问题。

实现的方法和之前的相同【参考1】:

  1. 首先在本机做一个虚拟的256MB硬盘,然后在上面放置一个 200MB 的内容为全0x00的文件(太大的话,处理起来非常麻烦);
  2. 将这个硬盘使用HXD 做成镜像文件,然后使用工具扫描这个文件,将所有的不为0的内容写入.h文件中(这就是256MBDisk.bin.h文件的来源);
  3. 这个文件中0x804 到 0x8CC扇区内容,和0x902 到 0x9CA扇区内容相同,因此在数组中删除后者内容,当有到这个扇区的请求时,用前者的内容替代,这样可以大大缩减编译后文件的体积;
  4. 在代码中响应 onRead(),如果请求的LBA在.h文件中,就进行Buffer 的填充;
  5. 插入过程 Windows 有写入操作,因此必须响应 onWrite

完整代码如下:

#include "USB.h"
#include "USBMSC.h"
#include "256MBDisk.h"

#if ARDUINO_USB_CDC_ON_BOOT
#define HWSerial Serial0
#define USBSerial Serial
#else
#define HWSerial Serial
//USBCDC USBSerial;
#endif

USBMSC MSC;

uint32_t findLBA(uint32_t LBA) {
  for (uint32_t i=0; i<415;i++) {
      if (Index[i]==LBA) {
          if (i>=205) {i=i-205+4;}
          return i;
        }
    }
  return 0xFFFFFF;  
}
static const uint32_t DISK_SECTOR_COUNT = 520192; 

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 getLBA=0;
  uint8_t *p;
  p=(uint8_t *)buffer;
  //HWSerial.printf("Buffer1 %X\n", buffer);
  for (uint32_t i=0;i<bufsize/DISK_SECTOR_SIZE;i++) {
     //HWSerial.printf("Check %u\n", lba+i);
     getLBA=findLBA(lba+i);
     //HWSerial.printf("getLBA %u %u\n", getLBA,lba+i);
     if (getLBA!=0xFFFFFF) { //如果找到了
        memcpy((void *)&p[i*DISK_SECTOR_SIZE], &msc_disk[getLBA][0], DISK_SECTOR_SIZE);
        //HWSerial.printf("%u %u %u %u\n", msc_disk[getLBA][0],msc_disk[getLBA][1],msc_disk[getLBA][2],msc_disk[getLBA][3]);
        //HWSerial.printf("Send %u\n", getLBA);
        //HWSerial.printf("Buffer2 %X\n", (void *)&p[i*DISK_SECTOR_SIZE]);
      } else {
       //HWSerial.printf("Send all zero to %u\n", getLBA);
        for (uint16_t j=0;j<DISK_SECTOR_SIZE;j++) {
              p[i*DISK_SECTOR_SIZE+j]=0;
          }
          
      }
    }
  return bufsize;
}

static bool onStartStop(uint8_t power_condition, bool start, bool load_eject){
  HWSerial.printf("MSC START/STOP: power: %u, start: %u, eject: %u\n", power_condition, start, load_eject);
  return true;
}

static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
  if(event_base == ARDUINO_USB_EVENTS){
    arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
    switch (event_id){
      case ARDUINO_USB_STARTED_EVENT:
        HWSerial.println("USB PLUGGED");
        break;
      case ARDUINO_USB_STOPPED_EVENT:
        HWSerial.println("USB UNPLUGGED");
        break;
      case ARDUINO_USB_SUSPEND_EVENT:
        HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
        break;
      case ARDUINO_USB_RESUME_EVENT:
        HWSerial.println("USB RESUMED");
        break;
      
      default:
        break;
    }
  }
}

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);
  return bufsize;
}
void setup() {
  HWSerial.begin(115200);
  HWSerial.setDebugOutput(true);

  USB.onEvent(usbEventCallback);
  MSC.vendorID("ESP32");//max 8 chars
  MSC.productID("USB_MSC");//max 16 chars
  MSC.productRevision("1.02");//max 4 chars
  MSC.onStartStop(onStartStop);
  MSC.onRead(onRead);
  MSC.onWrite(onWrite);
  MSC.mediaPresent(true);
  MSC.begin(DISK_SECTOR_COUNT, DISK_SECTOR_SIZE);
  //USBSerial.begin();
  USB.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
}

速度测试:

本文提到的用于测试的 U盘镜像:

代码1,这个是完整的分区:

代码2,这个是压缩后的分区(分区的FAT 表有部分是相同的,所以可以对这部分进行压缩)

参考:

  1. https://mc.dfrobot.com.cn/thread-309859-1-1.html ESP32 S2 做一个假U盘

发表评论

您的电子邮箱地址不会被公开。