这次介绍的是在 Arduino 1.8.16 + ESP32 2.0.1 下实现假U盘的例子。特别强调:ESP32 Arduino 环境必须是 2.0.1, 更高的版本反倒有兼容问题。
实现的方法和之前的相同【参考1】:
- 首先在本机做一个虚拟的256MB硬盘,然后在上面放置一个 200MB 的内容为全0x00的文件(太大的话,处理起来非常麻烦);
- 将这个硬盘使用HXD 做成镜像文件,然后使用工具扫描这个文件,将所有的不为0的内容写入.h文件中(这就是256MBDisk.bin.h文件的来源);
- 这个文件中0x804 到 0x8CC扇区内容,和0x902 到 0x9CA扇区内容相同,因此在数组中删除后者内容,当有到这个扇区的请求时,用前者的内容替代,这样可以大大缩减编译后文件的体积;
- 在代码中响应 onRead(),如果请求的LBA在.h文件中,就进行Buffer 的填充;
- 插入过程 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 表有部分是相同的,所以可以对这部分进行压缩)
参考:
- https://mc.dfrobot.com.cn/thread-309859-1-1.html ESP32 S2 做一个假U盘