今天小编给大家带来的是日横滨的Maker Naveen 基于Arduino Nano ESP32的游戏机项目,该游戏的平均帧率为34 FPS,分辨率为320*240,该项目重量轻、结构紧促,对用户非常友好。
材料清单
硬件:
- Arduino Nano ESP32 *1
- Adafruit 2.8 TFT Touch Shield *1
- M5Stack Joystick Unit MEGA328P I2C/Grove Connector
- Seeed Studio Grove - Dual Button
- ArduEZ ONE Stackable Breadboard
- Seeed Studio Grove - 4 pin Male Jumper to Grove 4 pin Conversion Cable
软件:
- Espressif ESP IDF
介绍
《毁灭战士》是 id Software 于 1993 年开发并发行的一款经典第一人称射击游戏。游戏以未来科幻世界为背景,玩家扮演一名太空海军陆战队员,必须与成群的恶魔战斗,杀出一条血路。地狱。
Doom 主要是用 C 编程语言开发的,有一些组件是用汇编语言编写的。开发人员使用运行 NeXTSTEP 操作系统的 NeXT 计算机。游戏的关卡和图形数据与引擎分开存储在 WAD 文件中,它代表“所有数据在哪里?”。这使得无需更改引擎代码即可轻松修改设计。
最初的《DOOM》游戏需要至少 4MB 的 RAM 和 20MB 的磁盘空间,这比典型的微控制器规格要大得多。
为了移植游戏,需要一些压缩和优化技术来将游戏数据和代码放入微控制器的闪存和RAM中,并且需要一些修改和适应以使游戏引擎在不同的平台和架构上运行,例如ESP32或RP2040芯片。
但是,这些更改可能会在游戏的行为和性能中引入一些错误或差异,从而可能影响游戏体验。
最近,人们做出了一些努力来使游戏与低功耗微控制器兼容。对于我们的项目,我们选择使用 Retro-Go,这是一种开源固件,允许在基于 ESP32 的 MCU 上玩复古游戏。虽然该固件不直接支持 Arduino Nano ESP32,但其灵活性和可配置性使其可以移植到其他基于 ESP32 的 MCU。
STEP - 1 硬件选型
该项目的主要目标是展示在资源和功能有限的微控制器上运行《DOOM》的可行性和性能。在此项目中,我们将经典《DOOM》游戏移植到 Arduino Nano ESP32 上,并使用 Adafruit 2.8" TFT 显示扩展板、M5Stack 操纵杆单元和 Seeed Grove 双按钮使其成为手持游戏机。
Adafruit TFT 显示扩展板使用跳线和可堆叠面包板扩展板通过 SPI 接口连接到 Arduino Nano ESP32。
Arduino Nano ESP32 放置在面包板防护罩的背面,堆栈的顶部。
操纵杆和按钮分别通过 I2C 和 GPIO 连接,使用公跳线连接到 Grove 4 针转换电缆。原理图部分给出了连接图。
STEP - 3 设置开发环境
尽管我们将在 macOS 上编译固件,但其他操作系统的工作流程类似。首先,我们需要下载并安装ESP-IDF开发框架。
$ mkdir ~/Nano_ESP32_DOOM
$ cd ~/Nano_ESP32_DOOM
$ git clone -b release/v4.4 https://github.com/espressif/esp-idf
$ cd esp-idf
$ ./install.sh
$ source export.sh
现在我们可以克隆 Retro-Go 存储库。
$ cd ~/Nano_ESP32_DOOM
$ git clone https://github.com/ducalex/retro-go
$ cd retro-go
STEP - 4配置
Retro-Go 固件代码库是可配置的。他们提供了一些基于 ESP32 的开发板配置文件。我们需要添加/修改 Arduino Nano ESP32 的一些配置。在baseS3.sdkconfig 文件中,添加以下行。
CONFIG_ESPTOOLPY_FLASHMODE="qio"
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
我们将针对基于 ESP32-S3 的 Arduino Nano ESP32 重新使用 Components/retro-go/targets/esplay-s3/config.h。我们需要为各种组件(例如按钮、操纵杆、TFT显示器和SD卡)提供GPIO、I2C和SPI引脚,以与Arduino Nano ESP32板连接。
// Target definition
#define RG_TARGET_NAME "ESPLAY-S3"
// Storage
#define RG_STORAGE_DRIVER 1 // 0 = Host, 1 = SDSPI, 2 = SDMMC, 3 = USB, 4 = Flash
#define RG_STORAGE_HOST SDMMC_HOST_SLOT_1 // Used by driver 1 and 2
#define RG_STORAGE_SPEED SDMMC_FREQ_DEFAULT // Used by driver 1 and 2
#define RG_STORAGE_ROOT "/sd" // Storage mount point
// Video
#define RG_SCREEN_DRIVER 0 // 0 = ILI9341
#define RG_SCREEN_HOST SPI2_HOST
#define RG_SCREEN_SPEED SPI_MASTER_FREQ_40M
#define RG_SCREEN_TYPE 0
#define RG_SCREEN_WIDTH 320
#define RG_SCREEN_HEIGHT 240
#define RG_SCREEN_ROTATE 0
#define RG_SCREEN_MARGIN_TOP 0
#define RG_SCREEN_MARGIN_BOTTOM 0
#define RG_SCREEN_MARGIN_LEFT 0
#define RG_SCREEN_MARGIN_RIGHT 0
// Input
#define RG_GAMEPAD_DRIVER 3 // 1 = GPIO, 2 = Serial, 3 = I2C
#define RG_GAMEPAD_HAS_MENU_BTN 0
#define RG_GAMEPAD_HAS_OPTION_BTN 0
#define RG_GAMEPAD_MAP {\
{RG_KEY_SELECT, RG_GPIO_GAMEPAD_SELECT},\
{RG_KEY_A, RG_GPIO_GAMEPAD_A},\
}
// Buttons
#define RG_GPIO_GAMEPAD_SELECT GPIO_NUM_5
#define RG_GPIO_GAMEPAD_A GPIO_NUM_6
// Status LED
#define RG_GPIO_LED GPIO_NUM_45
// I2C BUS
#define RG_GPIO_I2C_SDA GPIO_NUM_11
#define RG_GPIO_I2C_SCL GPIO_NUM_12
// SPI Display
#define RG_GPIO_LCD_MISO GPIO_NUM_47
#define RG_GPIO_LCD_MOSI GPIO_NUM_38
#define RG_GPIO_LCD_CLK GPIO_NUM_48
#define RG_GPIO_LCD_CS GPIO_NUM_21
#define RG_GPIO_LCD_DC GPIO_NUM_18
// SPI SD Card
#define RG_GPIO_SDSPI_MISO GPIO_NUM_47
#define RG_GPIO_SDSPI_MOSI GPIO_NUM_38
#define RG_GPIO_SDSPI_CS GPIO_NUM_9
#define RG_GPIO_SDSPI_CLK GPIO_NUM_48
对于操纵杆和按钮,我们需要在 Components/retro-go/rg_input.c 文件中添加以下行。
...
#elif RG_GAMEPAD_DRIVER == 3 // I2C
uint8_t data[3];
if (rg_i2c_read(0x52, -1, &data, 3))
{
int joyX = data[0];
int joyY = data[1];
int btnS = data[2];
if (joyY > 160) {
state |= RG_KEY_UP;
} else if (joyY < 96) {
state |= RG_KEY_DOWN;
}
if (joyX > 160) {
state |= RG_KEY_RIGHT;
} else if (joyX < 96) {
state |= RG_KEY_LEFT;
}
if (btnS) {
state |= RG_KEY_START;
}
}
for (size_t i = 0; i < keymap_size; ++i)
{
if (!gpio_get_level(keymap[i].src)) {
state |= keymap[i].key;
RG_LOGI("key=%d\n", i);
}
}
...
STEP - 5 加载固件
Retro-Go 固件提供了一个可自动执行编译过程的 Python 脚本。执行以下命令来构建 Retro-Go 启动器应用程序和 Prboom-go 应用程序(Doom 端口)。
$ chmod +x rg_tool.py
$ ./rg_tool.py --target arduino_nano_esp32 build-img prboom-go
STEP - 6 准备SD卡
我们需要使用 FAT32 格式化的 SD 卡来存储设置并保存游戏会话。另外,我们需要将 Doom 的 WAD 文件复制到 SD 卡上的 /roms/doom 目录。
$ cp prboom-go/components/prboom/data/doom1.wad/Volumes/SD/roms/doom
SD 卡插入 Adarfruit TFT 显示扩展板中。
STEP - 7 上传固件
要将固件上传到 Arduino Nano ESP32 板上,只需执行以下命令即可。
$ esptool.py --chip esp32s3 write_flash --flash_size detect 0x00 retro-go_1.39-pre-dirty_esplay-s3.img
STEP - 8 设置无线连接
通过使用 SD 卡上的 /retro-go/config/wifi.json 中的设置创建配置文件,可以自动启用 WiFi。
{
"ssid": "your_router_ssid",
"password": "your_network_password"
}
建立成功的网络连接后,它将与NTP服务器同步时间。输入 Arduino Nano ESP32 Wi-Fi 接口的 IP 地址可以访问 Web 界面来浏览/上传 SD 卡文件。
STEP - 9 外壳组装
为了快速制作原型,我们重新利用了从包装中借用的透明 PET 塑料,使设备的前面板具有手持游戏机的外观和感觉,同时也为操纵杆和按钮提供了额外的坚固性。
然而,3D 打印的外壳可能是更好的选择。
背面未覆盖或没有任何覆盖物。
原理图:
总结
我们已成功移植 Doom 并在 Arduino Nano ESP32 上实现了可接受的性能。我们已成功移植 Doom 并在 Arduino Nano ESP32 上实现了可接受的性能。